Repository: pandas-dev/pandas

Issue:
Inconsistent keyword argument name for the 'periods' in pandas.Index.shift for a DatetimeIndex
The actual implementation of ``pandas.Index.shift`` for a datetime-like index takes a differently named parameter for the number of shifts than the base method.

It should be named ``periods``, which would also be consistent with the ``pandas.DataFrame.shift`` or ``pandas.Series.shift`` methods. But for a DatetimeIndex it's ``n``.

See:
https://github.com/pandas-dev/pandas/blob/v0.23.4/pandas/core/indexes/base.py#L2586
vs.
https://github.com/pandas-dev/pandas/blob/v0.23.4/pandas/core/indexes/datetimelike.py#L1021

With a DatetimeIndex, following the documentation leads to a
```
TypeError: shift() got an unexpected keyword argument 'periods'
```

If this is somehow intended, then it should be stated so in the docstring for ``pandas.Index.shift``.

pandas version is 0.23.4



Discussion:
They probably used to be separate classes, and then were merged. We could deprecate `n` and replace it with `periods`.

Resolving patch:
diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py
--- a/pandas/core/arrays/datetimelike.py
+++ b/pandas/core/arrays/datetimelike.py
@@ -38,6 +38,7 @@
 from pandas.core.algorithms import checked_add_with_arr
 
 from .base import ExtensionOpsMixin
+from pandas.util._decorators import deprecate_kwarg
 
 
 def _make_comparison_op(op, cls):
@@ -522,40 +523,54 @@ def _addsub_offset_array(self, other, op):
             kwargs['freq'] = 'infer'
         return type(self)(res_values, **kwargs)
 
-    def shift(self, n, freq=None):
+    @deprecate_kwarg(old_arg_name='n', new_arg_name='periods')
+    def shift(self, periods, freq=None):
         """
-        Specialized shift which produces a Datetime/Timedelta Array/Index
+        Shift index by desired number of time frequency increments.
+
+        This method is for shifting the values of datetime-like indexes
+        by a specified time increment a given number of times.
 
         Parameters
         ----------
-        n : int
-            Periods to shift by
-        freq : DateOffset or timedelta-like, optional
+        periods : int
+            Number of periods (or increments) to shift by,
+            can be positive or negative.
+
+            .. versionchanged:: 0.24.0
+
+        freq : pandas.DateOffset, pandas.Timedelta or string, optional
+            Frequency increment to shift by.
+            If None, the index is shifted by its own `freq` attribute.
+            Offset aliases are valid strings, e.g., 'D', 'W', 'M' etc.
 
         Returns
         -------
-        shifted : same type as self
+        pandas.DatetimeIndex
+            Shifted index.
+
+        See Also
+        --------
+        Index.shift : Shift values of Index.
         """
         if freq is not None and freq != self.freq:
             if isinstance(freq, compat.string_types):
                 freq = frequencies.to_offset(freq)
-            offset = n * freq
+            offset = periods * freq
             result = self + offset
-
             if hasattr(self, 'tz'):
                 result._tz = self.tz
-
             return result
 
-        if n == 0:
+        if periods == 0:
             # immutable so OK
             return self.copy()
 
         if self.freq is None:
             raise NullFrequencyError("Cannot shift with no freq")
 
-        start = self[0] + n * self.freq
-        end = self[-1] + n * self.freq
+        start = self[0] + periods * self.freq
+        end = self[-1] + periods * self.freq
         attribs = self._get_attributes_dict()
         return self._generate_range(start=start, end=end, periods=None,
                                     **attribs)
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -8288,6 +8288,11 @@ def mask(self, cond, other=np.nan, inplace=False, axis=None, level=None,
             See Notes.
         axis : %(axes_single_arg)s
 
+        See Also
+        --------
+        Index.shift : Shift values of Index.
+        DatetimeIndex.shift : Shift values of DatetimeIndex.
+
         Notes
         -----
         If freq is specified then the index values are shifted but the data


Test patch:
diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py
--- a/pandas/tests/indexes/datetimes/test_ops.py
+++ b/pandas/tests/indexes/datetimes/test_ops.py
@@ -540,6 +540,16 @@ def test_shift(self):
             shifted = rng.shift(1, freq=CDay())
             assert shifted[0] == rng[0] + CDay()
 
+    def test_shift_periods(self):
+        # GH #22458 : argument 'n' was deprecated in favor of 'periods'
+        idx = pd.DatetimeIndex(start=START, end=END,
+                               periods=3)
+        tm.assert_index_equal(idx.shift(periods=0), idx)
+        tm.assert_index_equal(idx.shift(0), idx)
+        with tm.assert_produces_warning(FutureWarning,
+                                        check_stacklevel=True):
+            tm.assert_index_equal(idx.shift(n=0), idx)
+
     def test_pickle_unpickle(self):
         unpickled = tm.round_trip_pickle(self.rng)
         assert unpickled.freq is not None