-
-
Notifications
You must be signed in to change notification settings - Fork 18.2k
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
ERR: disallow non-hashables in Index/MultiIndex construction & rename #20548
Changes from all commits
9047d60
df7650d
dd64219
89e92ab
cd3e53a
cd070e3
351691f
3a7b0b2
70933d5
56fd617
d4ed636
b554bb3
6efd6cc
4fb3a6b
786f43f
01b712e
6f13cd0
26433c3
91ef466
85e35ea
5c2e240
4ca2a52
840cd88
18bcf2a
d98014f
b8a1d7e
edfbd1d
2322346
fa52655
c0f6936
a9c14e6
30da596
667d495
c4c1011
bd75433
74a9b54
b1cb7fd
863f7d3
0723009
7092d49
1d8f67a
12488ff
4a500ba
9ec64b0
47903ae
04f2eed
1a68188
97a2b06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
_ensure_platform_int, | ||
is_categorical_dtype, | ||
is_object_dtype, | ||
is_hashable, | ||
is_iterator, | ||
is_list_like, | ||
pandas_dtype, | ||
|
@@ -634,12 +635,29 @@ def _get_names(self): | |
|
||
def _set_names(self, names, level=None, validate=True): | ||
""" | ||
Set new names on index. Each name has to be a hashable type. | ||
|
||
Parameters | ||
---------- | ||
values : str or sequence | ||
name(s) to set | ||
level : int, level name, or sequence of int/level names (default None) | ||
If the index is a MultiIndex (hierarchical), level(s) to set (None | ||
for all levels). Otherwise level must be None | ||
validate : boolean, default True | ||
validate that the names match level lengths | ||
|
||
Raises | ||
------ | ||
TypeError if each name is not hashable. | ||
|
||
Notes | ||
----- | ||
sets names on levels. WARNING: mutates! | ||
|
||
Note that you generally want to set this *after* changing levels, so | ||
that it only acts on copies | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks good here. can you update the doc-string with the comment below |
||
""" | ||
|
||
# GH 15110 | ||
# Don't allow a single string for names in a MultiIndex | ||
if names is not None and not is_list_like(names): | ||
|
@@ -662,10 +680,20 @@ def _set_names(self, names, level=None, validate=True): | |
|
||
# set the name | ||
for l, name in zip(level, names): | ||
if name is not None and name in used: | ||
raise ValueError('Duplicated level name: "{}", assigned to ' | ||
'level {}, is already used for level ' | ||
'{}.'.format(name, l, used[name])) | ||
if name is not None: | ||
|
||
# GH 20527 | ||
# All items in 'names' need to be hashable: | ||
if not is_hashable(name): | ||
raise TypeError('{}.name must be a hashable type' | ||
.format(self.__class__.__name__)) | ||
|
||
if name in used: | ||
raise ValueError( | ||
'Duplicated level name: "{}", assigned to ' | ||
'level {}, is already used for level ' | ||
'{}.'.format(name, l, used[name])) | ||
|
||
self.levels[l].rename(name, inplace=True) | ||
used[name] = l | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -615,8 +615,27 @@ def test_constructor_mismatched_label_levels(self): | |
with tm.assert_raises_regex(ValueError, label_error): | ||
self.index.copy().set_labels([[0, 0, 0, 0], [0, 0]]) | ||
|
||
@pytest.mark.parametrize('names', [['a', 'b', 'a'], [1, 1, 2], | ||
[1, 'a', 1]]) | ||
def test_constructor_nonhashable_names(self): | ||
# GH 20527 | ||
levels = [[1, 2], [u'one', u'two']] | ||
labels = [[0, 0, 1, 1], [0, 1, 0, 1]] | ||
names = ((['foo'], ['bar'])) | ||
message = "MultiIndex.name must be a hashable type" | ||
tm.assert_raises_regex(TypeError, message, | ||
MultiIndex, levels=levels, | ||
labels=labels, names=names) | ||
|
||
# With .rename() | ||
mi = MultiIndex(levels=[[1, 2], [u'one', u'two']], | ||
labels=[[0, 0, 1, 1], [0, 1, 0, 1]], | ||
names=('foo', 'bar')) | ||
renamed = [['foor'], ['barr']] | ||
tm.assert_raises_regex(TypeError, message, mi.rename, names=renamed) | ||
# With .set_names() | ||
tm.assert_raises_regex(TypeError, message, mi.set_names, names=renamed) | ||
|
||
@pytest.mark.parametrize('names', [['a', 'b', 'a'], ['1', '1', '2'], | ||
['1', 'a', '1']]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jorisvandenbossche IIRC I changed it (in this commit) because the test was failing, but implementation changed a lot after that commit so I'm not sure if reverting this would cause a problem now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems to be passing there! |
||
def test_duplicate_level_names(self, names): | ||
# GH18872 | ||
pytest.raises(ValueError, pd.MultiIndex.from_product, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you also add a mention on
set_names
itself that the names must be hashable (and examples if you want)