-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Idx droplevel #21116
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
Idx droplevel #21116
Changes from 4 commits
e01b88b
fc6a297
56f1568
5cf4957
57a5332
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 |
---|---|---|
|
@@ -3158,6 +3158,60 @@ def _get_level_values(self, level): | |
|
||
get_level_values = _get_level_values | ||
|
||
def droplevel(self, level=0): | ||
""" | ||
Return index with requested level(s) removed. If resulting index has | ||
only 1 level left, the result will be of Index type, not MultiIndex. | ||
|
||
.. versionadded:: 0.23.1 (support for non-MultiIndex) | ||
|
||
Parameters | ||
---------- | ||
level : int, str, or list-like, default 0 | ||
If a string is given, must be the name of a level | ||
If list-like, elements must be names or indexes of levels. | ||
|
||
Returns | ||
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. can u add a versionadded tag 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. (done) |
||
------- | ||
index : Index or MultiIndex | ||
""" | ||
if not isinstance(level, (tuple, list)): | ||
level = [level] | ||
|
||
levnums = sorted(self._get_level_number(lev) for lev in level)[::-1] | ||
|
||
if len(level) == 0: | ||
return self | ||
if len(level) >= self.nlevels: | ||
raise ValueError("Cannot remove {} levels from an index with {} " | ||
"levels: at least one level must be " | ||
"left.".format(len(level), self.nlevels)) | ||
# The two checks above guarantee that here self is a MultiIndex | ||
|
||
new_levels = list(self.levels) | ||
new_labels = list(self.labels) | ||
new_names = list(self.names) | ||
|
||
for i in levnums: | ||
new_levels.pop(i) | ||
new_labels.pop(i) | ||
new_names.pop(i) | ||
|
||
if len(new_levels) == 1: | ||
|
||
# set nan if needed | ||
mask = new_labels[0] == -1 | ||
result = new_levels[0].take(new_labels[0]) | ||
if mask.any(): | ||
result = result.putmask(mask, np.nan) | ||
|
||
result.name = new_names[0] | ||
return result | ||
else: | ||
from .multi import MultiIndex | ||
return MultiIndex(levels=new_levels, labels=new_labels, | ||
names=new_names, verify_integrity=False) | ||
|
||
_index_shared_docs['get_indexer'] = """ | ||
Compute indexer and mask for new index given the current index. The | ||
indexer should be then used as an input to ndarray.take to align the | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1199,9 +1199,8 @@ def reset_index(self, level=None, drop=False, name=None, inplace=False): | |
if not isinstance(level, (tuple, list)): | ||
level = [level] | ||
level = [self.index._get_level_number(lev) for lev in level] | ||
if isinstance(self.index, MultiIndex): | ||
if len(level) < self.index.nlevels: | ||
new_index = self.index.droplevel(level) | ||
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. are there more places u can simplify? maybe in core/indexing.py ? 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. Found only another one. The other uses of droplevel do need to treat differently flat indexes. |
||
if len(level) < self.index.nlevels: | ||
new_index = self.index.droplevel(level) | ||
|
||
if inplace: | ||
self.index = new_index | ||
|
@@ -3177,7 +3176,8 @@ def apply(self, func, convert_dtype=True, args=(), **kwds): | |
|
||
# handle ufuncs and lambdas | ||
if kwds or args and not isinstance(func, np.ufunc): | ||
f = lambda x: func(x, *args, **kwds) | ||
def f(x): | ||
return func(x, *args, **kwds) | ||
else: | ||
f = func | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -245,6 +245,25 @@ def test_constructor_int_dtype_nan(self): | |
result = Index(data, dtype='float') | ||
tm.assert_index_equal(result, expected) | ||
|
||
def test_droplevel(self, indices): | ||
# GH 21115 | ||
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. can u test for all types here; see other test for how to iterate |
||
if isinstance(indices, MultiIndex): | ||
# Tested separately in test_multi.py | ||
return | ||
|
||
assert indices.droplevel([]).equals(indices) | ||
|
||
for level in indices.name, [indices.name]: | ||
if isinstance(indices.name, tuple) and level is indices.name: | ||
# GH 21121 : droplevel with tuple name | ||
continue | ||
with pytest.raises(ValueError): | ||
indices.droplevel(level) | ||
|
||
for level in 'wrong', ['wrong']: | ||
with pytest.raises(KeyError): | ||
indices.droplevel(level) | ||
|
||
@pytest.mark.parametrize("dtype", ['int64', 'uint64']) | ||
def test_constructor_int_dtype_nan_raises(self, dtype): | ||
# see gh-15187 | ||
|
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.
maybe should move to enhancements (later PR is fine).