From 2854537d255bb2ac110e2d81c653b31efd4c300c Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 19 Oct 2021 09:01:57 -0700 Subject: [PATCH 1/3] BUG: PeriodIndex[BDay].to_timestamp retain BDay instead of Day --- pandas/core/arrays/period.py | 13 ++++++++++++- pandas/tests/arrays/test_datetimelike.py | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 84e611659b165..50c8593074e49 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -12,6 +12,7 @@ import numpy as np +from pandas._libs import algos as libalgos from pandas._libs.arrays import NDArrayBacked from pandas._libs.tslibs import ( BaseOffset, @@ -506,7 +507,17 @@ def to_timestamp(self, freq=None, how: str = "start") -> DatetimeArray: new_parr = self.asfreq(freq, how=how) new_data = libperiod.periodarr_to_dt64arr(new_parr.asi8, base) - return DatetimeArray(new_data)._with_freq("infer") + dta = DatetimeArray(new_data) + + if self.freq.name == "B": + # See if we can retain BDay instead of Day in cases where + # len(self) is too small for infer_freq to distinguish between them + diffs = libalgos.unique_deltas(self.asi8) + if len(diffs) == 1: + dta._freq = self.freq + return dta + else: + return dta._with_freq("infer") # -------------------------------------------------------------------- diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 1e150f1b431c7..7c5ee2dc51019 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -1106,6 +1106,20 @@ def test_to_timestamp(self, how, arr1d): # an EA-specific tm.assert_ function tm.assert_index_equal(pd.Index(result), pd.Index(expected)) + def test_to_timestamp_roundtrip_bday(self): + # Case where infer_freq inside would choose "D" instead of "B" + dta = pd.date_range("2021-10-18", periods=3, freq="B")._data + parr = dta.to_period() + result = parr.to_timestamp() + assert result.freq == "B" + tm.assert_extension_array_equal(result, dta) + + dta2 = dta[::2] + parr2 = dta2.to_period() + result2 = parr2.to_timestamp() + assert result2.freq == "2B" + tm.assert_extension_array_equal(result2, dta2) + def test_to_timestamp_out_of_bounds(self): # GH#19643 previously overflowed silently pi = pd.period_range("1500", freq="Y", periods=3) From 18ba61713e3505bc212fa87e3cefc3a20a07015d Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 19 Oct 2021 09:30:33 -0700 Subject: [PATCH 2/3] one more case --- pandas/core/arrays/period.py | 7 ++++++- pandas/tests/arrays/test_datetimelike.py | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 50c8593074e49..c6a6b73dffcd6 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -514,7 +514,12 @@ def to_timestamp(self, freq=None, how: str = "start") -> DatetimeArray: # len(self) is too small for infer_freq to distinguish between them diffs = libalgos.unique_deltas(self.asi8) if len(diffs) == 1: - dta._freq = self.freq + diff = diffs[0] + if diff == self.freq.n: + dta._freq = self.freq + elif diff == 1: + dta._freq = self.freq.base + # TODO: other cases? return dta else: return dta._with_freq("infer") diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 7c5ee2dc51019..8f59d69a60c48 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -1120,6 +1120,11 @@ def test_to_timestamp_roundtrip_bday(self): assert result2.freq == "2B" tm.assert_extension_array_equal(result2, dta2) + parr3 = dta.to_period("2B") + result3 = parr3.to_timestamp() + assert result3.freq == "B" + tm.assert_extension_array_equal(result3, dta) + def test_to_timestamp_out_of_bounds(self): # GH#19643 previously overflowed silently pi = pd.period_range("1500", freq="Y", periods=3) From 8b43689b0f1a90cd2998194e0031519d294e1a8f Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 9 Nov 2021 20:14:21 -0800 Subject: [PATCH 3/3] whatsnew --- doc/source/whatsnew/v1.4.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 99a66c7e5454b..8732e1c397ce5 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -579,6 +579,7 @@ I/O Period ^^^^^^ - Bug in adding a :class:`Period` object to a ``np.timedelta64`` object incorrectly raising ``TypeError`` (:issue:`44182`) +- Bug in :meth:`PeriodIndex.to_timestamp` when the index has ``freq="B"`` inferring ``freq="D"`` for its result instead of ``freq="B"`` (:issue:`44105`) - Plotting