Skip to content

Commit b29dfc6

Browse files
committed
Support NDFrame.shift with EAs
Uses take internally. Closes pandas-dev#22386
1 parent b5d81cf commit b29dfc6

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

doc/source/whatsnew/v0.24.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ ExtensionType Changes
444444
the ``ExtensionDtype`` has gained the method ``construct_array_type`` (:issue:`21185`)
445445
- The ``ExtensionArray`` constructor, ``_from_sequence`` now take the keyword arg ``copy=False`` (:issue:`21185`)
446446
- Bug in :meth:`Series.get` for ``Series`` using ``ExtensionArray`` and integer index (:issue:`21257`)
447+
- :meth:`~Series.shift` now works with extension arrays, rather than raising an AttributeError (:isseu:`22386`)
447448
- :meth:`Series.combine()` works correctly with :class:`~pandas.api.extensions.ExtensionArray` inside of :class:`Series` (:issue:`20825`)
448449
- :meth:`Series.combine()` with scalar argument now works for any function type (:issue:`21248`)
449450
-

pandas/core/internals/blocks.py

+18
Original file line numberDiff line numberDiff line change
@@ -2061,6 +2061,24 @@ def interpolate(self, method='pad', axis=0, inplace=False, limit=None,
20612061
limit=limit),
20622062
placement=self.mgr_locs)
20632063

2064+
def shift(self, periods, axis=0, mgr=None):
2065+
# type: (int, int, Optional[BlockPlacement]) -> List[ExtensionBlock]
2066+
indexer = np.roll(np.arange(len(self)), periods)
2067+
2068+
if periods > 0:
2069+
indexer[:periods] = -1
2070+
else:
2071+
indexer[periods:] = -1
2072+
2073+
new_values = self.values.take(indexer, allow_fill=True)
2074+
return [self.make_block_same_class(new_values,
2075+
placement=self.mgr_locs,
2076+
ndim=self.ndim)]
2077+
2078+
@property
2079+
def _ftype(self):
2080+
return getattr(self.values, '_pandas_ftype', Block._ftype)
2081+
20642082

20652083
class NumericBlock(Block):
20662084
__slots__ = ()

pandas/tests/extension/base/methods.py

+25
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,28 @@ def test_combine_add(self, data_repeated):
138138
expected = pd.Series(
139139
orig_data1._from_sequence([a + val for a in list(orig_data1)]))
140140
self.assert_series_equal(result, expected)
141+
142+
@pytest.mark.parametrize('frame', [True, False])
143+
@pytest.mark.parametrize('periods, indices', [
144+
(-2, [2, 3, 4, -1, -1]),
145+
(0, [0, 1, 2, 3, 4]),
146+
(2, [-1, -1, 0, 1, 2]),
147+
])
148+
def test_container_shift_negative(self, data, frame, periods, indices):
149+
# https://github.com/pandas-dev/pandas/issues/22386
150+
subset = data[:5]
151+
data = pd.Series(subset, name='A')
152+
expected = pd.Series(subset.take(indices, allow_fill=True), name='A')
153+
154+
if frame:
155+
result = data.to_frame(name='A').assign(B=1).shift(periods)
156+
expected = pd.concat([
157+
expected,
158+
pd.Series([1] * 5, name='B').shift(periods)
159+
], axis=1)
160+
compare = tm.assert_frame_equal
161+
else:
162+
result = data.shift(periods)
163+
compare = tm.assert_series_equal
164+
165+
compare(result, expected)

0 commit comments

Comments
 (0)