Skip to content

Commit edb505e

Browse files
committed
BUG: Fixed inconsistent multiplication pandas-dev#47953
1 parent aca58cd commit edb505e

File tree

3 files changed

+52
-7
lines changed

3 files changed

+52
-7
lines changed

doc/source/whatsnew/v2.0.3.rst

+4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ Fixed regressions
2121

2222
Bug fixes
2323
~~~~~~~~~
24+
<<<<<<< Updated upstream
2425
- Bug in :func:`RangeIndex.union` when using ``sort=True`` with another :class:`RangeIndex` (:issue:`53490`)
26+
=======
27+
- Bug in :class:`RelativeDeltaOffset` which caused inconsistent behavior upon multiplying a :class:`DateOffset` (:issue:`47953`)
28+
>>>>>>> Stashed changes
2529
- Bug in :func:`read_csv` when defining ``dtype`` with ``bool[pyarrow]`` for the ``"c"`` and ``"python"`` engines (:issue:`53390`)
2630
- Bug in :meth:`Series.str.split` and :meth:`Series.str.rsplit` with ``expand=True`` for :class:`ArrowDtype` with ``pyarrow.string`` (:issue:`53532`)
2731
-

pandas/_libs/tslibs/offsets.pyx

+6-7
Original file line numberDiff line numberDiff line change
@@ -1221,15 +1221,14 @@ cdef class RelativeDeltaOffset(BaseOffset):
12211221
# perform calculation in UTC
12221222
other = other.replace(tzinfo=None)
12231223

1224-
if self.n > 0:
1225-
for i in range(self.n):
1226-
other = other + self._offset
1224+
if hasattr(self, "nanoseconds"):
1225+
td_nano = Timedelta(nanoseconds=self.nanoseconds)
1226+
other = self.n * td_nano + other
12271227
else:
1228-
for i in range(-self.n):
1229-
other = other - self._offset
1228+
td_nano = Timedelta(0)
1229+
1230+
other = other + ((self.offset + td_nano) * self.n)
12301231

1231-
if hasattr(self, "nanoseconds"):
1232-
other = self.n * Timedelta(nanoseconds=self.nanoseconds) + other
12331232
if other_nanos != 0:
12341233
other = Timedelta(nanoseconds=other_nanos) + other
12351234

pandas/tests/tseries/offsets/test_offsets.py

+42
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from pandas.errors import PerformanceWarning
3333

3434
from pandas import (
35+
DataFrame,
3536
DatetimeIndex,
3637
Series,
3738
date_range,
@@ -1075,3 +1076,44 @@ def test_dateoffset_add_sub_timestamp_series_with_nano(offset, expected):
10751076
assert testseries[0] == teststamp
10761077
testseries = offset + testseries
10771078
assert testseries[0] == expected
1079+
1080+
1081+
@pytest.mark.parametrize(
1082+
"month_value, scaling_factor, start_timestamp, expected_timestamp",
1083+
[(1, 2, "2020-01-30", "2020-03-30"), (2, 1, "2020-01-30", "2020-03-30")],
1084+
)
1085+
def test_offset_multiplication(
1086+
month_value, scaling_factor, start_timestamp, expected_timestamp
1087+
):
1088+
# GH 47953
1089+
mo1 = DateOffset(months=month_value)
1090+
1091+
startscalar = Timestamp(start_timestamp)
1092+
startarray = Series([startscalar])
1093+
1094+
resultscalar = startscalar + (mo1 * scaling_factor)
1095+
resultarray = startarray + (mo1 * scaling_factor)
1096+
1097+
expectedscalar = Timestamp(expected_timestamp)
1098+
expectedarray = Series([expectedscalar])
1099+
assert resultscalar == expectedscalar
1100+
1101+
tm.assert_series_equal(resultarray, expectedarray)
1102+
1103+
1104+
def test_dateoffset_operations_on_dataframes():
1105+
# GH 47953
1106+
df = DataFrame({"T": [Timestamp("2019-04-30")], "D": [DateOffset(months=1)]})
1107+
frameresult1 = df["T"] + 26 * df["D"]
1108+
df2 = DataFrame(
1109+
{
1110+
"T": [Timestamp("2019-04-30"), Timestamp("2019-04-30")],
1111+
"D": [DateOffset(months=1), DateOffset(months=1)],
1112+
}
1113+
)
1114+
expecteddate = Timestamp("2021-06-30")
1115+
with tm.assert_produces_warning(PerformanceWarning):
1116+
frameresult2 = df2["T"] + 26 * df2["D"]
1117+
1118+
assert frameresult1[0] == expecteddate
1119+
assert frameresult2[0] == expecteddate

0 commit comments

Comments
 (0)