Skip to content

Commit 19a1299

Browse files
jbrockmendelrhshadrach
authored andcommitted
Requested Follow-Ups (pandas-dev#33856)
1 parent ef3aeb6 commit 19a1299

File tree

8 files changed

+62
-8
lines changed

8 files changed

+62
-8
lines changed

doc/source/whatsnew/v1.1.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ Datetimelike
544544
- Bug in :meth:`DatetimeIndex.tz_localize` incorrectly retaining ``freq`` in some cases where the original freq is no longer valid (:issue:`30511`)
545545
- Bug in :meth:`DatetimeIndex.intersection` losing ``freq`` and timezone in some cases (:issue:`33604`)
546546
- Bug in :class:`DatetimeIndex` addition and subtraction with some types of :class:`DateOffset` objects incorrectly retaining an invalid ``freq`` attribute (:issue:`33779`)
547+
- Bug in :class:`DatetimeIndex` where setting the ``freq`` attribute on an index could silently change the ``freq`` attribute on another index viewing the same data (:issue:`33552`)
547548

548549
Timedelta
549550
^^^^^^^^^
@@ -570,6 +571,7 @@ Numeric
570571
- Bug in :meth:`DataFrame.count` with ``level="foo"`` and index level ``"foo"`` containing NaNs causes segmentation fault (:issue:`21824`)
571572
- Bug in :meth:`DataFrame.diff` with ``axis=1`` returning incorrect results with mixed dtypes (:issue:`32995`)
572573
- Bug in :meth:`DataFrame.corr` and :meth:`DataFrame.cov` raising when handling nullable integer columns with ``pandas.NA`` (:issue:`33803`)
574+
- Bug in :class:`DataFrame` and :class:`Series` addition and subtraction between object-dtype objects and ``datetime64`` dtype objects (:issue:`33824`)
573575

574576
Conversion
575577
^^^^^^^^^^

pandas/core/array_algos/transforms.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
def shift(values: np.ndarray, periods: int, axis: int, fill_value) -> np.ndarray:
1111
new_values = values
1212

13-
if periods == 0:
14-
# TODO: should we copy here?
15-
return new_values
13+
if periods == 0 or values.size == 0:
14+
return new_values.copy()
1615

1716
# make sure array sent to np.roll is c_contiguous
1817
f_ordered = values.flags.f_contiguous

pandas/core/arrays/categorical.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,7 @@ def shift(self, periods, fill_value=None):
11961196

11971197
fill_value = self._validate_fill_value(fill_value)
11981198

1199-
codes = shift(codes.copy(), periods, axis=0, fill_value=fill_value)
1199+
codes = shift(codes, periods, axis=0, fill_value=fill_value)
12001200

12011201
return self._constructor(codes, dtype=self.dtype, fastpath=True)
12021202

pandas/core/arrays/datetimelike.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -699,8 +699,6 @@ def _values_for_argsort(self):
699699

700700
@Appender(ExtensionArray.shift.__doc__)
701701
def shift(self, periods=1, fill_value=None, axis=0):
702-
if not self.size or periods == 0:
703-
return self.copy()
704702

705703
fill_value = self._validate_shift_value(fill_value)
706704
new_values = shift(self._data, periods, axis, fill_value)
@@ -745,7 +743,9 @@ def _validate_shift_value(self, fill_value):
745743
# TODO(2.0): once this deprecation is enforced, used _validate_fill_value
746744
if is_valid_nat_for_dtype(fill_value, self.dtype):
747745
fill_value = NaT
748-
elif not isinstance(fill_value, self._recognized_scalars):
746+
elif isinstance(fill_value, self._recognized_scalars):
747+
fill_value = self._scalar_type(fill_value)
748+
else:
749749
# only warn if we're not going to raise
750750
if self._scalar_type is Period and lib.is_integer(fill_value):
751751
# kludge for #31971 since Period(integer) tries to cast to str

pandas/core/internals/blocks.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -2237,7 +2237,12 @@ def diff(self, n: int, axis: int = 0) -> List["Block"]:
22372237
# Cannot currently calculate diff across multiple blocks since this
22382238
# function is invoked via apply
22392239
raise NotImplementedError
2240-
new_values = (self.values - self.shift(n, axis=axis)[0].values).asi8
2240+
2241+
if n == 0:
2242+
# Fastpath avoids making a copy in `shift`
2243+
new_values = np.zeros(self.values.shape, dtype=np.int64)
2244+
else:
2245+
new_values = (self.values - self.shift(n, axis=axis)[0].values).asi8
22412246

22422247
# Reshape the new_values like how algos.diff does for timedelta data
22432248
new_values = new_values.reshape(1, len(new_values))

pandas/tests/arrays/test_datetimes.py

+34
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,40 @@ def test_searchsorted_invalid_types(self, other, index):
374374
with pytest.raises(TypeError, match=msg):
375375
arr.searchsorted(other)
376376

377+
def test_shift_fill_value(self):
378+
dti = pd.date_range("2016-01-01", periods=3)
379+
380+
dta = dti._data
381+
expected = DatetimeArray(np.roll(dta._data, 1))
382+
383+
fv = dta[-1]
384+
for fill_value in [fv, fv.to_pydatetime(), fv.to_datetime64()]:
385+
result = dta.shift(1, fill_value=fill_value)
386+
tm.assert_datetime_array_equal(result, expected)
387+
388+
dta = dta.tz_localize("UTC")
389+
expected = expected.tz_localize("UTC")
390+
fv = dta[-1]
391+
for fill_value in [fv, fv.to_pydatetime()]:
392+
result = dta.shift(1, fill_value=fill_value)
393+
tm.assert_datetime_array_equal(result, expected)
394+
395+
def test_shift_value_tzawareness_mismatch(self):
396+
dti = pd.date_range("2016-01-01", periods=3)
397+
398+
dta = dti._data
399+
400+
fv = dta[-1].tz_localize("UTC")
401+
for invalid in [fv, fv.to_pydatetime()]:
402+
with pytest.raises(TypeError, match="Cannot compare"):
403+
dta.shift(1, fill_value=invalid)
404+
405+
dta = dta.tz_localize("UTC")
406+
fv = dta[-1].tz_localize(None)
407+
for invalid in [fv, fv.to_pydatetime(), fv.to_datetime64()]:
408+
with pytest.raises(TypeError, match="Cannot compare"):
409+
dta.shift(1, fill_value=invalid)
410+
377411

378412
class TestSequenceToDT64NS:
379413
def test_tz_dtype_mismatch_raises(self):

pandas/tests/extension/base/methods.py

+7
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,13 @@ def test_container_shift(self, data, frame, periods, indices):
237237

238238
compare(result, expected)
239239

240+
def test_shift_0_periods(self, data):
241+
# GH#33856 shifting with periods=0 should return a copy, not same obj
242+
result = data.shift(0)
243+
assert data[0] != data[1] # otherwise below is invalid
244+
data[0] = data[1]
245+
assert result[0] != result[1] # i.e. not the same object/view
246+
240247
@pytest.mark.parametrize("periods", [1, -2])
241248
def test_diff(self, data, periods):
242249
data = data[:5]

pandas/tests/extension/test_sparse.py

+7
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,13 @@ def test_searchsorted(self, data_for_sorting, as_series):
309309
with tm.assert_produces_warning(PerformanceWarning):
310310
super().test_searchsorted(data_for_sorting, as_series)
311311

312+
def test_shift_0_periods(self, data):
313+
# GH#33856 shifting with periods=0 should return a copy, not same obj
314+
result = data.shift(0)
315+
316+
data._sparse_values[0] = data._sparse_values[1]
317+
assert result._sparse_values[0] != result._sparse_values[1]
318+
312319

313320
class TestCasting(BaseSparseTests, base.BaseCastingTests):
314321
def test_astype_object_series(self, all_data):

0 commit comments

Comments
 (0)