From 1a36f56d30828bd97f322c919531539d668a928e Mon Sep 17 00:00:00 2001 From: Floura Angel Date: Sun, 19 Oct 2025 19:35:23 -0400 Subject: [PATCH 1/3] fix honor orgin in asfreq --- doc/source/whatsnew/v3.0.0.rst | 1 + pandas/core/resample.py | 3 ++- pandas/tests/resample/test_resample_api.py | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index e7d70ebb7b27f..8a0b6595affa6 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -1059,6 +1059,7 @@ Groupby/resample/rolling - Bug in :meth:`.Resampler.interpolate` on a :class:`DataFrame` with non-uniform sampling and/or indices not aligning with the resulting resampled index would result in wrong interpolation (:issue:`21351`) - Bug in :meth:`.Series.rolling` when used with a :class:`.BaseIndexer` subclass and computing min/max (:issue:`46726`) - Bug in :meth:`DataFrame.ewm` and :meth:`Series.ewm` when passed ``times`` and aggregation functions other than mean (:issue:`51695`) +- Bug in :meth:`DataFrame.resample.asfreq` where fixed-frequency indexes with ``origin`` ignored alignment and returned incorrect values. Now ``origin`` and ``offset`` are respected. (:issue:`62725`) - Bug in :meth:`DataFrame.resample` and :meth:`Series.resample` were not keeping the index name when the index had :class:`ArrowDtype` timestamp dtype (:issue:`61222`) - Bug in :meth:`DataFrame.resample` changing index type to :class:`MultiIndex` when the dataframe is empty and using an upsample method (:issue:`55572`) - Bug in :meth:`DataFrameGroupBy.agg` and :meth:`SeriesGroupBy.agg` that was returning numpy dtype values when input values are pyarrow dtype values, instead of returning pyarrow dtype values. (:issue:`53030`) diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 5ae88cff55d6d..a4c2502176eb6 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -1889,11 +1889,12 @@ def _upsample(self, method, limit: int | None = None, fill_value=None): binner = self.binner res_index = self._adjust_binner_for_upsample(binner) - # if we have the same frequency as our axis, then we are equal sampling + # if index exactly matches target grid (same freq & alignment), use fast path if ( limit is None and to_offset(ax.inferred_freq) == self.freq and len(obj) == len(res_index) + and obj.index.equals(res_index) ): result = obj.copy() result.index = res_index diff --git a/pandas/tests/resample/test_resample_api.py b/pandas/tests/resample/test_resample_api.py index 845b5ad7acc00..7314b1207d4d2 100644 --- a/pandas/tests/resample/test_resample_api.py +++ b/pandas/tests/resample/test_resample_api.py @@ -989,3 +989,25 @@ def test_resample_empty(): ) result = df.resample("8h").mean() tm.assert_frame_equal(result, expected) + + +def test_asfreq_respects_origin_with_fixed_freq_all_seconds_equal(): + idx = [ + datetime(2025, 10, 17, 17, 15, 10), + datetime(2025, 10, 17, 17, 16, 10), + datetime(2025, 10, 17, 17, 17, 10), + ] + df = DataFrame({"value": [0, 1, 2]}, index=idx) + + result = df.resample("1min", origin="start_day").asfreq() + + exp_idx = pd.to_datetime( + [ + "2025-10-17 17:15:00", + "2025-10-17 17:16:00", + "2025-10-17 17:17:00", + ] + ).astype(result.index.dtype) # match time unit (s/us/ns) + + exp = DataFrame({"value": [np.nan, np.nan, np.nan]}, index=exp_idx) + tm.assert_frame_equal(result, exp, check_freq=False) From eb00fe82e5aa75c568f2f53bffdc45c7b74e3cc9 Mon Sep 17 00:00:00 2001 From: Floura Angel Date: Wed, 29 Oct 2025 23:19:41 -0400 Subject: [PATCH 2/3] fix test doc and check --- pandas/tests/resample/test_resample_api.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pandas/tests/resample/test_resample_api.py b/pandas/tests/resample/test_resample_api.py index 7314b1207d4d2..27f65d4e83732 100644 --- a/pandas/tests/resample/test_resample_api.py +++ b/pandas/tests/resample/test_resample_api.py @@ -992,6 +992,8 @@ def test_resample_empty(): def test_asfreq_respects_origin_with_fixed_freq_all_seconds_equal(): + # GH#62725: Ensure Resampler.asfreq respects origin="start_day" + # when all datetimes share identical seconds values. idx = [ datetime(2025, 10, 17, 17, 15, 10), datetime(2025, 10, 17, 17, 16, 10), @@ -1001,13 +1003,13 @@ def test_asfreq_respects_origin_with_fixed_freq_all_seconds_equal(): result = df.resample("1min", origin="start_day").asfreq() - exp_idx = pd.to_datetime( - [ - "2025-10-17 17:15:00", - "2025-10-17 17:16:00", - "2025-10-17 17:17:00", - ] - ).astype(result.index.dtype) # match time unit (s/us/ns) + # Expected index: match dtype while preserving freq + exp_idx = pd.DatetimeIndex( + date_range("2025-10-17 17:15:00", periods=3, freq="min").astype( + result.index.dtype + ), + freq="min", + ) exp = DataFrame({"value": [np.nan, np.nan, np.nan]}, index=exp_idx) - tm.assert_frame_equal(result, exp, check_freq=False) + tm.assert_frame_equal(result, exp) From 68d2491ce81857d8b2dfc43d84a352e318135c10 Mon Sep 17 00:00:00 2001 From: Floura Angel Date: Mon, 3 Nov 2025 22:22:37 -0500 Subject: [PATCH 3/3] fix test review --- pandas/tests/resample/test_resample_api.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pandas/tests/resample/test_resample_api.py b/pandas/tests/resample/test_resample_api.py index 27f65d4e83732..69a20443db344 100644 --- a/pandas/tests/resample/test_resample_api.py +++ b/pandas/tests/resample/test_resample_api.py @@ -1003,11 +1003,14 @@ def test_asfreq_respects_origin_with_fixed_freq_all_seconds_equal(): result = df.resample("1min", origin="start_day").asfreq() - # Expected index: match dtype while preserving freq + # Expected index: list of Timestamps, matching dtype exp_idx = pd.DatetimeIndex( - date_range("2025-10-17 17:15:00", periods=3, freq="min").astype( - result.index.dtype - ), + [ + pd.Timestamp("2025-10-17 17:15:00"), + pd.Timestamp("2025-10-17 17:16:00"), + pd.Timestamp("2025-10-17 17:17:00"), + ], + dtype=result.index.dtype, freq="min", )