|
100 | 100 | is_dict_like, |
101 | 101 | is_dtype_equal, |
102 | 102 | is_extension_array_dtype, |
| 103 | + is_float, |
103 | 104 | is_float_dtype, |
104 | 105 | is_hashable, |
105 | 106 | is_integer, |
@@ -4465,7 +4466,34 @@ def _replace_columnwise( |
4465 | 4466 | return res.__finalize__(self) |
4466 | 4467 |
|
4467 | 4468 | @doc(NDFrame.shift, klass=_shared_doc_kwargs["klass"]) |
4468 | | - def shift(self, periods=1, freq=None, axis=0, fill_value=None) -> DataFrame: |
| 4469 | + def shift( |
| 4470 | + self, periods=1, freq=None, axis=0, fill_value=lib.no_default |
| 4471 | + ) -> DataFrame: |
| 4472 | + axis = self._get_axis_number(axis) |
| 4473 | + |
| 4474 | + ncols = len(self.columns) |
| 4475 | + if axis == 1 and periods != 0 and fill_value is lib.no_default and ncols > 0: |
| 4476 | + # We will infer fill_value to match the closest column |
| 4477 | + |
| 4478 | + if periods > 0: |
| 4479 | + result = self.iloc[:, :-periods] |
| 4480 | + for col in range(min(ncols, abs(periods))): |
| 4481 | + # TODO(EA2D): doing this in a loop unnecessary with 2D EAs |
| 4482 | + # Define filler inside loop so we get a copy |
| 4483 | + filler = self.iloc[:, 0].shift(len(self)) |
| 4484 | + result.insert(0, col, filler, allow_duplicates=True) |
| 4485 | + else: |
| 4486 | + result = self.iloc[:, -periods:] |
| 4487 | + for col in range(min(ncols, abs(periods))): |
| 4488 | + # Define filler inside loop so we get a copy |
| 4489 | + filler = self.iloc[:, -1].shift(len(self)) |
| 4490 | + result.insert( |
| 4491 | + len(result.columns), col, filler, allow_duplicates=True |
| 4492 | + ) |
| 4493 | + |
| 4494 | + result.columns = self.columns.copy() |
| 4495 | + return result |
| 4496 | + |
4469 | 4497 | return super().shift( |
4470 | 4498 | periods=periods, freq=freq, axis=axis, fill_value=fill_value |
4471 | 4499 | ) |
@@ -7215,13 +7243,13 @@ def melt( |
7215 | 7243 | Difference with previous column |
7216 | 7244 |
|
7217 | 7245 | >>> df.diff(axis=1) |
7218 | | - a b c |
7219 | | - 0 NaN 0.0 0.0 |
7220 | | - 1 NaN -1.0 3.0 |
7221 | | - 2 NaN -1.0 7.0 |
7222 | | - 3 NaN -1.0 13.0 |
7223 | | - 4 NaN 0.0 20.0 |
7224 | | - 5 NaN 2.0 28.0 |
| 7246 | + a b c |
| 7247 | + 0 NaN 0 0 |
| 7248 | + 1 NaN -1 3 |
| 7249 | + 2 NaN -1 7 |
| 7250 | + 3 NaN -1 13 |
| 7251 | + 4 NaN 0 20 |
| 7252 | + 5 NaN 2 28 |
7225 | 7253 |
|
7226 | 7254 | Difference with 3rd previous row |
7227 | 7255 |
|
@@ -7255,12 +7283,15 @@ def melt( |
7255 | 7283 | ), |
7256 | 7284 | ) |
7257 | 7285 | def diff(self, periods: int = 1, axis: Axis = 0) -> DataFrame: |
| 7286 | + if not isinstance(periods, int): |
| 7287 | + if not (is_float(periods) and periods.is_integer()): |
| 7288 | + raise ValueError("periods must be an integer") |
| 7289 | + periods = int(periods) |
7258 | 7290 |
|
7259 | 7291 | bm_axis = self._get_block_manager_axis(axis) |
7260 | | - self._consolidate_inplace() |
7261 | 7292 |
|
7262 | 7293 | if bm_axis == 0 and periods != 0: |
7263 | | - return self.T.diff(periods, axis=0).T |
| 7294 | + return self - self.shift(periods, axis=axis) # type: ignore[operator] |
7264 | 7295 |
|
7265 | 7296 | new_data = self._mgr.diff(n=periods, axis=bm_axis) |
7266 | 7297 | return self._constructor(new_data) |
|
0 commit comments