Skip to content

Commit bac7c6e

Browse files
committed
Merge pull request #5730 from jreback/repr_fix
CLN/BUG: indexing fixes (GH5725, GH5727)
2 parents e89ebae + 3574a4a commit bac7c6e

File tree

4 files changed

+52
-15
lines changed

4 files changed

+52
-15
lines changed

doc/source/release.rst

+1
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@ Bug Fixes
826826
- Bug in repeated indexing of object with resultant non-unique index (:issue:`5678`)
827827
- Bug in fillna with Series and a passed series/dict (:issue:`5703`)
828828
- Bug in groupby transform with a datetime-like grouper (:issue:`5712`)
829+
- Bug in multi-index selection in PY3 when using certain keys (:issue:`5725`)
829830

830831
pandas 0.12.0
831832
-------------

pandas/core/generic.py

+21-14
Original file line numberDiff line numberDiff line change
@@ -721,13 +721,13 @@ def __setstate__(self, state):
721721
# to avoid definitional recursion
722722
# e.g. say fill_value needing _data to be
723723
# defined
724-
for k in self._internal_names:
724+
for k in self._internal_names_set:
725725
if k in state:
726726
v = state[k]
727727
object.__setattr__(self, k, v)
728728

729729
for k, v in state.items():
730-
if k not in self._internal_names:
730+
if k not in self._internal_names_set:
731731
object.__setattr__(self, k, v)
732732

733733
else:
@@ -938,15 +938,22 @@ def to_clipboard(self, excel=None, sep=None, **kwargs):
938938
@classmethod
939939
def _create_indexer(cls, name, indexer):
940940
""" create an indexer like _name in the class """
941-
iname = '_%s' % name
942-
setattr(cls, iname, None)
943941

944-
def _indexer(self):
945-
if getattr(self, iname, None) is None:
946-
setattr(self, iname, indexer(self, name))
947-
return getattr(self, iname)
942+
if getattr(cls, name, None) is None:
943+
iname = '_%s' % name
944+
setattr(cls, iname, None)
948945

949-
setattr(cls, name, property(_indexer))
946+
def _indexer(self):
947+
i = getattr(self, iname)
948+
if i is None:
949+
i = indexer(self, name)
950+
setattr(self, iname, i)
951+
return i
952+
953+
setattr(cls, name, property(_indexer))
954+
955+
# add to our internal names set
956+
cls._internal_names_set.add(iname)
950957

951958
def get(self, key, default=None):
952959
"""
@@ -1831,9 +1838,9 @@ def fillna(self, value=None, method=None, axis=0, inplace=False,
18311838
pad / ffill: propagate last valid observation forward to next valid
18321839
backfill / bfill: use NEXT valid observation to fill gap
18331840
value : scalar, dict, or Series
1834-
Value to use to fill holes (e.g. 0), alternately a dict/Series of
1835-
values specifying which value to use for each index (for a Series) or
1836-
column (for a DataFrame). (values not in the dict/Series will not be
1841+
Value to use to fill holes (e.g. 0), alternately a dict/Series of
1842+
values specifying which value to use for each index (for a Series) or
1843+
column (for a DataFrame). (values not in the dict/Series will not be
18371844
filled). This value cannot be a list.
18381845
axis : {0, 1}, default 0
18391846
0: fill column-by-column
@@ -1845,8 +1852,8 @@ def fillna(self, value=None, method=None, axis=0, inplace=False,
18451852
limit : int, default None
18461853
Maximum size gap to forward or backward fill
18471854
downcast : dict, default is None
1848-
a dict of item->dtype of what to downcast if possible,
1849-
or the string 'infer' which will try to downcast to an appropriate
1855+
a dict of item->dtype of what to downcast if possible,
1856+
or the string 'infer' which will try to downcast to an appropriate
18501857
equal type (e.g. float64 to int64 if possible)
18511858
18521859
See also

pandas/core/index.py

+6
Original file line numberDiff line numberDiff line change
@@ -2300,8 +2300,14 @@ def _try_mi(k):
23002300

23012301
# a Timestamp will raise a TypeError in a multi-index
23022302
# rather than a KeyError, try it here
2303+
# note that a string that 'looks' like a Timestamp will raise
2304+
# a KeyError! (GH5725)
23032305
if isinstance(key, (datetime.datetime, np.datetime64)) or (
23042306
compat.PY3 and isinstance(key, compat.string_types)):
2307+
try:
2308+
return _try_mi(key)
2309+
except:
2310+
pass
23052311
try:
23062312
return _try_mi(Timestamp(key))
23072313
except:

pandas/tests/test_indexing.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import itertools
44
import warnings
55

6-
from pandas.compat import range, lrange, StringIO, lmap, map
6+
from pandas.compat import range, lrange, lzip, StringIO, lmap, map
77
from numpy import random, nan
88
from numpy.random import randn
99
import numpy as np
@@ -249,6 +249,15 @@ def _print(result, error = None):
249249
k2 = key2
250250
_eq(t, o, a, obj, key1, k2)
251251

252+
def test_indexer_caching(self):
253+
# GH5727
254+
# make sure that indexers are in the _internal_names_set
255+
n = 1000001
256+
arrays = [lrange(n), lrange(n)]
257+
index = MultiIndex.from_tuples(lzip(*arrays))
258+
s = Series(np.zeros(n), index=index)
259+
str(s)
260+
252261
def test_at_and_iat_get(self):
253262

254263
def _check(f, func, values = False):
@@ -830,6 +839,20 @@ def test_xs_multiindex(self):
830839
expected.columns = expected.columns.droplevel('lvl1')
831840
assert_frame_equal(result, expected)
832841

842+
def test_getitem_multiindex(self):
843+
844+
# GH 5725
845+
# the 'A' happens to be a valid Timestamp so the doesn't raise the appropriate
846+
# error, only in PY3 of course!
847+
index = MultiIndex(levels=[['A', 'B', 'C'], [0, 26, 27, 37, 57, 67, 75, 82]],
848+
labels=[[0, 0, 0, 1, 2, 2, 2, 2, 2, 2], [1, 3, 4, 6, 0, 2, 2, 3, 5, 7]],
849+
names=['tag', 'day'])
850+
arr = np.random.randn(len(index),1)
851+
df = DataFrame(arr,index=index,columns=['val'])
852+
result = df.val['A']
853+
expected = Series(arr.ravel()[0:3],name='val',index=Index([26,37,57],name='day'))
854+
assert_series_equal(result,expected)
855+
833856
def test_setitem_dtype_upcast(self):
834857

835858
# GH3216

0 commit comments

Comments
 (0)