Skip to content

Commit 3544394

Browse files
API: Deprecate renamae_axis and reindex_axis (#17842)
* API: Deprecate renamae_axis and reindex_axis Closes #17833 * REF: Refactor axis style validator to generic This sets us up to re-use it for Panel reindex * fixup! API: Deprecate renamae_axis and reindex_axis * fixup! API: Deprecate renamae_axis and reindex_axis * fixup! API: Deprecate renamae_axis and reindex_axis * fixup! API: Deprecate renamae_axis and reindex_axis * Ugh * fixup! API: Deprecate renamae_axis and reindex_axis * Fixup
1 parent 727ea20 commit 3544394

23 files changed

+199
-106
lines changed

doc/source/basics.rst

+5-2
Original file line numberDiff line numberDiff line change
@@ -1201,8 +1201,11 @@ With a DataFrame, you can simultaneously reindex the index and columns:
12011201
df
12021202
df.reindex(index=['c', 'f', 'b'], columns=['three', 'two', 'one'])
12031203
1204-
For convenience, you may utilize the :meth:`~Series.reindex_axis` method, which
1205-
takes the labels and a keyword ``axis`` parameter.
1204+
You may also use ``reindex`` with an ``axis`` keyword:
1205+
1206+
.. ipython:: python
1207+
1208+
df.reindex(index=['c', 'f', 'b'], axis='index')
12061209
12071210
Note that the ``Index`` objects containing the actual axis labels can be
12081211
**shared** between objects. So if we have a Series and a DataFrame, the

doc/source/whatsnew/v0.21.0.txt

+3
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,8 @@ Deprecations
810810
- ``.get_value`` and ``.set_value`` on ``Series``, ``DataFrame``, ``Panel``, ``SparseSeries``, and ``SparseDataFrame`` are deprecated in favor of using ``.iat[]`` or ``.at[]`` accessors (:issue:`15269`)
811811
- Passing a non-existent column in ``.to_excel(..., columns=)`` is deprecated and will raise a ``KeyError`` in the future (:issue:`17295`)
812812
- ``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`)
813+
- 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`).
814+
- :meth:`~DataFrame.reindex_axis` has been deprecated in favor of :meth:`~DataFrame.reindex`. See :ref`here` <whatsnew_0210.enhancements.rename_reindex_axis> for more (:issue:`17833`).
813815

814816
.. _whatsnew_0210.deprecations.select:
815817

@@ -998,6 +1000,7 @@ Reshaping
9981000
- Bug in :func:`unique` where checking a tuple of strings raised a ``TypeError`` (:issue:`17108`)
9991001
- Bug in :func:`concat` where order of result index was unpredictable if it contained non-comparable elements (:issue:`17344`)
10001002
- Fixes regression when sorting by multiple columns on a ``datetime64`` dtype ``Series`` with ``NaT`` values (:issue:`16836`)
1003+
- Bug in :fun:`pivot_table` where the result's columns did not preserve the categorical dtype of ``columns`` when ``dropna`` was ``False`` (:issue:`17842`)
10011004

10021005
Numeric
10031006
^^^^^^^

pandas/core/computation/align.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def _align_core(terms):
8989
for axis, items in zip(range(ndim), axes):
9090
ti = terms[i].value
9191

92-
if hasattr(ti, 'reindex_axis'):
92+
if hasattr(ti, 'reindex'):
9393
transpose = isinstance(ti, pd.Series) and naxes > 1
9494
reindexer = axes[naxes - 1] if transpose else items
9595

@@ -104,11 +104,7 @@ def _align_core(terms):
104104
).format(axis=axis, term=terms[i].name, ordm=ordm)
105105
warnings.warn(w, category=PerformanceWarning, stacklevel=6)
106106

107-
if transpose:
108-
f = partial(ti.reindex, index=reindexer, copy=False)
109-
else:
110-
f = partial(ti.reindex_axis, reindexer, axis=axis,
111-
copy=False)
107+
f = partial(ti.reindex, reindexer, axis=axis, copy=False)
112108

113109
terms[i].update(f())
114110

pandas/core/frame.py

+11-53
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
_values_from_object,
6666
_maybe_box_datetimelike,
6767
_dict_compat,
68-
_all_not_none,
6968
standardize_mapping)
7069
from pandas.core.generic import NDFrame, _shared_docs
7170
from pandas.core.index import (Index, MultiIndex, _ensure_index,
@@ -2736,7 +2735,7 @@ def reindexer(value):
27362735
if isinstance(loc, (slice, Series, np.ndarray, Index)):
27372736
cols = maybe_droplevels(self.columns[loc], key)
27382737
if len(cols) and not cols.equals(value.columns):
2739-
value = value.reindex_axis(cols, axis=1)
2738+
value = value.reindex(cols, axis=1)
27402739
# now align rows
27412740
value = reindexer(value).T
27422741

@@ -2783,47 +2782,6 @@ def reindexer(value):
27832782

27842783
return np.atleast_2d(np.asarray(value))
27852784

2786-
def _validate_axis_style_args(self, arg, arg_name, index, columns,
2787-
axis, method_name):
2788-
if axis is not None:
2789-
# Using "axis" style, along with a positional arg
2790-
# Both index and columns should be None then
2791-
axis = self._get_axis_name(axis)
2792-
if index is not None or columns is not None:
2793-
msg = (
2794-
"Can't specify both 'axis' and 'index' or 'columns'. "
2795-
"Specify either\n"
2796-
"\t.{method_name}.rename({arg_name}, axis=axis), or\n"
2797-
"\t.{method_name}.rename(index=index, columns=columns)"
2798-
).format(arg_name=arg_name, method_name=method_name)
2799-
raise TypeError(msg)
2800-
if axis == 'index':
2801-
index = arg
2802-
elif axis == 'columns':
2803-
columns = arg
2804-
2805-
elif _all_not_none(arg, index, columns):
2806-
msg = (
2807-
"Cannot specify all of '{arg_name}', 'index', and 'columns'. "
2808-
"Specify either {arg_name} and 'axis', or 'index' and "
2809-
"'columns'."
2810-
).format(arg_name=arg_name)
2811-
raise TypeError(msg)
2812-
2813-
elif _all_not_none(arg, index):
2814-
# This is the "ambiguous" case, so emit a warning
2815-
msg = (
2816-
"Interpreting call to '.{method_name}(a, b)' as "
2817-
"'.{method_name}(index=a, columns=b)'. "
2818-
"Use keyword arguments to remove any ambiguity."
2819-
).format(method_name=method_name)
2820-
warnings.warn(msg, stacklevel=3)
2821-
index, columns = arg, index
2822-
elif index is None:
2823-
# This is for the default axis, like reindex([0, 1])
2824-
index = arg
2825-
return index, columns
2826-
28272785
@property
28282786
def _series(self):
28292787
result = {}
@@ -2952,11 +2910,11 @@ def align(self, other, join='outer', axis=None, level=None, copy=True,
29522910
@Appender(_shared_docs['reindex'] % _shared_doc_kwargs)
29532911
def reindex(self, labels=None, index=None, columns=None, axis=None,
29542912
**kwargs):
2955-
index, columns = self._validate_axis_style_args(labels, 'labels',
2956-
index, columns,
2957-
axis, 'reindex')
2958-
return super(DataFrame, self).reindex(index=index, columns=columns,
2959-
**kwargs)
2913+
axes = self._validate_axis_style_args(labels, 'labels',
2914+
axes=[index, columns],
2915+
axis=axis, method_name='reindex')
2916+
kwargs.update(axes)
2917+
return super(DataFrame, self).reindex(**kwargs)
29602918

29612919
@Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs)
29622920
def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True,
@@ -3041,11 +2999,11 @@ def rename(self, mapper=None, index=None, columns=None, axis=None,
30412999
2 2 5
30423000
4 3 6
30433001
"""
3044-
index, columns = self._validate_axis_style_args(mapper, 'mapper',
3045-
index, columns,
3046-
axis, 'rename')
3047-
return super(DataFrame, self).rename(index=index, columns=columns,
3048-
**kwargs)
3002+
axes = self._validate_axis_style_args(mapper, 'mapper',
3003+
axes=[index, columns],
3004+
axis=axis, method_name='rename')
3005+
kwargs.update(axes)
3006+
return super(DataFrame, self).rename(**kwargs)
30493007

30503008
@Appender(_shared_docs['fillna'] % _shared_doc_kwargs)
30513009
def fillna(self, value=None, method=None, axis=None, inplace=False,

pandas/core/generic.py

+74-18
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
from pandas.core.dtypes.missing import isna, notna
3030
from pandas.core.dtypes.generic import ABCSeries, ABCPanel, ABCDataFrame
3131

32-
from pandas.core.common import (_values_from_object,
32+
from pandas.core.common import (_all_not_none,
33+
_values_from_object,
3334
_maybe_box_datetimelike,
3435
SettingWithCopyError, SettingWithCopyWarning,
3536
AbstractMethodError)
@@ -729,6 +730,51 @@ def swaplevel(self, i=-2, j=-1, axis=0):
729730
result._data.set_axis(axis, labels.swaplevel(i, j))
730731
return result
731732

733+
def _validate_axis_style_args(self, arg, arg_name, axes,
734+
axis, method_name):
735+
out = {}
736+
for i, value in enumerate(axes):
737+
if value is not None:
738+
out[self._AXIS_NAMES[i]] = value
739+
740+
aliases = ', '.join(self._AXIS_NAMES.values())
741+
if axis is not None:
742+
# Using "axis" style, along with a positional arg
743+
# Both index and columns should be None then
744+
axis = self._get_axis_name(axis)
745+
if any(x is not None for x in axes):
746+
msg = (
747+
"Can't specify both 'axis' and {aliases}. "
748+
"Specify either\n"
749+
"\t.{method_name}({arg_name}, axis=axis), or\n"
750+
"\t.{method_name}(index=index, columns=columns)"
751+
).format(arg_name=arg_name, method_name=method_name,
752+
aliases=aliases)
753+
raise TypeError(msg)
754+
out[axis] = arg
755+
756+
elif _all_not_none(arg, *axes):
757+
msg = (
758+
"Cannot specify all of '{arg_name}', {aliases}. "
759+
"Specify either {arg_name} and 'axis', or {aliases}."
760+
).format(arg_name=arg_name, aliases=aliases)
761+
raise TypeError(msg)
762+
763+
elif _all_not_none(arg, axes[0]):
764+
# This is the "ambiguous" case, so emit a warning
765+
msg = (
766+
"Interpreting call to '.{method_name}(a, b)' as "
767+
"'.{method_name}(index=a, columns=b)'. " # TODO
768+
"Use keyword arguments to remove any ambiguity."
769+
).format(method_name=method_name)
770+
warnings.warn(msg, stacklevel=3)
771+
out[self._AXIS_ORDERS[0]] = arg
772+
out[self._AXIS_ORDERS[1]] = axes[0]
773+
elif axes[0] is None:
774+
# This is for the default axis, like reindex([0, 1])
775+
out[self._AXIS_ORDERS[0]] = arg
776+
return out
777+
732778
# ----------------------------------------------------------------------
733779
# Rename
734780

@@ -893,17 +939,12 @@ def f(x):
893939
rename.__doc__ = _shared_docs['rename']
894940

895941
def rename_axis(self, mapper, axis=0, copy=True, inplace=False):
896-
"""
897-
Alter index and / or columns using input function or functions.
898-
A scalar or list-like for ``mapper`` will alter the ``Index.name``
899-
or ``MultiIndex.names`` attribute.
900-
A function or dict for ``mapper`` will alter the labels.
901-
Function / dict values must be unique (1-to-1). Labels not contained in
902-
a dict / Series will be left as-is.
942+
"""Alter the name of the index or columns.
903943
904944
Parameters
905945
----------
906-
mapper : scalar, list-like, dict-like or function, optional
946+
mapper : scalar, list-like, optional
947+
Value to set the axis name attribute.
907948
axis : int or string, default 0
908949
copy : boolean, default True
909950
Also copy underlying data
@@ -913,38 +954,45 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False):
913954
-------
914955
renamed : type of caller or None if inplace=True
915956
957+
Notes
958+
-----
959+
Prior to version 0.21.0, ``rename_axis`` could also be used to change
960+
the axis *labels* by passing a mapping or scalar. This behavior is
961+
deprecated and will be removed in a future version. Use ``rename``
962+
instead.
963+
916964
See Also
917965
--------
918-
pandas.NDFrame.rename
966+
pandas.Series.rename, pandas.DataFrame.rename
919967
pandas.Index.rename
920968
921969
Examples
922970
--------
923971
924972
>>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
925-
>>> df.rename_axis("foo") # scalar, alters df.index.name
973+
>>> df.rename_axis("foo")
926974
A B
927975
foo
928976
0 1 4
929977
1 2 5
930978
2 3 6
931-
>>> df.rename_axis(lambda x: 2 * x) # function: alters labels
932-
A B
933-
0 1 4
934-
2 2 5
935-
4 3 6
936-
>>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns") # mapping
937-
ehh B
979+
980+
>>> df.rename_axis("bar", axis="columns")
981+
bar A B
938982
0 1 4
939983
1 2 5
940984
2 3 6
985+
941986
"""
942987
inplace = validate_bool_kwarg(inplace, 'inplace')
943988
non_mapper = is_scalar(mapper) or (is_list_like(mapper) and not
944989
is_dict_like(mapper))
945990
if non_mapper:
946991
return self._set_axis_name(mapper, axis=axis, inplace=inplace)
947992
else:
993+
msg = ("Using 'rename_axis' to alter labels is deprecated. "
994+
"Use '.rename' instead")
995+
warnings.warn(msg, FutureWarning, stacklevel=2)
948996
axis = self._get_axis_name(axis)
949997
d = {'copy': copy, 'inplace': inplace}
950998
d[axis] = mapper
@@ -2981,6 +3029,11 @@ def reindex(self, *args, **kwargs):
29813029
tolerance = kwargs.pop('tolerance', None)
29823030
fill_value = kwargs.pop('fill_value', np.nan)
29833031

3032+
# Series.reindex doesn't use / need the axis kwarg
3033+
# We pop and ignore it here, to make writing Series/Frame generic code
3034+
# easier
3035+
kwargs.pop("axis", None)
3036+
29843037
if kwargs:
29853038
raise TypeError('reindex() got an unexpected keyword '
29863039
'argument "{0}"'.format(list(kwargs.keys())[0]))
@@ -3085,11 +3138,14 @@ def _reindex_multi(self, axes, copy, fill_value):
30853138
@Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs)
30863139
def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True,
30873140
limit=None, fill_value=np.nan):
3141+
msg = ("'.reindex_axis' is deprecated and will be removed in a future "
3142+
"version. Use '.reindex' instead.")
30883143
self._consolidate_inplace()
30893144

30903145
axis_name = self._get_axis_name(axis)
30913146
axis_values = self._get_axis(axis_name)
30923147
method = missing.clean_reindex_fill_method(method)
3148+
warnings.warn(msg, FutureWarning, stacklevel=3)
30933149
new_index, indexer = axis_values.reindex(labels, method, level,
30943150
limit=limit)
30953151
return self._reindex_with_indexers({axis: [new_index, indexer]},

pandas/core/groupby.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ def reset_identity(values):
901901
result.index.get_indexer_for(ax.values))
902902
result = result.take(indexer, axis=self.axis)
903903
else:
904-
result = result.reindex_axis(ax, axis=self.axis)
904+
result = result.reindex(ax, axis=self.axis)
905905

906906
elif self.group_keys:
907907

pandas/core/indexing.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ def _setitem_with_indexer(self, indexer, value):
368368
# so the object is the same
369369
index = self.obj._get_axis(i)
370370
labels = index.insert(len(index), key)
371-
self.obj._data = self.obj.reindex_axis(labels, i)._data
371+
self.obj._data = self.obj.reindex(labels, axis=i)._data
372372
self.obj._maybe_update_cacher(clear=True)
373373
self.obj.is_copy = None
374374

@@ -1132,7 +1132,7 @@ def _getitem_iterable(self, key, axis=None):
11321132
if labels.is_unique and Index(keyarr).is_unique:
11331133

11341134
try:
1135-
return self.obj.reindex_axis(keyarr, axis=axis)
1135+
return self.obj.reindex(keyarr, axis=axis)
11361136
except AttributeError:
11371137

11381138
# Series

pandas/core/internals.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3283,8 +3283,8 @@ def apply(self, f, axes=None, filter=None, do_integrity_check=False,
32833283

32843284
for k, obj in aligned_args.items():
32853285
axis = getattr(obj, '_info_axis_number', 0)
3286-
kwargs[k] = obj.reindex_axis(b_items, axis=axis,
3287-
copy=align_copy)
3286+
kwargs[k] = obj.reindex(b_items, axis=axis,
3287+
copy=align_copy)
32883288

32893289
kwargs['mgr'] = self
32903290
applied = getattr(b, f)(**kwargs)

pandas/core/panel.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -1197,13 +1197,21 @@ def _wrap_result(self, result, axis):
11971197
return self._construct_return_type(result, axes)
11981198

11991199
@Appender(_shared_docs['reindex'] % _shared_doc_kwargs)
1200-
def reindex(self, items=None, major_axis=None, minor_axis=None, **kwargs):
1200+
def reindex(self, labels=None,
1201+
items=None, major_axis=None, minor_axis=None,
1202+
axis=None, **kwargs):
12011203
major_axis = (major_axis if major_axis is not None else
12021204
kwargs.pop('major', None))
12031205
minor_axis = (minor_axis if minor_axis is not None else
12041206
kwargs.pop('minor', None))
1205-
return super(Panel, self).reindex(items=items, major_axis=major_axis,
1206-
minor_axis=minor_axis, **kwargs)
1207+
axes = self._validate_axis_style_args(
1208+
labels, 'labels', axes=[items, major_axis, minor_axis],
1209+
axis=axis, method_name='reindex')
1210+
if self.ndim >= 4:
1211+
# Hack for PanelND
1212+
axes = {}
1213+
kwargs.update(axes)
1214+
return super(Panel, self).reindex(**kwargs)
12071215

12081216
@Appender(_shared_docs['rename'] % _shared_doc_kwargs)
12091217
def rename(self, items=None, major_axis=None, minor_axis=None, **kwargs):

pandas/core/panel4d.py

+14
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,18 @@ def panel4d_init(self, data=None, labels=None, items=None, major_axis=None,
5757
dtype=dtype)
5858

5959

60+
def panel4d_reindex(self, labs=None, labels=None, items=None, major_axis=None,
61+
minor_axis=None, axis=None, **kwargs):
62+
# Hack for reindex_axis deprecation
63+
# Ha, we used labels for two different things
64+
# I think this will work still.
65+
axes = self._validate_axis_style_args(
66+
labs, 'labels',
67+
axes=[labels, items, major_axis, minor_axis],
68+
axis=axis, method_name='reindex')
69+
kwargs.update(axes)
70+
return super(Panel, self).reindex(**kwargs)
71+
72+
6073
Panel4D.__init__ = panel4d_init
74+
Panel4D.reindex = panel4d_reindex

0 commit comments

Comments
 (0)