Skip to content

Commit 11f3dcd

Browse files
authored
REF: avoid ravel in ints_to_pytimedelta (#47261)
* REF: avoid ravel in ints_to_pytimedelta * copy/paste fixup * typo fixup * copy/paste mixup
1 parent 1e9c151 commit 11f3dcd

File tree

2 files changed

+35
-18
lines changed

2 files changed

+35
-18
lines changed

pandas/_libs/tslibs/timedeltas.pyx

+34-15
Original file line numberDiff line numberDiff line change
@@ -161,42 +161,61 @@ def ints_to_pytimedelta(ndarray m8values, box=False):
161161
array of Timedelta or timedeltas objects
162162
"""
163163
cdef:
164+
NPY_DATETIMEUNIT reso = get_unit_from_dtype(m8values.dtype)
164165
Py_ssize_t i, n = m8values.size
165166
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
168175

169-
arr = m8values.view("i8")
176+
ndarray arr = m8values.view("i8")
177+
cnp.flatiter it = cnp.PyArray_IterNew(arr)
170178

171179
for i in range(n):
180+
# Analogous to: value = arr[i]
181+
value = (<int64_t*>cnp.PyArray_ITER_DATA(it))[0]
172182

173-
value = arr[i]
174183
if value == NPY_NAT:
175-
result[i] = <object>NaT
184+
res_val = <object>NaT
176185
else:
177186
if box:
178-
result[i] = _timedelta_from_value_and_reso(value, reso=reso)
187+
res_val = _timedelta_from_value_and_reso(value, reso=reso)
179188
elif reso == NPY_DATETIMEUNIT.NPY_FR_ns:
180-
result[i] = timedelta(microseconds=int(value) / 1000)
189+
res_val = timedelta(microseconds=int(value) / 1000)
181190
elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
182-
result[i] = timedelta(microseconds=value)
191+
res_val = timedelta(microseconds=value)
183192
elif reso == NPY_DATETIMEUNIT.NPY_FR_ms:
184-
result[i] = timedelta(milliseconds=value)
193+
res_val = timedelta(milliseconds=value)
185194
elif reso == NPY_DATETIMEUNIT.NPY_FR_s:
186-
result[i] = timedelta(seconds=value)
195+
res_val = timedelta(seconds=value)
187196
elif reso == NPY_DATETIMEUNIT.NPY_FR_m:
188-
result[i] = timedelta(minutes=value)
197+
res_val = timedelta(minutes=value)
189198
elif reso == NPY_DATETIMEUNIT.NPY_FR_h:
190-
result[i] = timedelta(hours=value)
199+
res_val = timedelta(hours=value)
191200
elif reso == NPY_DATETIMEUNIT.NPY_FR_D:
192-
result[i] = timedelta(days=value)
201+
res_val = timedelta(days=value)
193202
elif reso == NPY_DATETIMEUNIT.NPY_FR_W:
194-
result[i] = timedelta(weeks=value)
203+
res_val = timedelta(weeks=value)
195204
else:
196205
# Month, Year, NPY_FR_GENERIC, pico, fempto, atto
197206
raise NotImplementedError(reso)
198207

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
200219

201220

202221
# ----------------------------------------------------------------------

pandas/core/arrays/datetimelike.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,7 @@ def astype(self, dtype, copy: bool = True):
437437
return converted.reshape(self.shape)
438438

439439
elif self.dtype.kind == "m":
440-
i8data = self.asi8.ravel()
441-
converted = ints_to_pytimedelta(self._ndarray.ravel(), box=True)
442-
return converted.reshape(self.shape)
440+
return ints_to_pytimedelta(self._ndarray, box=True)
443441

444442
return self._box_values(self.asi8.ravel()).reshape(self.shape)
445443

0 commit comments

Comments
 (0)