From 5486e759b2d0f9190b45e4675da5c88ec60830f3 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:55:38 -0800 Subject: [PATCH 1/3] BUG: .quantile with empty temporal data --- pandas/core/arrays/_mixins.py | 4 +--- pandas/tests/series/methods/test_quantile.py | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/_mixins.py b/pandas/core/arrays/_mixins.py index 11928e79ffc62..ba054484f859b 100644 --- a/pandas/core/arrays/_mixins.py +++ b/pandas/core/arrays/_mixins.py @@ -512,9 +512,7 @@ def _quantile( else: # e.g. test_quantile_empty we are empty integer dtype and res_values # has floating dtype - # TODO: technically __init__ isn't defined here. - # Should we raise NotImplementedError and handle this on NumpyEA? - return type(self)(res_values) # type: ignore[call-arg] + return type(self)._from_sequence(res_values) # type: ignore[call-arg] # ------------------------------------------------------------------------ # numpy-like methods diff --git a/pandas/tests/series/methods/test_quantile.py b/pandas/tests/series/methods/test_quantile.py index fa0563271d7df..294437e557fdc 100644 --- a/pandas/tests/series/methods/test_quantile.py +++ b/pandas/tests/series/methods/test_quantile.py @@ -245,3 +245,10 @@ def test_quantile_dtype_size(self, any_int_ea_dtype): result = ser.quantile([0.1, 0.5]) expected = Series([1, 1], dtype=any_int_ea_dtype, index=[0.1, 0.5]) tm.assert_series_equal(result, expected) + + +@pytest.mark.parametrize("typ", ["datetime64", "timedelta64"]) +def test_quantile_empty_datetimelike(typ, unit): + ser = Series([], dtype=f"{typ}[{unit}]") + result = ser.quantile() + assert result is pd.NaT From a56a87d4e1832c98802223ffb6724e3bfbebdfa8 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Thu, 18 Dec 2025 10:41:44 -0800 Subject: [PATCH 2/3] Revert old change, ensure accounts for .unit --- pandas/core/arrays/_mixins.py | 4 +++- pandas/core/arrays/datetimes.py | 5 ++++- pandas/core/arrays/timedeltas.py | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/_mixins.py b/pandas/core/arrays/_mixins.py index ba054484f859b..11928e79ffc62 100644 --- a/pandas/core/arrays/_mixins.py +++ b/pandas/core/arrays/_mixins.py @@ -512,7 +512,9 @@ def _quantile( else: # e.g. test_quantile_empty we are empty integer dtype and res_values # has floating dtype - return type(self)._from_sequence(res_values) # type: ignore[call-arg] + # TODO: technically __init__ isn't defined here. + # Should we raise NotImplementedError and handle this on NumpyEA? + return type(self)(res_values) # type: ignore[call-arg] # ------------------------------------------------------------------------ # numpy-like methods diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index d3c0bbdf53310..fc894e1de0e37 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -226,13 +226,16 @@ class DatetimeArray(dtl.TimelikeOps, dtl.DatelikeOps): """ _typ = "datetimearray" - _internal_fill_value = np.datetime64("NaT", "ns") _recognized_scalars = (datetime, np.datetime64) _is_recognized_dtype: Callable[[DtypeObj], bool] = lambda x: lib.is_np_dtype( x, "M" ) or isinstance(x, DatetimeTZDtype) _infer_matches = ("datetime", "datetime64", "date") + @property + def _internal_fill_value(self) -> np.datetime64: + return np.datetime64("NaT", self.unit) + @property def _scalar_type(self) -> type[Timestamp]: return Timestamp diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 514ad28f698d6..ff2e2e6429936 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -154,11 +154,14 @@ class TimedeltaArray(dtl.TimelikeOps): """ _typ = "timedeltaarray" - _internal_fill_value = np.timedelta64("NaT", "ns") _recognized_scalars = (timedelta, np.timedelta64, Tick) _is_recognized_dtype: Callable[[DtypeObj], bool] = lambda x: lib.is_np_dtype(x, "m") _infer_matches = ("timedelta", "timedelta64") + @property + def _internal_fill_value(self) -> np.timedelta64: + return np.timedelta64("NaT", self.unit) + @property def _scalar_type(self) -> type[Timedelta]: return Timedelta From b7570bed19643dbfb3c09c9b8f94e2d04378bc8f Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:28:39 -0800 Subject: [PATCH 3/3] Add DataFrame version of the test --- pandas/tests/frame/methods/test_quantile.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pandas/tests/frame/methods/test_quantile.py b/pandas/tests/frame/methods/test_quantile.py index eded9d9473385..df1f2a80e76e7 100644 --- a/pandas/tests/frame/methods/test_quantile.py +++ b/pandas/tests/frame/methods/test_quantile.py @@ -928,3 +928,12 @@ def test_multi_quantile_numeric_only_retains_columns(): tm.assert_frame_equal( result, expected, check_index_type=True, check_column_type=True ) + + +@pytest.mark.parametrize("typ", ["datetime64", "timedelta64"]) +def test_quantile_empty_datetimelike(typ, unit): + dtype = f"{typ}[{unit}]" + df = DataFrame(np.array([], dtype=dtype)) + result = df.quantile() + expected = Series([pd.NaT], name=0.5, dtype=dtype) + tm.assert_series_equal(result, expected)