diff --git a/doc/source/basics.rst b/doc/source/basics.rst index be9d1a5d83b85..3044a8886b9ae 100644 --- a/doc/source/basics.rst +++ b/doc/source/basics.rst @@ -1201,8 +1201,11 @@ With a DataFrame, you can simultaneously reindex the index and columns: df df.reindex(index=['c', 'f', 'b'], columns=['three', 'two', 'one']) -For convenience, you may utilize the :meth:`~Series.reindex_axis` method, which -takes the labels and a keyword ``axis`` parameter. +You may also use ``reindex`` with an ``axis`` keyword: + +.. ipython:: python + + df.reindex(index=['c', 'f', 'b'], axis='index') Note that the ``Index`` objects containing the actual axis labels can be **shared** between objects. So if we have a Series and a DataFrame, the diff --git a/doc/source/whatsnew/v0.21.0.txt b/doc/source/whatsnew/v0.21.0.txt index f04410ef63531..f7225314d3935 100644 --- a/doc/source/whatsnew/v0.21.0.txt +++ b/doc/source/whatsnew/v0.21.0.txt @@ -810,6 +810,8 @@ Deprecations - ``.get_value`` and ``.set_value`` on ``Series``, ``DataFrame``, ``Panel``, ``SparseSeries``, and ``SparseDataFrame`` are deprecated in favor of using ``.iat[]`` or ``.at[]`` accessors (:issue:`15269`) - Passing a non-existent column in ``.to_excel(..., columns=)`` is deprecated and will raise a ``KeyError`` in the future (:issue:`17295`) - ``raise_on_error`` parameter to :func:`Series.where`, :func:`Series.mask`, :func:`DataFrame.where`, :func:`DataFrame.mask` is deprecated, in favor of ``errors=`` (:issue:`14968`) +- Using :meth:`DataFrame.rename_axis` and :meth:`Series.rename_axis` to alter index or column *labels* is now deprecated in favor of using ``.rename``. ``rename_axis`` may still be used to alter the name of the index or columns (:issue:`17833`). +- :meth:`~NDFrame.reindex_axis` has been deprecated in favor of :meth:`~NDFrame.reindex`. See :ref`here` for more (:issue:`17833`). .. _whatsnew_0210.deprecations.select: diff --git a/pandas/core/computation/align.py b/pandas/core/computation/align.py index 691eaebfd5fc1..473f0bf258a59 100644 --- a/pandas/core/computation/align.py +++ b/pandas/core/computation/align.py @@ -89,6 +89,7 @@ def _align_core(terms): for axis, items in zip(range(ndim), axes): ti = terms[i].value + # TODO: handle this for when reindex_axis is removed... if hasattr(ti, 'reindex_axis'): transpose = isinstance(ti, pd.Series) and naxes > 1 reindexer = axes[naxes - 1] if transpose else items @@ -104,11 +105,7 @@ def _align_core(terms): ).format(axis=axis, term=terms[i].name, ordm=ordm) warnings.warn(w, category=PerformanceWarning, stacklevel=6) - if transpose: - f = partial(ti.reindex, index=reindexer, copy=False) - else: - f = partial(ti.reindex_axis, reindexer, axis=axis, - copy=False) + f = partial(ti.reindex, reindexer, axis=axis, copy=False) terms[i].update(f()) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 94ff70f287fbe..27598c26c4f92 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2736,7 +2736,7 @@ def reindexer(value): if isinstance(loc, (slice, Series, np.ndarray, Index)): cols = maybe_droplevels(self.columns[loc], key) if len(cols) and not cols.equals(value.columns): - value = value.reindex_axis(cols, axis=1) + value = value.reindex(cols, axis=1) # now align rows value = reindexer(value).T diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 9d9d8334fcaf4..8066a5ebfd98f 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -129,7 +129,7 @@ def __init__(self, data, axes=None, copy=False, dtype=None, if axes is not None: for i, ax in enumerate(axes): - data = data.reindex_axis(ax, axis=i) + data = data.reindex(ax, axis=i) object.__setattr__(self, 'is_copy', None) object.__setattr__(self, '_data', data) @@ -893,17 +893,12 @@ def f(x): rename.__doc__ = _shared_docs['rename'] def rename_axis(self, mapper, axis=0, copy=True, inplace=False): - """ - Alter index and / or columns using input function or functions. - A scalar or list-like for ``mapper`` will alter the ``Index.name`` - or ``MultiIndex.names`` attribute. - A function or dict for ``mapper`` will alter the labels. - Function / dict values must be unique (1-to-1). Labels not contained in - a dict / Series will be left as-is. + """Alter the name of the index or columns. Parameters ---------- - mapper : scalar, list-like, dict-like or function, optional + mapper : scalar, list-like, optional + Value to set the axis name attribute. axis : int or string, default 0 copy : boolean, default True Also copy underlying data @@ -913,6 +908,13 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False): ------- renamed : type of caller or None if inplace=True + Notes + ----- + Prior to version 0.21.0, ``rename_axis`` could also be used to change + the axis *labels* by passing a mapping or scalar. This behavior is + deprecated and will be removed in a future version. Use ``rename`` + instead. + See Also -------- pandas.NDFrame.rename @@ -922,22 +924,19 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False): -------- >>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]}) - >>> df.rename_axis("foo") # scalar, alters df.index.name + >>> df.rename_axis("foo") A B foo 0 1 4 1 2 5 2 3 6 - >>> df.rename_axis(lambda x: 2 * x) # function: alters labels - A B - 0 1 4 - 2 2 5 - 4 3 6 - >>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns") # mapping - ehh B + + >>> df.rename_axis("bar", axis="columns") + bar A B 0 1 4 1 2 5 2 3 6 + """ inplace = validate_bool_kwarg(inplace, 'inplace') non_mapper = is_scalar(mapper) or (is_list_like(mapper) and not @@ -945,6 +944,9 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False): if non_mapper: return self._set_axis_name(mapper, axis=axis, inplace=inplace) else: + msg = ("Using 'rename_axis' to alter labels is deprecated. " + "Use '.rename' instead") + warnings.warn(msg, FutureWarning, stacklevel=2) axis = self._get_axis_name(axis) d = {'copy': copy, 'inplace': inplace} d[axis] = mapper @@ -2981,6 +2983,11 @@ def reindex(self, *args, **kwargs): tolerance = kwargs.pop('tolerance', None) fill_value = kwargs.pop('fill_value', np.nan) + # Series.reindex doesn't use / need the axis kwarg + # We pop and ignore it here, to make writing Series/Frame generic code + # easier + kwargs.pop("axis", None) + if kwargs: raise TypeError('reindex() got an unexpected keyword ' 'argument "{0}"'.format(list(kwargs.keys())[0])) @@ -3085,11 +3092,14 @@ def _reindex_multi(self, axes, copy, fill_value): @Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs) def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, limit=None, fill_value=np.nan): + msg = ("'.reindex_axis' is deprecated and will be removed in a future " + "version. Use '.reindex' instead.") self._consolidate_inplace() axis_name = self._get_axis_name(axis) axis_values = self._get_axis(axis_name) method = missing.clean_reindex_fill_method(method) + warnings.warn(msg, FutureWarning, stacklevel=3) new_index, indexer = axis_values.reindex(labels, method, level, limit=limit) return self._reindex_with_indexers({axis: [new_index, indexer]}, diff --git a/pandas/core/groupby.py b/pandas/core/groupby.py index 9518f17e5f4f1..95392aeed6c1d 100644 --- a/pandas/core/groupby.py +++ b/pandas/core/groupby.py @@ -901,7 +901,7 @@ def reset_identity(values): result.index.get_indexer_for(ax.values)) result = result.take(indexer, axis=self.axis) else: - result = result.reindex_axis(ax, axis=self.axis) + result = result.reindex(ax, axis=self.axis) elif self.group_keys: @@ -4580,7 +4580,7 @@ def _get_sorted_data(self): # this is sort of wasteful but... sorted_axis = data.axes[self.axis].take(self.sort_idx) - sorted_data = data.reindex_axis(sorted_axis, axis=self.axis) + sorted_data = data.reindex(sorted_axis, axis=self.axis) return sorted_data diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index f1a3fe81a4540..654c3510b7cf7 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -368,7 +368,7 @@ def _setitem_with_indexer(self, indexer, value): # so the object is the same index = self.obj._get_axis(i) labels = index.insert(len(index), key) - self.obj._data = self.obj.reindex_axis(labels, i)._data + self.obj._data = self.obj.reindex(labels, axis=i)._data self.obj._maybe_update_cacher(clear=True) self.obj.is_copy = None @@ -1132,7 +1132,7 @@ def _getitem_iterable(self, key, axis=None): if labels.is_unique and Index(keyarr).is_unique: try: - return self.obj.reindex_axis(keyarr, axis=axis) + return self.obj.reindex(keyarr, axis=axis) except AttributeError: # Series diff --git a/pandas/core/internals.py b/pandas/core/internals.py index 689f5521e1ccb..879859309c4f9 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -3283,8 +3283,8 @@ def apply(self, f, axes=None, filter=None, do_integrity_check=False, for k, obj in aligned_args.items(): axis = getattr(obj, '_info_axis_number', 0) - kwargs[k] = obj.reindex_axis(b_items, axis=axis, - copy=align_copy) + kwargs[k] = obj.reindex(b_items, axis=axis, + copy=align_copy) kwargs['mgr'] = self applied = getattr(b, f)(**kwargs) diff --git a/pandas/core/panel.py b/pandas/core/panel.py index b2f50eaf733d8..fb6c4f2d28d04 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -722,7 +722,7 @@ def dropna(self, axis=0, how='any', inplace=False): cond = mask == per_slice new_ax = self._get_axis(axis)[cond] - result = self.reindex_axis(new_ax, axis=axis) + result = self.reindex(new_ax, axis=axis) if inplace: self._update_inplace(result) else: diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index 38c28af4d6ecb..7ee021e5c6246 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -101,14 +101,14 @@ def pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', try: m = MultiIndex.from_arrays(cartesian_product(table.index.levels), names=table.index.names) - table = table.reindex_axis(m, axis=0) + table = table.reindex(m, axis=0) except AttributeError: pass # it's a single level try: m = MultiIndex.from_arrays(cartesian_product(table.columns.levels), names=table.columns.names) - table = table.reindex_axis(m, axis=1) + table = table.reindex(m, axis=1) except AttributeError: pass # it's a single level or a series diff --git a/pandas/core/series.py b/pandas/core/series.py index 93afdc5151b35..8499f8b55d2d0 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2615,6 +2615,10 @@ def reindex_axis(self, labels, axis=0, **kwargs): """ for compatibility with higher dims """ if axis != 0: raise ValueError("cannot reindex series on non-zero axis!") + msg = ("'.reindex_axis' is deprecated and will be removed in a future " + "version. Use '.reindex' instead.") + warnings.warn(msg, FutureWarning, stacklevel=2) + return self.reindex(index=labels, **kwargs) def memory_usage(self, index=True, deep=False): diff --git a/pandas/core/sparse/scipy_sparse.py b/pandas/core/sparse/scipy_sparse.py index d2b9583d8efe5..748a52f484893 100644 --- a/pandas/core/sparse/scipy_sparse.py +++ b/pandas/core/sparse/scipy_sparse.py @@ -134,5 +134,5 @@ def _coo_to_sparse_series(A, dense_index=False): i = range(A.shape[0]) j = range(A.shape[1]) ind = MultiIndex.from_product([i, j]) - s = s.reindex_axis(ind) + s = s.reindex(ind) return s diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index ca1b4d031d3ce..9adab0be96714 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -1040,7 +1040,7 @@ def append_to_multiple(self, d, value, selector, data_columns=None, dc = data_columns if k == selector else None # compute the val - val = value.reindex_axis(v, axis=axis) + val = value.reindex(v, axis=axis) self.append(k, val, data_columns=dc, **kwargs) diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index c4cd562df7eb3..0d77b5f41a08e 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -697,7 +697,7 @@ def _parse_errorbars(self, label, err): from pandas import DataFrame, Series def match_labels(data, e): - e = e.reindex_axis(data.index) + e = e.reindex(data.index) return e # key-matched DataFrame diff --git a/pandas/tests/frame/test_alter_axes.py b/pandas/tests/frame/test_alter_axes.py index feb32324ff1b1..84f7dd108f2cb 100644 --- a/pandas/tests/frame/test_alter_axes.py +++ b/pandas/tests/frame/test_alter_axes.py @@ -436,6 +436,25 @@ def test_rename_axis_inplace(self): assert no_return is None assert_frame_equal(result, expected) + def test_rename_axis_warns(self): + # https://github.com/pandas-dev/pandas/issues/17833 + df = pd.DataFrame({"A": [1, 2], "B": [1, 2]}) + with tm.assert_produces_warning(FutureWarning) as w: + df.rename_axis(id, axis=0) + assert 'rename' in str(w[0].message) + + with tm.assert_produces_warning(FutureWarning) as w: + df.rename_axis({0: 10, 1: 20}, axis=0) + assert 'rename' in str(w[0].message) + + with tm.assert_produces_warning(FutureWarning) as w: + df.rename_axis(id, axis=1) + assert 'rename' in str(w[0].message) + + with tm.assert_produces_warning(FutureWarning) as w: + df['A'].rename_axis(id) + assert 'rename' in str(w[0].message) + def test_rename_multiindex(self): tuples_index = [('foo1', 'bar1'), ('foo2', 'bar2')] diff --git a/pandas/tests/frame/test_axis_select_reindex.py b/pandas/tests/frame/test_axis_select_reindex.py index 38ed8ee20bc50..fee0c8b213bd9 100644 --- a/pandas/tests/frame/test_axis_select_reindex.py +++ b/pandas/tests/frame/test_axis_select_reindex.py @@ -418,11 +418,13 @@ def test_reindex_fill_value(self): assert_frame_equal(result, expected) # reindex_axis - result = df.reindex_axis(lrange(15), fill_value=0., axis=0) + with tm.assert_produces_warning(FutureWarning): + result = df.reindex_axis(lrange(15), fill_value=0., axis=0) expected = df.reindex(lrange(15)).fillna(0) assert_frame_equal(result, expected) - result = df.reindex_axis(lrange(5), fill_value=0., axis=1) + with tm.assert_produces_warning(FutureWarning): + result = df.reindex_axis(lrange(5), fill_value=0., axis=1) expected = df.reindex(columns=lrange(5)).fillna(0) assert_frame_equal(result, expected) @@ -1030,12 +1032,16 @@ def test_reindex_corner(self): def test_reindex_axis(self): cols = ['A', 'B', 'E'] - reindexed1 = self.intframe.reindex_axis(cols, axis=1) + with tm.assert_produces_warning(FutureWarning) as m: + reindexed1 = self.intframe.reindex_axis(cols, axis=1) + assert 'reindex' in str(m[0].message) reindexed2 = self.intframe.reindex(columns=cols) assert_frame_equal(reindexed1, reindexed2) rows = self.intframe.index[0:5] - reindexed1 = self.intframe.reindex_axis(rows, axis=0) + with tm.assert_produces_warning(FutureWarning) as m: + reindexed1 = self.intframe.reindex_axis(rows, axis=0) + assert 'reindex' in str(m[0].message) reindexed2 = self.intframe.reindex(index=rows) assert_frame_equal(reindexed1, reindexed2) @@ -1043,7 +1049,9 @@ def test_reindex_axis(self): # no-op case cols = self.frame.columns.copy() - newFrame = self.frame.reindex_axis(cols, axis=1) + with tm.assert_produces_warning(FutureWarning) as m: + newFrame = self.frame.reindex_axis(cols, axis=1) + assert 'reindex' in str(m[0].message) assert_frame_equal(newFrame, self.frame) def test_reindex_with_nans(self): diff --git a/pandas/tests/reshape/test_reshape.py b/pandas/tests/reshape/test_reshape.py index 632d3b4ad2e7a..fc9f89934b4ea 100644 --- a/pandas/tests/reshape/test_reshape.py +++ b/pandas/tests/reshape/test_reshape.py @@ -311,7 +311,7 @@ def test_include_na(self): 'a': {0: 1, 1: 0, 2: 0}, 'b': {0: 0, 1: 1, 2: 0}}, dtype=np.uint8) - exp_na = exp_na.reindex_axis(['a', 'b', nan], 1) + exp_na = exp_na.reindex(['a', 'b', nan], axis=1) # hack (NaN handling in assert_index_equal) exp_na.columns = res_na.columns assert_frame_equal(res_na, exp_na) @@ -542,8 +542,8 @@ def test_basic_drop_first_NA(self): 2: 0}, nan: {0: 0, 1: 0, - 2: 1}}, dtype=np.uint8).reindex_axis( - ['b', nan], 1) + 2: 1}}, dtype=np.uint8).reindex( + ['b', nan], axis=1) assert_frame_equal(res_na, exp_na) res_just_na = get_dummies([nan], dummy_na=True, sparse=self.sparse, diff --git a/pandas/tests/sparse/test_series.py b/pandas/tests/sparse/test_series.py index 7c7399317809f..c218eee921bb1 100644 --- a/pandas/tests/sparse/test_series.py +++ b/pandas/tests/sparse/test_series.py @@ -1414,6 +1414,12 @@ def test_deprecated_numpy_func_call(self): check_stacklevel=False): getattr(getattr(self, series), func)() + def test_deprecated_reindex_axis(self): + # https://github.com/pandas-dev/pandas/issues/17833 + with tm.assert_produces_warning(FutureWarning) as m: + self.bseries.reindex_axis([0, 1, 2]) + assert 'reindex' in str(m[0].message) + @pytest.mark.parametrize( 'datetime_type', (np.datetime64, diff --git a/pandas/tests/test_multilevel.py b/pandas/tests/test_multilevel.py index 94577db15f01a..785be71e236d7 100644 --- a/pandas/tests/test_multilevel.py +++ b/pandas/tests/test_multilevel.py @@ -1805,7 +1805,7 @@ def test_reindex_level_partial_selection(self): expected = self.frame.iloc[[0, 1, 2, 7, 8, 9]] tm.assert_frame_equal(result, expected) - result = self.frame.T.reindex_axis(['foo', 'qux'], axis=1, level=0) + result = self.frame.T.reindex(['foo', 'qux'], axis=1, level=0) tm.assert_frame_equal(result, expected.T) result = self.frame.loc[['foo', 'qux']] diff --git a/pandas/tests/test_resample.py b/pandas/tests/test_resample.py index cd15203eccd82..4e26689badb3c 100644 --- a/pandas/tests/test_resample.py +++ b/pandas/tests/test_resample.py @@ -1422,7 +1422,7 @@ def test_resample_ohlc_dataframe(self): Timestamp('2011-01-06 10:59:05', tz=None): 1500000000, Timestamp('2011-01-06 12:43:33', tz=None): 5000000000, Timestamp('2011-01-06 12:54:09', tz=None): 100000000}}) - ).reindex_axis(['VOLUME', 'PRICE'], axis=1) + ).reindex(['VOLUME', 'PRICE'], axis=1) res = df.resample('H').ohlc() exp = pd.concat([df['VOLUME'].resample('H').ohlc(), df['PRICE'].resample('H').ohlc()], @@ -1652,7 +1652,7 @@ def test_resample_categorical_data_with_timedeltaindex(self): expected = DataFrame({'Group_obj': ['A', 'A'], 'Group': ['A', 'A']}, index=pd.to_timedelta([0, 10], unit='s')) - expected = expected.reindex_axis(['Group_obj', 'Group'], 1) + expected = expected.reindex(['Group_obj', 'Group'], axis=1) tm.assert_frame_equal(result, expected) def test_resample_daily_anchored(self):