Skip to content

Commit aa06810

Browse files
committed
fixup! API: Accept 'axis' keyword argument for reindex
1 parent 1b10e14 commit aa06810

File tree

5 files changed

+178
-47
lines changed

5 files changed

+178
-47
lines changed

doc/source/whatsnew/v0.21.0.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,8 @@ For example:
115115
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
116116

117117
The :meth:`DataFrame.rename` and :meth:`DataFrame.reindex` methods have gained
118-
the ``axis`` keyword to specify the axis to target with the operation. This
119-
harmonizes the method signature of these methods with most of the rest of the
120-
library (:issue:`12392`).
118+
the ``axis`` keyword to specify the axis to target with the operation
119+
(:issue:`12392`).
121120

122121
Here's ``rename``:
123122

@@ -134,11 +133,12 @@ And ``reindex``:
134133
df.reindex(['A', 'B', 'C'], axis='columns')
135134
df.reindex([0, 1, 3], axis='index')
136135

137-
The ``.rename(index=id, columns=str.lower)`` style continues to work as before.
136+
The "index, columns" style continues to work as before.
138137

139138
.. ipython:: python
140139

141140
df.rename(index=id, columns=str.lower)
141+
df.reindex(index=[0, 1, 3], columns=['A', 'B', 'C'])
142142

143143
We *highly* encourage using named arguments to avoid confusion when using either
144144
style.

pandas/core/frame.py

+77-6
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@
113113
by : str or list of str
114114
Name or list of names which refer to the axis items.""",
115115
versionadded_to_excel='',
116-
optional_mapper="""mapper : dict-like or function
117-
Applied to the axis specified by `axis`""",
118116
optional_labels="""labels : array-like, optional
119117
New labels / index to conform the axis specified by 'axis' to.""",
120118
optional_axis="""axis : int or str, optional
@@ -2776,7 +2774,7 @@ def reindexer(value):
27762774

27772775
def _validate_axis_style_args(self, arg, arg_name, index, columns,
27782776
axis, method_name):
2779-
if axis != 0:
2777+
if axis is not None:
27802778
# Using "axis" style, along with a positional arg
27812779
# Both index and columns should be None then
27822780
axis = self._get_axis_name(axis)
@@ -2811,6 +2809,7 @@ def _validate_axis_style_args(self, arg, arg_name, index, columns,
28112809
warnings.warn(msg, stacklevel=3)
28122810
index, columns = arg, index
28132811
elif index is None:
2812+
# This is for the default axis, like reindex([0, 1])
28142813
index = arg
28152814
return index, columns
28162815

@@ -2940,7 +2939,7 @@ def align(self, other, join='outer', axis=None, level=None, copy=True,
29402939
broadcast_axis=broadcast_axis)
29412940

29422941
@Appender(_shared_docs['reindex'] % _shared_doc_kwargs)
2943-
def reindex(self, labels=None, index=None, columns=None, axis=0,
2942+
def reindex(self, labels=None, index=None, columns=None, axis=None,
29442943
**kwargs):
29452944
index, columns = self._validate_axis_style_args(labels, 'labels',
29462945
index, columns,
@@ -2956,9 +2955,81 @@ def reindex_axis(self, labels, axis=0, method=None, level=None, copy=True,
29562955
method=method, level=level, copy=copy,
29572956
limit=limit, fill_value=fill_value)
29582957

2959-
@Appender(_shared_docs['rename'] % _shared_doc_kwargs)
2960-
def rename(self, mapper=None, index=None, columns=None, axis=0,
2958+
def rename(self, mapper=None, index=None, columns=None, axis=None,
29612959
**kwargs):
2960+
"""Alter axes labels.
2961+
2962+
Function / dict values must be unique (1-to-1). Labels not contained in
2963+
a dict / Series will be left as-is. Extra labels listed don't throw an
2964+
error.
2965+
2966+
See the :ref:`user guide <basics.rename>` for more.
2967+
2968+
Parameters
2969+
----------
2970+
mapper : dict-like or function, optional
2971+
Applied to the axis specified by ``axis``
2972+
index, columns : scalar, list-like, dict-like or function, optional
2973+
dict-like or functions transformations to apply to
2974+
that axis' values
2975+
axis : int or str, optional
2976+
Axis to target with ``mapper``. Can be either the axis name
2977+
('index', 'columns') or number (0, 1). The default is 'index'.
2978+
copy : boolean, default True
2979+
Also copy underlying data
2980+
inplace : boolean, default False
2981+
Whether to return a new %(klass)s. If True then value of copy is
2982+
ignored.
2983+
level : int or level name, default None
2984+
In case of a MultiIndex, only rename labels in the specified
2985+
level.
2986+
2987+
Returns
2988+
-------
2989+
renamed : DataFrame
2990+
2991+
See Also
2992+
--------
2993+
pandas.NDFrame.rename_axis
2994+
2995+
Examples
2996+
--------
2997+
2998+
``DataFrame.rename`` supports two calling conventions
2999+
3000+
* ``(index=index_mapper, columns=columns_mapper, ...)
3001+
* ``(mapper, axis={'index', 'columns'}, ...)
3002+
3003+
We *highly* recommend using keyword arguments to clarify your
3004+
intent.
3005+
3006+
>>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
3007+
>>> df.rename(index=str, columns={"A": "a", "B": "c"})
3008+
a c
3009+
0 1 4
3010+
1 2 5
3011+
2 3 6
3012+
3013+
>>> df.rename(index=str, columns={"A": "a", "C": "c"})
3014+
a B
3015+
0 1 4
3016+
1 2 5
3017+
2 3 6
3018+
3019+
Using axis-style parameters
3020+
3021+
>>> df.rename(str.lower, axis='columns')
3022+
a b
3023+
0 1 4
3024+
1 2 5
3025+
2 3 6
3026+
3027+
>>> df.rename({1: 2, 2: 4}, axis='index')
3028+
A B
3029+
0 1 4
3030+
2 2 5
3031+
4 3 6
3032+
"""
29623033
index, columns = self._validate_axis_style_args(mapper, 'mapper',
29633034
index, columns,
29643035
axis, 'rename')

pandas/core/series.py

+59-1
Original file line numberDiff line numberDiff line change
@@ -2525,8 +2525,66 @@ def align(self, other, join='outer', axis=None, level=None, copy=True,
25252525
limit=limit, fill_axis=fill_axis,
25262526
broadcast_axis=broadcast_axis)
25272527

2528-
@Appender(generic._shared_docs['rename'] % _shared_doc_kwargs)
25292528
def rename(self, index=None, **kwargs):
2529+
"""Alter Series row labels or name
2530+
2531+
Function / dict values must be unique (1-to-1). Labels not contained in
2532+
a dict / Series will be left as-is. Extra labels listed don't throw an
2533+
error.
2534+
2535+
Alternatively, change ``Series.name`` with a scalar value.
2536+
2537+
See the :ref:`user guide <basics.rename>` for more.
2538+
2539+
Parameters
2540+
----------
2541+
index : scalar, list-like, dict-like or function, optional
2542+
dict-like or functions are transformations to apply to
2543+
the index.
2544+
Scalar or list-like will alter the ``Series.name`` attribute.
2545+
copy : boolean, default True
2546+
Also copy underlying data
2547+
inplace : boolean, default False
2548+
Whether to return a new %(klass)s. If True then value of copy is
2549+
ignored.
2550+
level : int or level name, default None
2551+
In case of a MultiIndex, only rename labels in the specified
2552+
level.
2553+
2554+
Returns
2555+
-------
2556+
renamed : Series (new object)
2557+
2558+
See Also
2559+
--------
2560+
pandas.DataFrame.rename_axis
2561+
2562+
Examples
2563+
--------
2564+
2565+
>>> s = pd.Series([1, 2, 3])
2566+
>>> s
2567+
0 1
2568+
1 2
2569+
2 3
2570+
dtype: int64
2571+
>>> s.rename("my_name") # scalar, changes Series.name
2572+
0 1
2573+
1 2
2574+
2 3
2575+
Name: my_name, dtype: int64
2576+
>>> s.rename(lambda x: x ** 2) # function, changes labels
2577+
0 1
2578+
1 2
2579+
4 3
2580+
dtype: int64
2581+
>>> s.rename({1: 3, 2: 5}) # mapping, changes labels
2582+
0 1
2583+
3 2
2584+
5 3
2585+
dtype: int64
2586+
2587+
"""
25302588
kwargs['inplace'] = validate_bool_kwarg(kwargs.get('inplace', False),
25312589
'inplace')
25322590

pandas/tests/frame/test_alter_axes.py

+24-29
Original file line numberDiff line numberDiff line change
@@ -868,9 +868,6 @@ def test_rename_axis_style(self):
868868
result = df.rename({'X': 'x', 'Y': 'y'}, axis='index')
869869
assert_frame_equal(result, expected)
870870

871-
result = df.rename(index=str.lower, axis=0)
872-
assert_frame_equal(result, expected)
873-
874871
def test_rename_mapper_multi(self):
875872
df = pd.DataFrame({"A": ['a', 'b'], "B": ['c', 'd'],
876873
'C': [1, 2]}).set_index(["A", "B"])
@@ -902,6 +899,9 @@ def test_rename_axis_style_raises(self):
902899
with tm.assert_raises_regex(TypeError, None):
903900
df.rename(columns=str.lower, axis='columns')
904901

902+
with tm.assert_raises_regex(TypeError, None):
903+
df.rename(index=str.lower, axis=0)
904+
905905
# Multiple targets and axis
906906
with tm.assert_raises_regex(TypeError, None):
907907
df.rename(str.lower, str.lower, axis='columns')
@@ -910,37 +910,32 @@ def test_rename_axis_style_raises(self):
910910
with tm.assert_raises_regex(TypeError, None):
911911
df.rename(str.lower, str.lower, str.lower)
912912

913-
def test_drop_api_equivalence(self):
914-
# https://github.com/pandas-dev/pandas/issues/12392
913+
def test_reindex_api_equivalence(self):
915914
# equivalence of the labels/axis and index/columns API's
916915
df = DataFrame([[1, 2, 3], [3, 4, 5], [5, 6, 7]],
917916
index=['a', 'b', 'c'],
918917
columns=['d', 'e', 'f'])
919918

920-
res1 = df.drop('a')
921-
res2 = df.drop(index='a')
922-
tm.assert_frame_equal(res1, res2)
923-
924-
res1 = df.drop('d', 1)
925-
res2 = df.drop(columns='d')
926-
tm.assert_frame_equal(res1, res2)
927-
928-
res1 = df.drop(labels='e', axis=1)
929-
res2 = df.drop(columns='e')
930-
tm.assert_frame_equal(res1, res2)
931-
932-
res1 = df.drop(['a'], axis=0)
933-
res2 = df.drop(index=['a'])
934-
tm.assert_frame_equal(res1, res2)
935-
936-
res1 = df.drop(['a'], axis=0).drop(['d'], axis=1)
937-
res2 = df.drop(index=['a'], columns=['d'])
938-
939-
with pytest.raises(ValueError):
940-
df.drop(labels='a', index='b')
941-
942-
with pytest.raises(ValueError):
943-
df.drop(axis=1)
919+
res1 = df.reindex(['b', 'a'])
920+
res2 = df.reindex(index=['b', 'a'])
921+
res3 = df.reindex(labels=['b', 'a'])
922+
res4 = df.reindex(labels=['b', 'a'], axis=0)
923+
res5 = df.reindex(['b', 'a'], axis=0)
924+
for res in [res2, res3, res4, res5]:
925+
tm.assert_frame_equal(res1, res)
926+
927+
res1 = df.reindex(columns=['e', 'd'])
928+
res2 = df.reindex(['e', 'd'], axis=1)
929+
res3 = df.reindex(labels=['e', 'd'], axis=1)
930+
for res in [res2, res3]:
931+
tm.assert_frame_equal(res1, res)
932+
933+
res1 = df.reindex(index=['b', 'a'], columns=['e', 'd'])
934+
res2 = df.reindex(columns=['e', 'd'], index=['b', 'a'])
935+
res3 = df.reindex(labels=['b', 'a'], axis=0).reindex(labels=['e', 'd'],
936+
axis=1)
937+
for res in [res2, res3]:
938+
tm.assert_frame_equal(res1, res)
944939

945940
def test_assign_columns(self):
946941
self.frame['hi'] = 'there'

pandas/tests/frame/test_axis_select_reindex.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -474,27 +474,34 @@ def test_reindex_positional_warns(self):
474474
def test_reindex_axis_style_raises(self):
475475
# https://github.com/pandas-dev/pandas/issues/12392
476476
df = pd.DataFrame({"A": [1, 2, 3], 'B': [4, 5, 6]})
477-
with tm.assert_raises_regex(TypeError, ''):
477+
with tm.assert_raises_regex(TypeError, 'reindex'):
478478
df.reindex([0, 1], ['A'], axis=1)
479479

480-
with tm.assert_raises_regex(TypeError, ''):
480+
with tm.assert_raises_regex(TypeError, 'reindex'):
481481
df.reindex([0, 1], ['A'], axis='index')
482482

483-
with tm.assert_raises_regex(TypeError, ''):
483+
with tm.assert_raises_regex(TypeError, 'reindex'):
484484
df.reindex(index=[0, 1], axis='index')
485485

486-
with tm.assert_raises_regex(TypeError, ''):
486+
with tm.assert_raises_regex(TypeError, 'reindex'):
487487
df.reindex(index=[0, 1], axis='columns')
488488

489-
with tm.assert_raises_regex(TypeError, ''):
489+
with tm.assert_raises_regex(TypeError, 'reindex'):
490490
df.reindex(columns=[0, 1], axis='columns')
491491

492-
with tm.assert_raises_regex(TypeError, ''):
492+
with tm.assert_raises_regex(TypeError, 'reindex'):
493493
df.reindex(index=[0, 1], columns=[0, 1], axis='columns')
494494

495-
with tm.assert_raises_regex(TypeError, ''):
495+
with tm.assert_raises_regex(TypeError, 'Cannot specify all'):
496496
df.reindex([0, 1], [0], ['A'])
497497

498+
# Mixing styles
499+
with tm.assert_raises_regex(TypeError, 'reindex'):
500+
df.reindex(index=[0, 1], axis='index')
501+
502+
with tm.assert_raises_regex(TypeError, 'reindex'):
503+
df.reindex(index=[0, 1], axis='columns')
504+
498505
def test_reindex_single_named_indexer(self):
499506
# https://github.com/pandas-dev/pandas/issues/12392
500507
df = pd.DataFrame({"A": [1, 2, 3], "B": [1, 2, 3]})

0 commit comments

Comments
 (0)