|
27 | 27 | from pandas.core.dtypes.common import (
|
28 | 28 | ensure_int64,
|
29 | 29 | ensure_object,
|
30 |
| - ensure_platform_int, |
31 | 30 | is_categorical_dtype,
|
32 | 31 | is_datetime64_dtype,
|
33 | 32 | is_dict_like,
|
|
51 | 50 | from pandas.core.accessor import PandasDelegate, delegate_names
|
52 | 51 | import pandas.core.algorithms as algorithms
|
53 | 52 | from pandas.core.algorithms import _get_data_algo, factorize, take, take_1d, unique1d
|
| 53 | +from pandas.core.array_algos.transforms import shift |
54 | 54 | from pandas.core.arrays.base import ExtensionArray, _extension_array_shared_docs
|
55 | 55 | from pandas.core.base import NoNewAttributesMixin, PandasObject, _shared_docs
|
56 | 56 | import pandas.core.common as com
|
@@ -1241,23 +1241,41 @@ def shift(self, periods, fill_value=None):
|
1241 | 1241 | codes = self.codes
|
1242 | 1242 | if codes.ndim > 1:
|
1243 | 1243 | raise NotImplementedError("Categorical with ndim > 1.")
|
1244 |
| - if np.prod(codes.shape) and (periods != 0): |
1245 |
| - codes = np.roll(codes, ensure_platform_int(periods), axis=0) |
1246 |
| - if isna(fill_value): |
1247 |
| - fill_value = -1 |
1248 |
| - elif fill_value in self.categories: |
1249 |
| - fill_value = self.categories.get_loc(fill_value) |
1250 |
| - else: |
1251 |
| - raise ValueError( |
1252 |
| - f"'fill_value={fill_value}' is not present " |
1253 |
| - "in this Categorical's categories" |
1254 |
| - ) |
1255 |
| - if periods > 0: |
1256 |
| - codes[:periods] = fill_value |
1257 |
| - else: |
1258 |
| - codes[periods:] = fill_value |
1259 | 1244 |
|
1260 |
| - return self.from_codes(codes, dtype=self.dtype) |
| 1245 | + fill_value = self._validate_fill_value(fill_value) |
| 1246 | + |
| 1247 | + codes = shift(codes.copy(), periods, axis=0, fill_value=fill_value) |
| 1248 | + |
| 1249 | + return self._constructor(codes, dtype=self.dtype, fastpath=True) |
| 1250 | + |
| 1251 | + def _validate_fill_value(self, fill_value): |
| 1252 | + """ |
| 1253 | + Convert a user-facing fill_value to a representation to use with our |
| 1254 | + underlying ndarray, raising ValueError if this is not possible. |
| 1255 | +
|
| 1256 | + Parameters |
| 1257 | + ---------- |
| 1258 | + fill_value : object |
| 1259 | +
|
| 1260 | + Returns |
| 1261 | + ------- |
| 1262 | + fill_value : int |
| 1263 | +
|
| 1264 | + Raises |
| 1265 | + ------ |
| 1266 | + ValueError |
| 1267 | + """ |
| 1268 | + |
| 1269 | + if isna(fill_value): |
| 1270 | + fill_value = -1 |
| 1271 | + elif fill_value in self.categories: |
| 1272 | + fill_value = self.categories.get_loc(fill_value) |
| 1273 | + else: |
| 1274 | + raise ValueError( |
| 1275 | + f"'fill_value={fill_value}' is not present " |
| 1276 | + "in this Categorical's categories" |
| 1277 | + ) |
| 1278 | + return fill_value |
1261 | 1279 |
|
1262 | 1280 | def __array__(self, dtype=None) -> np.ndarray:
|
1263 | 1281 | """
|
@@ -1835,24 +1853,12 @@ def take(self, indexer, allow_fill: bool = False, fill_value=None):
|
1835 | 1853 | """
|
1836 | 1854 | indexer = np.asarray(indexer, dtype=np.intp)
|
1837 | 1855 |
|
1838 |
| - dtype = self.dtype |
1839 |
| - |
1840 |
| - if isna(fill_value): |
1841 |
| - fill_value = -1 |
1842 |
| - elif allow_fill: |
| 1856 | + if allow_fill: |
1843 | 1857 | # convert user-provided `fill_value` to codes
|
1844 |
| - if fill_value in self.categories: |
1845 |
| - fill_value = self.categories.get_loc(fill_value) |
1846 |
| - else: |
1847 |
| - msg = ( |
1848 |
| - f"'fill_value' ('{fill_value}') is not in this " |
1849 |
| - "Categorical's categories." |
1850 |
| - ) |
1851 |
| - raise TypeError(msg) |
| 1858 | + fill_value = self._validate_fill_value(fill_value) |
1852 | 1859 |
|
1853 | 1860 | codes = take(self._codes, indexer, allow_fill=allow_fill, fill_value=fill_value)
|
1854 |
| - result = type(self).from_codes(codes, dtype=dtype) |
1855 |
| - return result |
| 1861 | + return self._constructor(codes, dtype=self.dtype, fastpath=True) |
1856 | 1862 |
|
1857 | 1863 | def take_nd(self, indexer, allow_fill: bool = False, fill_value=None):
|
1858 | 1864 | # GH#27745 deprecate alias that other EAs dont have
|
|
0 commit comments