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
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v0.21.0.txt
Expand Up @@ -811,7 +811,7 @@ Deprecations
- 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` <whatsnew_0210.enhancements.rename_reindex_axis> for more (: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
3 changes: 1 addition & 2 deletions pandas/core/computation/align.py
Expand Up @@ -89,8 +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'):
if hasattr(ti, 'reindex'):
transpose = isinstance(ti, pd.Series) and naxes > 1
reindexer = axes[naxes - 1] if transpose else items

Expand Down
60 changes: 8 additions & 52 deletions pandas/core/frame.py
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 @@ -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
52 changes: 49 additions & 3 deletions pandas/core/generic.py
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 @@ -129,7 +130,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(ax, axis=i)
data = data.reindex_axis(ax, axis=i)

object.__setattr__(self, 'is_copy', None)
object.__setattr__(self, '_data', data)
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 @@ -917,7 +963,7 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False):

See Also
--------
pandas.NDFrame.rename
pandas.Series.rename, pandas.DataFrame.rename
pandas.Index.rename

Examples
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/groupby.py
Expand Up @@ -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(sorted_axis, axis=self.axis)
sorted_data = data.reindex_axis(sorted_axis, axis=self.axis)
Copy link
Member

Choose a reason for hiding this comment

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

This won't cause a deprecation warning in our own code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

data here is a BlockManager (I incorrectly changed this in an earlier commit)


return sorted_data

Expand Down
15 changes: 11 additions & 4 deletions pandas/core/panel.py
Expand Up @@ -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(new_ax, axis=axis)
result = self.reindex_axis(new_ax, axis=axis)
if inplace:
self._update_inplace(result)
else:
Expand Down 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
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
4 changes: 2 additions & 2 deletions pandas/io/pytables.py
Expand Up @@ -3493,15 +3493,15 @@ def get_blk_items(mgr, blocks):
data_columns = self.validate_data_columns(
data_columns, min_itemsize)
if len(data_columns):
mgr = block_obj.reindex_axis(
mgr = block_obj.reindex(
Index(axis_labels).difference(Index(data_columns)),
axis=axis
)._data

blocks = list(mgr.blocks)
blk_items = get_blk_items(mgr, blocks)
for c in data_columns:
mgr = block_obj.reindex_axis([c], axis=axis)._data
mgr = block_obj.reindex([c], axis=axis)._data
blocks.extend(mgr.blocks)
blk_items.extend(get_blk_items(mgr, mgr.blocks))

Expand Down
4 changes: 3 additions & 1 deletion pandas/tests/reshape/test_pivot.py
Expand Up @@ -116,7 +116,9 @@ def test_pivot_table_dropna_categoricals(self):

result_false = df.pivot_table(index='B', columns='A', values='C',
dropna=False)
expected_columns = Series(['a', 'b', 'c', 'd'], name='A')
expected_columns = (
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 wanted to flag this change in the tests. AFAICT, this is actually the desired behavior? Looking for issues about this now.

Series(['a', 'b', 'c', 'd'], name='A').astype('category')
)
Copy link
Member

Choose a reason for hiding this comment

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

how is this change related?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure :) The fact that it's only not a CategoricalIndex when in the columns and dropna=True seems like a bug on master.

# This is on master
In [7]: df.pivot_table(index="B", columns="A", values="C", dropna=False).columns
Out[7]: Index(['a', 'b', 'c', 'd'], dtype='object', name='A')

In [8]: df.pivot_table(index="A", columns="B", values="C", dropna=False).index
Out[8]: CategoricalIndex(['a', 'b', 'c', 'd'], categories=['a', 'b', 'c', 'd'], ordered=False, name='A', dtype='category')

I've added a whatsnew.

expected_false = DataFrame([[0.0, 3.0, 6.0, np.NaN],
[1.0, 4.0, 7.0, np.NaN],
[2.0, 5.0, 8.0, np.NaN]],
Expand Down
16 changes: 16 additions & 0 deletions pandas/tests/test_panel.py
Expand Up @@ -1444,6 +1444,22 @@ def test_reindex(self):
assert_panel_equal(result, self.panel)
assert result is self.panel

def test_reindex_axis_style(self):
with catch_warnings(record=True):
panel = Panel(np.random.rand(5, 5, 5))
expected0 = Panel(panel.values).iloc[[0, 1]]
expected1 = Panel(panel.values).iloc[:, [0, 1]]
expected2 = Panel(panel.values).iloc[:, :, [0, 1]]

result = panel.reindex([0, 1], axis=0)
assert_panel_equal(result, expected0)

result = panel.reindex([0, 1], axis=1)
assert_panel_equal(result, expected1)

result = panel.reindex([0, 1], axis=2)
assert_panel_equal(result, expected2)

def test_reindex_multi(self):
with catch_warnings(record=True):

Expand Down