Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API: Deprecate renamae_axis and reindex_axis #17842

Merged
merged 9 commits into from
Oct 11, 2017
7 changes: 5 additions & 2 deletions doc/source/basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v0.21.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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:`~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`).

.. _whatsnew_0210.deprecations.select:

Expand Down
8 changes: 2 additions & 6 deletions pandas/core/computation/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _align_core(terms):
for axis, items in zip(range(ndim), axes):
ti = terms[i].value

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

Expand All @@ -104,11 +104,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())

Expand Down
62 changes: 9 additions & 53 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
_values_from_object,
_maybe_box_datetimelike,
_dict_compat,
_all_not_none,
standardize_mapping)
from pandas.core.generic import NDFrame, _shared_docs
from pandas.core.index import (Index, MultiIndex, _ensure_index,
Expand Down Expand Up @@ -2736,7 +2735,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

Expand Down Expand Up @@ -2783,47 +2782,6 @@ def reindexer(value):

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

def _validate_axis_style_args(self, arg, arg_name, index, columns,
axis, method_name):
if axis is not None:
# Using "axis" style, along with a positional arg
# Both index and columns should be None then
axis = self._get_axis_name(axis)
if index is not None or columns is not None:
msg = (
"Can't specify both 'axis' and 'index' or 'columns'. "
"Specify either\n"
"\t.{method_name}.rename({arg_name}, axis=axis), or\n"
"\t.{method_name}.rename(index=index, columns=columns)"
).format(arg_name=arg_name, method_name=method_name)
raise TypeError(msg)
if axis == 'index':
index = arg
elif axis == 'columns':
columns = arg

elif _all_not_none(arg, index, columns):
msg = (
"Cannot specify all of '{arg_name}', 'index', and 'columns'. "
"Specify either {arg_name} and 'axis', or 'index' and "
"'columns'."
).format(arg_name=arg_name)
raise TypeError(msg)

elif _all_not_none(arg, index):
# This is the "ambiguous" case, so emit a warning
msg = (
"Interpreting call to '.{method_name}(a, b)' as "
"'.{method_name}(index=a, columns=b)'. "
"Use keyword arguments to remove any ambiguity."
).format(method_name=method_name)
warnings.warn(msg, stacklevel=3)
index, columns = arg, index
elif index is None:
# This is for the default axis, like reindex([0, 1])
index = arg
return index, columns

@property
def _series(self):
result = {}
Expand Down Expand Up @@ -2952,11 +2910,10 @@ def align(self, other, join='outer', axis=None, level=None, copy=True,
@Appender(_shared_docs['reindex'] % _shared_doc_kwargs)
def reindex(self, labels=None, index=None, columns=None, axis=None,
**kwargs):
index, columns = self._validate_axis_style_args(labels, 'labels',
index, columns,
axis, 'reindex')
return super(DataFrame, self).reindex(index=index, columns=columns,
**kwargs)
axes = self._validate_axis_style_args(labels, 'labels',
axes=[index, columns],
axis=axis, method_name='reindex')
return super(DataFrame, self).reindex(**axes, **kwargs)

@Appender(_shared_docs['reindex_axis'] % _shared_doc_kwargs)
def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True,
Expand Down Expand Up @@ -3041,11 +2998,10 @@ def rename(self, mapper=None, index=None, columns=None, axis=None,
2 2 5
4 3 6
"""
index, columns = self._validate_axis_style_args(mapper, 'mapper',
index, columns,
axis, 'rename')
return super(DataFrame, self).rename(index=index, columns=columns,
**kwargs)
axes = self._validate_axis_style_args(mapper, 'mapper',
axes=[index, columns],
axis=axis, method_name='rename')
return super(DataFrame, self).rename(**axes, **kwargs)

@Appender(_shared_docs['fillna'] % _shared_doc_kwargs)
def fillna(self, value=None, method=None, axis=None, inplace=False,
Expand Down
92 changes: 74 additions & 18 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
from pandas.core.dtypes.missing import isna, notna
from pandas.core.dtypes.generic import ABCSeries, ABCPanel, ABCDataFrame

from pandas.core.common import (_values_from_object,
from pandas.core.common import (_all_not_none,
_values_from_object,
_maybe_box_datetimelike,
SettingWithCopyError, SettingWithCopyWarning,
AbstractMethodError)
Expand Down Expand Up @@ -729,6 +730,51 @@ def swaplevel(self, i=-2, j=-1, axis=0):
result._data.set_axis(axis, labels.swaplevel(i, j))
return result

def _validate_axis_style_args(self, arg, arg_name, axes,
axis, method_name):
out = {}
for i, value in enumerate(axes):
if value is not None:
out[self._AXIS_NAMES[i]] = value

aliases = ', '.join(self._AXIS_NAMES.values())
if axis is not None:
# Using "axis" style, along with a positional arg
# Both index and columns should be None then
axis = self._get_axis_name(axis)
if any(x is not None for x in axes):
msg = (
"Can't specify both 'axis' and {aliases}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add ". " at the end of this string? (point + space)

"Specify either\n"
"\t.{method_name}({arg_name}, axis=axis), or\n"
"\t.{method_name}(index=index, columns=columns)" # TODO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does your 'todo' point to the fact that "index=index, columns=columns" is not generic for all NDFrame types ? (like Panels) If that is the case, I personally wouldn't care.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'll happily ignore :)

).format(arg_name=arg_name, method_name=method_name,
aliases=aliases)
raise TypeError(msg)
out[axis] = arg

elif _all_not_none(arg, *axes):
msg = (
"Cannot specify all of '{arg_name}', {aliases}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also missing ". "

"Specify either {arg_name} and 'axis', or {aliases}."
).format(arg_name=arg_name, aliases=aliases)
raise TypeError(msg)

elif _all_not_none(arg, axes[0]):
# This is the "ambiguous" case, so emit a warning
msg = (
"Interpreting call to '.{method_name}(a, b)' as "
"'.{method_name}(index=a, columns=b)'. " # TODO
"Use keyword arguments to remove any ambiguity."
).format(method_name=method_name)
warnings.warn(msg, stacklevel=3)
out[self._AXIS_ORDERS[0]] = arg
out[self._AXIS_ORDERS[1]] = axes[0]
elif axes[0] is None:
# This is for the default axis, like reindex([0, 1])
out[self._AXIS_ORDERS[0]] = arg
return out

# ----------------------------------------------------------------------
# Rename

Expand Down Expand Up @@ -893,17 +939,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
Expand All @@ -913,38 +954,45 @@ 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
pandas.Series.rename, pandas.DataFrame.rename
pandas.Index.rename

Examples
--------

>>> 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
is_dict_like(mapper))
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
Expand Down Expand Up @@ -2981,6 +3029,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]))
Expand Down Expand Up @@ -3085,11 +3138,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]},
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
4 changes: 2 additions & 2 deletions pandas/core/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 10 additions & 3 deletions pandas/core/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1197,13 +1197,20 @@ def _wrap_result(self, result, axis):
return self._construct_return_type(result, axes)

@Appender(_shared_docs['reindex'] % _shared_doc_kwargs)
def reindex(self, items=None, major_axis=None, minor_axis=None, **kwargs):
def reindex(self, labels=None,
items=None, major_axis=None, minor_axis=None,
axis=None, **kwargs):
major_axis = (major_axis if major_axis is not None else
kwargs.pop('major', None))
minor_axis = (minor_axis if minor_axis is not None else
kwargs.pop('minor', None))
return super(Panel, self).reindex(items=items, major_axis=major_axis,
minor_axis=minor_axis, **kwargs)
axes = self._validate_axis_style_args(
labels, 'labels', axes=[items, major_axis, minor_axis],
axis=axis, method_name='reindex')
if self.ndim >= 4:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got a bit annoyed with PanelND :) I think this is acceptable until we remove it.

# Hack for PanelND
axes = {}
return super(Panel, self).reindex(**axes, **kwargs)

@Appender(_shared_docs['rename'] % _shared_doc_kwargs)
def rename(self, items=None, major_axis=None, minor_axis=None, **kwargs):
Expand Down
13 changes: 13 additions & 0 deletions pandas/core/panel4d.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,17 @@ def panel4d_init(self, data=None, labels=None, items=None, major_axis=None,
dtype=dtype)


def panel4d_reindex(self, labs=None, labels=None, items=None, major_axis=None,
minor_axis=None, axis=None, **kwargs):
# Hack for reindex_axis deprecation
# Ha, we used labels for two different things
# I think this will work still.
axes = self._validate_axis_style_args(
labs, 'labels',
axes=[labels, items, major_axis, minor_axis],
axis=axis, method_name='reindex')
return super(Panel, self).reindex(**axes, **kwargs)


Panel4D.__init__ = panel4d_init
Panel4D.reindex = panel4d_reindex
Loading