From 185ea601912cf92036391b57e847e716c19f569b Mon Sep 17 00:00:00 2001 From: Pietro Battiston Date: Mon, 17 Jul 2017 13:26:02 +0200 Subject: [PATCH] ENH: provide "inplace" argument to set_axis() closes #14636 --- doc/source/whatsnew/v0.21.0.txt | 1 + pandas/core/generic.py | 83 +++++++++++++++++++++++++- pandas/tests/frame/test_alter_axes.py | 54 +++++++++++++++++ pandas/tests/series/test_alter_axes.py | 39 ++++++++++++ 4 files changed, 174 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.21.0.txt b/doc/source/whatsnew/v0.21.0.txt index cba3691b25ab1c..de7e5e2254850b 100644 --- a/doc/source/whatsnew/v0.21.0.txt +++ b/doc/source/whatsnew/v0.21.0.txt @@ -66,6 +66,7 @@ Other Enhancements - :func:`Series.to_dict` and :func:`DataFrame.to_dict` now support an ``into`` keyword which allows you to specify the ``collections.Mapping`` subclass that you would like returned. The default is ``dict``, which is backwards compatible. (:issue:`16122`) - :func:`RangeIndex.append` now returns a ``RangeIndex`` object when possible (:issue:`16212`) - :func:`Series.rename_axis` and :func:`DataFrame.rename_axis` with ``inplace=True`` now return ``None`` while renaming the axis inplace. (:issue:`15704`) +- :func:`Series.set_axis` now supports the ``inplace`` parameter. (:issue:`14656`) - :func:`Series.to_pickle` and :func:`DataFrame.to_pickle` have gained a ``protocol`` parameter (:issue:`16252`). By default, this parameter is set to `HIGHEST_PROTOCOL `__ - :func:`api.types.infer_dtype` now infers decimals. (:issue:`15690`) - :func:`read_feather` has gained the ``nthreads`` parameter for multi-threaded operations (:issue:`16359`) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index c95129bdaa0059..c4c1b02cdc5abc 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -466,9 +466,86 @@ def _expand_axes(self, key): return new_axes - def set_axis(self, axis, labels): - """ public verson of axis assignment """ - setattr(self, self._get_axis_name(axis), labels) + _shared_docs['set_axis'] = """Assign desired index to given axis + + Parameters + ---------- + labels: list-like or Index + The values for the new index + axis : int or string, default 0 + inplace : boolean, default None + Whether to return a new %(klass)s instance. + + WARNING: inplace=None currently falls back to to True, but + in a future version, will default to False. Use inplace=True + explicitly rather than relying on the default. + + Returns + ------- + renamed : %(klass)s or None + New object if inplace=False, None otherwise. + + See Also + -------- + pandas.NDFrame.rename + + Examples + -------- + >>> s = pd.Series([1, 2, 3]) + >>> s + 0 1 + 1 2 + 2 3 + dtype: int64 + >>> s.set_axis(0, ['a', 'b', 'c'], inplace=False) + a 1 + b 2 + c 3 + dtype: int64 + >>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]}) + >>> df.set_axis(0, ['a', 'b', 'c'], inplace=False) + A B + a 1 4 + b 2 5 + c 3 6 + >>> df.set_axis(1, ['I', 'II'], inplace=False) + I II + 0 1 4 + 1 2 5 + 2 3 6 + >>> df.set_axis(1, ['i', 'ii'], inplace=True) + >>> df + i ii + 0 1 4 + 1 2 5 + 2 3 6 + + """ + + @Appender(_shared_docs['set_axis'] % dict(klass='NDFrame')) + def set_axis(self, labels, axis=0, inplace=None): + if isinstance(labels, (int, str)): + warnings.warn( + "set_axis now takes \"labels\" as first argument, and " + "\"axis\" as named parameter. The old form, with \"axis\" as " + "first parameter and \"labels\" as second, is still supported " + "but will be deprecated in a future version of pandas.", + FutureWarning, stacklevel=2) + labels, axis = axis, labels + + if inplace is None: + warnings.warn( + "set_axis currently defaults to operating inplace.\nThis " + "will change in a future version of pandas, use " + "inplace=True to avoid this warning.", + FutureWarning, stacklevel=2) + inplace = True + if inplace: + setattr(self, self._get_axis_name(axis), labels) + else: + obj = self.copy() + obj.set_axis(labels, axis=axis, inplace=True) + return obj def _set_axis(self, axis, labels): self._data.set_axis(axis, labels) diff --git a/pandas/tests/frame/test_alter_axes.py b/pandas/tests/frame/test_alter_axes.py index 434c02b8eba2f3..5c6f0da95fd77d 100644 --- a/pandas/tests/frame/test_alter_axes.py +++ b/pandas/tests/frame/test_alter_axes.py @@ -908,3 +908,57 @@ def test_set_reset_index(self): df = df.set_index('B') df = df.reset_index() + + def test_set_axis_inplace(self): + # GH14636 + df = DataFrame({'A': [1.1, 2.2, 3.3], + 'B': [5.0, 6.1, 7.2], + 'C': [4.4, 5.5, 6.6]}, + index=[2010, 2011, 2012]) + + expected = {0: df.copy(), + 1: df.copy()} + expected[0].index = list('abc') + expected[1].columns = list('abc') + expected['index'] = expected[0] + expected['columns'] = expected[1] + + for axis in expected: + # inplace=True + # The FutureWarning comes from the fact that we would like to have + # inplace default to False some day + for inplace, warn in (None, FutureWarning), (True, None): + kwargs = {'inplace': inplace} + + result = df.copy() + with tm.assert_produces_warning(warn): + result.set_axis(list('abc'), axis=axis, **kwargs) + tm.assert_frame_equal(result, expected[axis]) + + # inplace=False + result = df.set_axis(list('abc'), axis=axis, inplace=False) + tm.assert_frame_equal(expected[axis], result) + + # omitting the "axis" parameter + with tm.assert_produces_warning(None): + result = df.set_axis(list('abc'), inplace=False) + tm.assert_frame_equal(result, expected[0]) + + def test_set_axis_old_signature(self): + df = DataFrame({'A': [1.1, 2.2, 3.3], + 'B': [5.0, 6.1, 7.2], + 'C': [4.4, 5.5, 6.6]}, + index=[2010, 2011, 2012]) + + expected = {0: df.copy(), + 1: df.copy()} + expected[0].index = list('abc') + expected[1].columns = list('abc') + expected['index'] = expected[0] + expected['columns'] = expected[1] + + # old signature + for axis in expected: + with tm.assert_produces_warning(FutureWarning): + result = df.set_axis(axis, list('abc'), inplace=False) + tm.assert_frame_equal(result, expected[axis]) diff --git a/pandas/tests/series/test_alter_axes.py b/pandas/tests/series/test_alter_axes.py index d93f0326fd3b11..d5c5f36d77a495 100644 --- a/pandas/tests/series/test_alter_axes.py +++ b/pandas/tests/series/test_alter_axes.py @@ -234,3 +234,42 @@ def test_rename_axis_inplace(self): assert no_return is None assert_series_equal(result, expected) + + def test_set_axis_inplace(self): + # GH14636 + + s = Series(np.arange(4), index=[1, 3, 5, 7], dtype='int64') + + expected = s.copy() + expected.index = list('abcd') + + for axis in 0, 'index': + # inplace=True + # The FutureWarning comes from the fact that we would like to have + # inplace default to False some day + for inplace, warn in (None, FutureWarning), (True, None): + result = s.copy() + kwargs = {'inplace': inplace} + with tm.assert_produces_warning(warn): + result.set_axis(list('abcd'), axis=axis, **kwargs) + tm.assert_series_equal(result, expected) + + # inplace=False + result = s.set_axis(list('abcd'), axis=0, inplace=False) + tm.assert_series_equal(expected, result) + + # omitting the "axis" parameter + with tm.assert_produces_warning(None): + result = s.set_axis(list('abcd'), inplace=False) + tm.assert_series_equal(result, expected) + + def test_set_axis_old_signature(self): + s = Series(np.arange(4), index=[1, 3, 5, 7], dtype='int64') + + expected = s.copy() + expected.index = list('abcd') + + for axis in 0, 'index': + with tm.assert_produces_warning(FutureWarning): + result = s.set_axis(0, list('abcd'), inplace=False) + tm.assert_series_equal(result, expected)