Skip to content

Commit e9005c1

Browse files
authored
ENH: support non-nano in DTA.std (#47245)
1 parent 4f6410c commit e9005c1

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

pandas/core/arrays/datetimes.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
astype_overflowsafe,
3030
fields,
3131
get_resolution,
32+
get_unit_from_dtype,
3233
iNaT,
3334
ints_to_pydatetime,
3435
is_date_array_normalized,
@@ -336,10 +337,12 @@ def _simple_new( # type: ignore[override]
336337
assert isinstance(values, np.ndarray)
337338
assert dtype.kind == "M"
338339
if isinstance(dtype, np.dtype):
339-
# TODO: once non-nano DatetimeTZDtype is implemented, require that
340-
# dtype's reso match values's reso
341340
assert dtype == values.dtype
342341
assert not is_unitless(dtype)
342+
else:
343+
# DatetimeTZDtype. If we have e.g. DatetimeTZDtype[us, UTC],
344+
# then values.dtype should be M8[us].
345+
assert dtype._reso == get_unit_from_dtype(values.dtype)
343346

344347
result = super()._simple_new(values, dtype)
345348
result._freq = freq
@@ -1186,6 +1189,9 @@ def to_perioddelta(self, freq) -> TimedeltaArray:
11861189
)
11871190
from pandas.core.arrays.timedeltas import TimedeltaArray
11881191

1192+
if self._ndarray.dtype != "M8[ns]":
1193+
raise NotImplementedError("Only supported for nanosecond resolution.")
1194+
11891195
i8delta = self.asi8 - self.to_period(freq).to_timestamp().asi8
11901196
m8delta = i8delta.view("m8[ns]")
11911197
return TimedeltaArray(m8delta)
@@ -1974,10 +1980,13 @@ def std(
19741980
# without creating a copy by using a view on self._ndarray
19751981
from pandas.core.arrays import TimedeltaArray
19761982

1977-
tda = TimedeltaArray(self._ndarray.view("i8"))
1978-
return tda.std(
1979-
axis=axis, dtype=dtype, out=out, ddof=ddof, keepdims=keepdims, skipna=skipna
1980-
)
1983+
# Find the td64 dtype with the same resolution as our dt64 dtype
1984+
dtype_str = self._ndarray.dtype.name.replace("datetime64", "timedelta64")
1985+
dtype = np.dtype(dtype_str)
1986+
1987+
tda = TimedeltaArray._simple_new(self._ndarray.view(dtype), dtype=dtype)
1988+
1989+
return tda.std(axis=axis, out=out, ddof=ddof, keepdims=keepdims, skipna=skipna)
19811990

19821991

19831992
# -------------------------------------------------------------------

pandas/tests/arrays/test_datetimes.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,28 @@ def test_normalize(self, unit):
8585
res = dta.normalize()
8686
tm.assert_extension_array_equal(res, expected)
8787

88+
def test_simple_new_requires_match(self, unit):
89+
arr = np.arange(5, dtype=np.int64).view(f"M8[{unit}]")
90+
dtype = DatetimeTZDtype(unit, "UTC")
91+
92+
dta = DatetimeArray._simple_new(arr, dtype=dtype)
93+
assert dta.dtype == dtype
94+
95+
wrong = DatetimeTZDtype("ns", "UTC")
96+
with pytest.raises(AssertionError, match=""):
97+
DatetimeArray._simple_new(arr, dtype=wrong)
98+
99+
def test_std_non_nano(self, unit):
100+
dti = pd.date_range("2016-01-01", periods=55, freq="D")
101+
arr = np.asarray(dti).astype(f"M8[{unit}]")
102+
103+
dta = DatetimeArray._simple_new(arr, dtype=arr.dtype)
104+
105+
# we should match the nano-reso std, but floored to our reso.
106+
res = dta.std()
107+
assert res._reso == dta._reso
108+
assert res == dti.std().floor(unit)
109+
88110

89111
class TestDatetimeArrayComparisons:
90112
# TODO: merge this into tests/arithmetic/test_datetime64 once it is

0 commit comments

Comments
 (0)