@@ -161,42 +161,61 @@ def ints_to_pytimedelta(ndarray m8values, box=False):
161
161
array of Timedelta or timedeltas objects
162
162
"""
163
163
cdef:
164
+ NPY_DATETIMEUNIT reso = get_unit_from_dtype(m8values.dtype)
164
165
Py_ssize_t i, n = m8values.size
165
166
int64_t value
166
- object [::1 ] result = np.empty(n, dtype = object )
167
- NPY_DATETIMEUNIT reso = get_unit_from_dtype(m8values.dtype)
167
+ object res_val
168
+
169
+ # Note that `result` (and thus `result_flat`) is C-order and
170
+ # `it` iterates C-order as well, so the iteration matches
171
+ # See discussion at
172
+ # github.com/pandas-dev/pandas/pull/46886#discussion_r860261305
173
+ ndarray result = cnp.PyArray_EMPTY(m8values.ndim, m8values.shape, cnp.NPY_OBJECT, 0 )
174
+ object [::1 ] res_flat = result.ravel() # should NOT be a copy
168
175
169
- arr = m8values.view(" i8" )
176
+ ndarray arr = m8values.view(" i8" )
177
+ cnp.flatiter it = cnp.PyArray_IterNew(arr)
170
178
171
179
for i in range (n):
180
+ # Analogous to: value = arr[i]
181
+ value = (< int64_t* > cnp.PyArray_ITER_DATA(it))[0 ]
172
182
173
- value = arr[i]
174
183
if value == NPY_NAT:
175
- result[i] = < object > NaT
184
+ res_val = < object > NaT
176
185
else :
177
186
if box:
178
- result[i] = _timedelta_from_value_and_reso(value, reso = reso)
187
+ res_val = _timedelta_from_value_and_reso(value, reso = reso)
179
188
elif reso == NPY_DATETIMEUNIT.NPY_FR_ns:
180
- result[i] = timedelta(microseconds = int (value) / 1000 )
189
+ res_val = timedelta(microseconds = int (value) / 1000 )
181
190
elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
182
- result[i] = timedelta(microseconds = value)
191
+ res_val = timedelta(microseconds = value)
183
192
elif reso == NPY_DATETIMEUNIT.NPY_FR_ms:
184
- result[i] = timedelta(milliseconds = value)
193
+ res_val = timedelta(milliseconds = value)
185
194
elif reso == NPY_DATETIMEUNIT.NPY_FR_s:
186
- result[i] = timedelta(seconds = value)
195
+ res_val = timedelta(seconds = value)
187
196
elif reso == NPY_DATETIMEUNIT.NPY_FR_m:
188
- result[i] = timedelta(minutes = value)
197
+ res_val = timedelta(minutes = value)
189
198
elif reso == NPY_DATETIMEUNIT.NPY_FR_h:
190
- result[i] = timedelta(hours = value)
199
+ res_val = timedelta(hours = value)
191
200
elif reso == NPY_DATETIMEUNIT.NPY_FR_D:
192
- result[i] = timedelta(days = value)
201
+ res_val = timedelta(days = value)
193
202
elif reso == NPY_DATETIMEUNIT.NPY_FR_W:
194
- result[i] = timedelta(weeks = value)
203
+ res_val = timedelta(weeks = value)
195
204
else :
196
205
# Month, Year, NPY_FR_GENERIC, pico, fempto, atto
197
206
raise NotImplementedError (reso)
198
207
199
- return result.base # .base to access underlying np.ndarray
208
+ # Note: we can index result directly instead of using PyArray_MultiIter_DATA
209
+ # like we do for the other functions because result is known C-contiguous
210
+ # and is the first argument to PyArray_MultiIterNew2. The usual pattern
211
+ # does not seem to work with object dtype.
212
+ # See discussion at
213
+ # github.com/pandas-dev/pandas/pull/46886#discussion_r860261305
214
+ res_flat[i] = res_val
215
+
216
+ cnp.PyArray_ITER_NEXT(it)
217
+
218
+ return result
200
219
201
220
202
221
# ----------------------------------------------------------------------
0 commit comments