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

DEPR: MultiIndex.to_hierarchical, labels #29766

Merged
merged 10 commits into from
Nov 25, 2019
5 changes: 5 additions & 0 deletions doc/source/whatsnew/v1.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ or ``matplotlib.Axes.plot``. See :ref:`plotting.formatters` for more.
- :meth:`pandas.Series.str.cat` now defaults to aligning ``others``, using ``join='left'`` (:issue:`27611`)
- :meth:`pandas.Series.str.cat` does not accept list-likes *within* list-likes anymore (:issue:`27611`)
- Removed the previously deprecated :meth:`ExtensionArray._formatting_values`. Use :attr:`ExtensionArray._formatter` instead. (:issue:`23601`)
- Removed the previously deprecated :meth:`MultiIndex.to_hierarchical` (:issue:`21613`)
- Removed the previously deprecated :attr:`MultiIndex.labels`, use :attr:`MultiIndex.codes` instead (:issue:`23752`)
- Removed the previously deprecated "labels" keyword from the :class:`MultiIndex` constructor, use "codes" instead (:issue:`23752`)
- Removed the previously deprecated :meth:`MultiIndex.set_labels`, use :meth:`MultiIndex.set_codes` instead (:issue:23752`)
jbrockmendel marked this conversation as resolved.
Show resolved Hide resolved
- Removed the previously deprecated "labels" keyword from :meth:`MultiIndex.set_codes`, :meth:`MultiIndex.copy`, :meth:`MultiIndex.drop`, use "codes" instead (:issue:`23752`)
- :func:`read_excel` removed support for "skip_footer" argument, use "skipfooter" instead (:issue:`18836`)
- :meth:`DataFrame.to_records` no longer supports the argument "convert_datetime64" (:issue:`18902`)
- Removed the previously deprecated ``IntervalIndex.from_intervals`` in favor of the :class:`IntervalIndex` constructor (:issue:`19263`)
Expand Down
6 changes: 6 additions & 0 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ def _new_Index(cls, d):
from pandas.core.indexes.period import _new_PeriodIndex

return _new_PeriodIndex(cls, **d)

if issubclass(cls, ABCMultiIndex):
if "labels" in d and "codes" not in d:
# GH#23752 "labels" kwarg has been replaced with "codes"
Copy link
Contributor

Choose a reason for hiding this comment

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

do we have an issue about removing this code at some point, what are the pre-conditions for that?

Copy link
Member Author

Choose a reason for hiding this comment

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

what are the pre-conditions for that?

not wanting/needing to support sufficiently-old pickles

Copy link
Contributor

Choose a reason for hiding this comment

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

can you clarify, meaning < 0.23.4? later? (if you can pls put in the issue)

d["codes"] = d.pop("labels")
simonjayhawkins marked this conversation as resolved.
Show resolved Hide resolved

return cls.__new__(cls, **d)


Expand Down
94 changes: 4 additions & 90 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from pandas._libs.hashtable import duplicated_int64
from pandas.compat.numpy import function as nv
from pandas.errors import PerformanceWarning, UnsortedIndexError
from pandas.util._decorators import Appender, cache_readonly, deprecate_kwarg
from pandas.util._decorators import Appender, cache_readonly

from pandas.core.dtypes.common import (
ensure_int64,
Expand Down Expand Up @@ -229,9 +229,7 @@ class MultiIndex(Index):
of the mentioned helper methods.
"""

_deprecations = Index._deprecations | frozenset(
["labels", "set_labels", "to_hierarchical"]
)
_deprecations = Index._deprecations | frozenset()

# initialize to zero-length tuples to make everything work
_typ = "multiindex"
Expand All @@ -244,7 +242,6 @@ class MultiIndex(Index):
# --------------------------------------------------------------------
# Constructors

@deprecate_kwarg(old_arg_name="labels", new_arg_name="codes")
def __new__(
cls,
levels=None,
Expand Down Expand Up @@ -813,15 +810,6 @@ def set_levels(self, levels, level=None, inplace=False, verify_integrity=True):
def codes(self):
return self._codes

@property
def labels(self):
warnings.warn(
(".labels was deprecated in version 0.24.0. Use .codes instead."),
FutureWarning,
stacklevel=2,
)
return self.codes

def _set_codes(
self, codes, level=None, copy=False, validate=True, verify_integrity=False
):
Expand Down Expand Up @@ -854,23 +842,6 @@ def _set_codes(
self._tuples = None
self._reset_cache()

def set_labels(self, labels, level=None, inplace=False, verify_integrity=True):
warnings.warn(
(
".set_labels was deprecated in version 0.24.0. "
"Use .set_codes instead."
),
FutureWarning,
stacklevel=2,
)
return self.set_codes(
codes=labels,
level=level,
inplace=inplace,
verify_integrity=verify_integrity,
)

@deprecate_kwarg(old_arg_name="labels", new_arg_name="codes")
def set_codes(self, codes, level=None, inplace=False, verify_integrity=True):
"""
Set new codes on MultiIndex. Defaults to returning
Expand Down Expand Up @@ -947,7 +918,6 @@ def set_codes(self, codes, level=None, inplace=False, verify_integrity=True):
if not inplace:
return idx

@deprecate_kwarg(old_arg_name="labels", new_arg_name="codes")
def copy(
self,
names=None,
Expand Down Expand Up @@ -981,7 +951,8 @@ def copy(
"""
name = kwargs.get("name")
names = self._validate_names(name=name, names=names, deep=deep)

if "labels" in kwargs:
raise TypeError("'labels' argument has been removed; use 'codes' instead")
Comment on lines +954 to +955
Copy link
Member

Choose a reason for hiding this comment

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

same

Copy link
Member Author

Choose a reason for hiding this comment

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

ideally i think we should just get kwargs out of the signature of this function, but im fine with adding a test for this in the interim

if deep:
from copy import deepcopy

Expand Down Expand Up @@ -1700,62 +1671,6 @@ def to_frame(self, index=True, name=None):
result.index = self
return result

def to_hierarchical(self, n_repeat, n_shuffle=1):
"""
Return a MultiIndex reshaped to conform to the
shapes given by n_repeat and n_shuffle.

.. deprecated:: 0.24.0

Useful to replicate and rearrange a MultiIndex for combination
with another Index with n_repeat items.

Parameters
----------
n_repeat : int
Number of times to repeat the labels on self.
n_shuffle : int
Controls the reordering of the labels. If the result is going
to be an inner level in a MultiIndex, n_shuffle will need to be
greater than one. The size of each label must divisible by
n_shuffle.

Returns
-------
MultiIndex

Examples
--------
>>> idx = pd.MultiIndex.from_tuples([(1, 'one'), (1, 'two'),
(2, 'one'), (2, 'two')])
>>> idx.to_hierarchical(3)
MultiIndex([(1, 'one'),
(1, 'one'),
(1, 'one'),
(1, 'two'),
(1, 'two'),
(1, 'two'),
(2, 'one'),
(2, 'one'),
(2, 'one'),
(2, 'two'),
(2, 'two'),
(2, 'two')],
)
"""
levels = self.levels
codes = [np.repeat(level_codes, n_repeat) for level_codes in self.codes]
# Assumes that each level_codes is divisible by n_shuffle
codes = [x.reshape(n_shuffle, -1).ravel(order="F") for x in codes]
names = self.names
warnings.warn(
"Method .to_hierarchical is deprecated and will "
"be removed in a future version",
FutureWarning,
stacklevel=2,
)
return MultiIndex(levels=levels, codes=codes, names=names)

def to_flat_index(self):
"""
Convert a MultiIndex to an Index of Tuples containing the level values.
Expand Down Expand Up @@ -2148,7 +2063,6 @@ def repeat(self, repeats, axis=None):
def where(self, cond, other=None):
raise NotImplementedError(".where is not supported for MultiIndex operations")

@deprecate_kwarg(old_arg_name="labels", new_arg_name="codes")
def drop(self, codes, level=None, errors="raise"):
"""
Make new MultiIndex with passed list of codes deleted
Expand Down
12 changes: 0 additions & 12 deletions pandas/tests/indexes/multi/test_constructor.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,6 @@ def test_na_levels():
tm.assert_index_equal(result, expected)


def test_labels_deprecated(idx):
# GH23752
with tm.assert_produces_warning(FutureWarning):
MultiIndex(
levels=[["foo", "bar", "baz", "qux"]],
labels=[[0, 1, 2, 3]],
names=["first"],
)
with tm.assert_produces_warning(FutureWarning):
idx.labels


def test_copy_in_constructor():
levels = np.array(["a", "b", "c"])
codes = np.array([1, 1, 2, 0, 0, 1, 1])
Expand Down
55 changes: 2 additions & 53 deletions pandas/tests/indexes/multi/test_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,59 +133,8 @@ def test_to_frame_resulting_column_order():
assert result == expected


def test_to_hierarchical():
index = MultiIndex.from_tuples([(1, "one"), (1, "two"), (2, "one"), (2, "two")])
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = index.to_hierarchical(3)
expected = MultiIndex(
levels=[[1, 2], ["one", "two"]],
codes=[
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
],
)
tm.assert_index_equal(result, expected)
assert result.names == index.names

# K > 1
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = index.to_hierarchical(3, 2)
expected = MultiIndex(
levels=[[1, 2], ["one", "two"]],
codes=[
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
],
)
tm.assert_index_equal(result, expected)
assert result.names == index.names

# non-sorted
index = MultiIndex.from_tuples(
[(2, "c"), (1, "b"), (2, "a"), (2, "b")], names=["N1", "N2"]
)

with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
result = index.to_hierarchical(2)
expected = MultiIndex.from_tuples(
[
(2, "c"),
(2, "c"),
(1, "b"),
(1, "b"),
(2, "a"),
(2, "a"),
(2, "b"),
(2, "b"),
],
names=["N1", "N2"],
)
tm.assert_index_equal(result, expected)
assert result.names == index.names


def test_roundtrip_pickle_with_tz():
return
return # FIXME: this can't be right?

# GH 8367
# round-trip of timezone
Expand All @@ -198,7 +147,7 @@ def test_roundtrip_pickle_with_tz():


def test_pickle(indices):
return
return # FIXME: this can't be right?

unpickled = tm.round_trip_pickle(indices)
assert indices.equals(unpickled)
Expand Down
6 changes: 0 additions & 6 deletions pandas/tests/indexes/multi/test_copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ def test_shallow_copy(idx):
assert_multiindex_copied(i_copy, idx)


def test_labels_deprecated(idx):
# GH23752
with tm.assert_produces_warning(FutureWarning):
idx.copy(labels=idx.codes)


def test_view(idx):
i_view = idx.view()
assert_multiindex_copied(i_view, idx)
Expand Down
21 changes: 0 additions & 21 deletions pandas/tests/indexes/multi/test_get_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,27 +306,6 @@ def test_set_codes(idx):
result.set_codes(codes=new_codes, level=1, inplace=True)
assert result.equals(expected)

with tm.assert_produces_warning(FutureWarning):
ind.set_codes(labels=new_codes, level=1)


def test_set_labels_deprecated():
# GH23752
ind = pd.MultiIndex.from_tuples([(0, i) for i in range(130)])
new_labels = range(129, -1, -1)
expected = pd.MultiIndex.from_tuples([(0, i) for i in new_labels])

# [w/o mutation]
with tm.assert_produces_warning(FutureWarning):
result = ind.set_labels(labels=new_labels, level=1)
assert result.equals(expected)

# [w/ mutation]
result = ind.copy()
with tm.assert_produces_warning(FutureWarning):
result.set_labels(labels=new_labels, level=1, inplace=True)
assert result.equals(expected)


def test_set_levels_codes_names_bad_input(idx):
levels, codes = idx.levels, idx.codes
Expand Down