Skip to content

Commit

Permalink
BUG: fix DataFrame.__getitem__ and .loc with non-list listlikes
Browse files Browse the repository at this point in the history
  • Loading branch information
toobaz committed Jun 4, 2018
1 parent 4274b84 commit 8b34417
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 38 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.23.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Indexing
- Bug in :meth:`Series.reset_index` where appropriate error was not raised with an invalid level name (:issue:`20925`)
- Bug in :func:`interval_range` when ``start``/``periods`` or ``end``/``periods`` are specified with float ``start`` or ``end`` (:issue:`21161`)
- Bug in :meth:`MultiIndex.set_names` where error raised for a ``MultiIndex`` with ``nlevels == 1`` (:issue:`21149`)
- Bug in :meth:`DataFrame.__getitem__` and :meth:`DataFrame.loc` which did not accept columns keys passed as non-list iterables (:issue:`21294`)
-

I/O
Expand Down
22 changes: 11 additions & 11 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -2664,27 +2664,27 @@ def __getitem__(self, key):
key = com._apply_if_callable(key, self)

# shortcut if we are an actual column
is_mi_columns = isinstance(self.columns, MultiIndex)
try:
if key in self.columns and not is_mi_columns:
return self._getitem_column(key)
except:
pass
return self._getitem_column(key)
except (KeyError, TypeError, ValueError) as exc:
exception = exc

# see if we can slice the rows
indexer = convert_to_index_sliceable(self, key)
if indexer is not None:
return self._getitem_slice(indexer)

if isinstance(key, (Series, np.ndarray, Index, list)):
# indexing with Boolean dataframe
if isinstance(key, DataFrame):
return self._getitem_frame(key)
elif is_list_like(key) and not isinstance(key, tuple):
# either boolean or fancy integer index
return self._getitem_array(key)
elif isinstance(key, DataFrame):
return self._getitem_frame(key)
elif is_mi_columns:
# partial indexing of MultiIndex columns
if isinstance(self.columns, MultiIndex):
return self._getitem_multilevel(key)
else:
return self._getitem_column(key)

raise exception

def _getitem_column(self, key):
""" return the actual column """
Expand Down
55 changes: 28 additions & 27 deletions pandas/tests/frame/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,45 +92,46 @@ def test_get(self):
result = df.get(None)
assert result is None

def test_getitem_iterator(self):
def test_loc_iterable(self):
idx = iter(['A', 'B', 'C'])
result = self.frame.loc[:, idx]
expected = self.frame.loc[:, ['A', 'B', 'C']]
assert_frame_equal(result, expected)

idx = iter(['A', 'B', 'C'])
result = self.frame.loc[:, idx]
expected = self.frame.loc[:, ['A', 'B', 'C']]
assert_frame_equal(result, expected)
@pytest.mark.parametrize(
"idx_type",
[list, iter, Index, set,
lambda l: dict(zip(l, range(len(l)))),
lambda l: dict(zip(l, range(len(l)))).keys()],
ids=["list", "iter", "Index", "set", "dict", "dict_keys"])
@pytest.mark.parametrize("levels", [1, 2])
def test_getitem_listlike(self, idx_type, levels):
# GH 21294

if levels == 1:
frame, missing = self.frame, 'food'
else:
# MultiIndex columns
frame = DataFrame(randn(8, 3),
columns=Index([('foo', 'bar'), ('baz', 'qux'),
('peek', 'aboo')],
name=('sth', 'sth2')))
missing = ('good', 'food')

def test_getitem_list(self):
self.frame.columns.name = 'foo'
keys = [frame.columns[1], frame.columns[0]]
idx = idx_type(keys)
idx_check = list(idx_type(keys))

result = self.frame[['B', 'A']]
result2 = self.frame[Index(['B', 'A'])]
result = frame[idx]

expected = self.frame.loc[:, ['B', 'A']]
expected.columns.name = 'foo'
expected = frame.loc[:, idx_check]
expected.columns.names = frame.columns.names

assert_frame_equal(result, expected)
assert_frame_equal(result2, expected)

assert result.columns.name == 'foo'

with tm.assert_raises_regex(KeyError, 'not in index'):
self.frame[['B', 'A', 'food']]
idx = idx_type(keys + [missing])
with tm.assert_raises_regex(KeyError, 'not in index'):
self.frame[Index(['B', 'A', 'foo'])]

# tuples
df = DataFrame(randn(8, 3),
columns=Index([('foo', 'bar'), ('baz', 'qux'),
('peek', 'aboo')], name=('sth', 'sth2')))

result = df[[('foo', 'bar'), ('baz', 'qux')]]
expected = df.iloc[:, :2]
assert_frame_equal(result, expected)
assert result.columns.names == ('sth', 'sth2')
frame[idx]

def test_getitem_callable(self):
# GH 12533
Expand Down

0 comments on commit 8b34417

Please sign in to comment.