Skip to content

DEPR: Deprecate NDFrame.swapaxes #26654

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v0.25.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,8 @@ Other Deprecations
- The :meth:`DataFrame.compound` and :meth:`Series.compound` methods are deprecated and will be removed in a future version (:issue:`26405`).
- The internal attributes ``_start``, ``_stop`` and ``_step`` attributes of :class:`RangeIndex` have been deprecated.
Use the public attributes :attr:`~RangeIndex.start`, :attr:`~RangeIndex.stop` and :attr:`~RangeIndex.step` instead (:issue:`26581`).
- The :meth:`DataFrame.swapaxes` and :meth:`Series.swapaxes` methods are deprecated and will be removed in a future version.
Use :meth:`DataFrame.transpose` and :meth:`Series.transpose` instead (:issue:`26654`).


.. _whatsnew_0250.prior_deprecations:
Expand Down
7 changes: 7 additions & 0 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,10 +695,17 @@ def swapaxes(self, axis1, axis2, copy=True):
"""
Interchange axes and swap values axes appropriately.

.. deprecated:: 0.25.0
Use ``.T`` or ``.transpose()`` instead.

Returns
-------
y : same as input
"""
warnings.warn("{obj}.swapaxes is deprecated and will be removed "
"in a future version. Use {obj}.T or {obj}.transpose() "
"instead".format(obj=type(self).__name__),
FutureWarning, stacklevel=2)
i = self._get_axis_number(axis1)
j = self._get_axis_number(axis2)

Expand Down
10 changes: 5 additions & 5 deletions pandas/core/groupby/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,14 +468,14 @@ def _cython_operation(self, kind, values, how, axis, min_count=-1,
arity = self._cython_arity.get(how, 1)

vdim = values.ndim
swapped = False
transposed = False
if vdim == 1:
values = values[:, None]
out_shape = (self.ngroups, arity)
else:
if axis > 0:
swapped = True
values = values.swapaxes(0, axis)
transposed = True
values = values.transpose()
if arity > 1:
raise NotImplementedError("arity of more than 1 is not "
"supported for the 'how' argument")
Expand Down Expand Up @@ -567,8 +567,8 @@ def _cython_operation(self, kind, values, how, axis, min_count=-1,
else:
names = None

if swapped:
result = result.swapaxes(0, axis)
if transposed:
result = result.transpose()

return result, names

Expand Down
1 change: 1 addition & 0 deletions pandas/core/internals/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,7 @@ def func(cond, values, other):
# might need to separate out blocks
axis = cond.ndim - 1
cond = cond.swapaxes(axis, 0)

mask = np.array([cond[i].all() for i in range(cond.shape[0])],
dtype=bool)

Expand Down
14 changes: 6 additions & 8 deletions pandas/tests/frame/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,15 +363,13 @@ def test_transpose(self, float_frame):
for col, s in mixed_T.items():
assert s.dtype == np.object_

def test_swapaxes(self):
def test_swapaxes_deprecated(self):
df = self.klass(np.random.randn(10, 5))
self._assert_frame_equal(df.T, df.swapaxes(0, 1))
self._assert_frame_equal(df.T, df.swapaxes(1, 0))
self._assert_frame_equal(df, df.swapaxes(0, 0))
msg = ("No axis named 2 for object type"
r" <class 'pandas.core(.sparse)?.frame.(Sparse)?DataFrame'>")
with pytest.raises(ValueError, match=msg):
df.swapaxes(2, 5)

with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason you need the check_stacklevel=False ? (it seems a simple first level function, not multiple levels deep)

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 an error related to stack level, because something else is also deprecated. I can't exactly remember which function was deprecated, but I just thought this was the easy solution (the exact stack level isn't that important...)

Copy link
Member

Choose a reason for hiding this comment

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

(the exact stack level isn't that important...)

It is relevant for the user experience. But the stacklevel of 2 should be correct.
But a different deprecation being raised sounds a bit suspicious. Can you check which one it was?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It has something to do with SparseDataFrame:

    def test_swapaxes_deprecated(self):
        df = self.klass(np.random.randn(10, 5))

        with tm.assert_produces_warning(FutureWarning):
>           swapped = df.swapaxes(0, 1)

pandas\tests\frame\test_api.py:370:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <contextlib._GeneratorContextManager object at 0x000002453AD9E940>, type = None, value = None, traceback = None

    def __exit__(self, type, value, traceback):
        if type is None:
            try:
>               next(self.gen)
E               AssertionError: Warning not set with correct stacklevel. File where warning is raised: C:\Users\TP\Documents\Python\pandasdev\pandasdev\pandas\core\generic.py != C:\Users\TP\Documents\Python\pandasdev\pandasdev\pandas\tests\frame\test_api.py. Warning message: SparseDataFrame is deprecated and will be removed in a future version.
E               Use a regular DataFrame whose columns are SparseArrays instead.
E
E               See http://pandas.pydata.org/pandas-docs/stable/user_guide/sparse.html#migrating for more.

I'll try to work around it.

Copy link
Contributor

Choose a reason for hiding this comment

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

assert_produces_warning doesn't handle stack level correctly with multiple warnings. I'd recommend just check_stacklevel=False like you've done.

Copy link
Member

Choose a reason for hiding this comment

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

or don't test it for SparseDataFrame? That's deprecated anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you explain why the stack level important? Seems not so relevant to me.

Copy link
Member

Choose a reason for hiding this comment

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

It's not that important here, as I can see you set it correctly.

But, in general, we want to test it because it ensures that end-users get a warning with the correction "location" indicated. If the stacklevel is set correctly, the warning message points to where this deprecated feature is being used. If not, it points to some other place, which is not useful or even plain confusing.

swapped = df.swapaxes(0, 1)

tm.assert_frame_equal(swapped, df.T)

def test_axis_aliases(self, float_frame):
f = float_frame
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/generic/test_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ def test_size_compat(self):

def test_split_compat(self):
# xref GH8846
o = self._construct(shape=10)
o = self._construct(shape=10).values
Copy link
Member

Choose a reason for hiding this comment

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

This defeats the purpose of the test, I think, as it was actually testing that np.array_split works on a DataFrame/Series.

So the "quick fix" is rather to ignore warnings in this specific test, so it doesn't fail the test, I think.

Ideally we would prevent that the numpy function raises a warning at all, but not sure that is easily possible.

assert len(np.array_split(o, 5)) == 5
Copy link
Contributor Author

Choose a reason for hiding this comment

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

nparray_split depends on np.swapaxes, so I sidestep the problem here. This test is not very useful. Is that __array_function__ in the pipeline to implement?

I'm thinking that maybe we should maybe keep swapaxes, perhaps hidden in _deprecations like we do with tolist?

assert len(np.array_split(o, 2)) == 2

Expand Down