Skip to content

Commit 04314e6

Browse files
committed
Merge pull request #4791 from jreback/pickle_fix
BUG: pickle failing on FrozenList, when using MultiIndex (GH4788)
2 parents 71615a4 + 22193f4 commit 04314e6

13 files changed

+51
-21
lines changed

pandas/core/base.py

+3
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ def __mul__(self, other):
108108

109109
__imul__ = __mul__
110110

111+
def __reduce__(self):
112+
return self.__class__, (list(self),)
113+
111114
def __hash__(self):
112115
return hash(tuple(self))
113116

pandas/core/index.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2109,7 +2109,7 @@ def __contains__(self, key):
21092109
def __reduce__(self):
21102110
"""Necessary for making this object picklable"""
21112111
object_state = list(np.ndarray.__reduce__(self))
2112-
subclass_state = (self.levels, self.labels, self.sortorder, self.names)
2112+
subclass_state = (list(self.levels), list(self.labels), self.sortorder, list(self.names))
21132113
object_state[2] = (object_state[2], subclass_state)
21142114
return tuple(object_state)
21152115

pandas/io/api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
from pandas.io.html import read_html
1111
from pandas.io.sql import read_sql
1212
from pandas.io.stata import read_stata
13-
from pandas.io.pickle import read_pickle
13+
from pandas.io.pickle import read_pickle, to_pickle

pandas/io/pickle.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ def try_read(path, encoding=None):
3939
# the param
4040
try:
4141
with open(path,'rb') as fh:
42-
with open(path,'rb') as fh:
43-
return pc.load(fh, encoding=encoding, compat=False)
42+
return pc.load(fh, encoding=encoding, compat=False)
4443
except:
4544
with open(path,'rb') as fh:
4645
return pc.load(fh, encoding=encoding, compat=True)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

pandas/io/tests/generate_legacy_pickles.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,23 @@ def create_data():
7777

7878
index = dict(int = Index(np.arange(10)),
7979
date = date_range('20130101',periods=10))
80-
mi = dict(reg = MultiIndex.from_tuples(list(zip([['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
80+
mi = dict(reg2 = MultiIndex.from_tuples(tuple(zip(*[['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
8181
['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']])),
8282
names=['first', 'second']))
8383
series = dict(float = Series(data['A']),
8484
int = Series(data['B']),
8585
mixed = Series(data['E']),
86-
ts = TimeSeries(np.arange(10).astype(np.int64),index=date_range('20130101',periods=10)))
86+
ts = TimeSeries(np.arange(10).astype(np.int64),index=date_range('20130101',periods=10)),
87+
mi = Series(np.arange(5).astype(np.float64),index=MultiIndex.from_tuples(tuple(zip(*[[1,1,2,2,2],
88+
[3,4,3,4,5]])),
89+
names=['one','two'])))
8790
frame = dict(float = DataFrame(dict(A = series['float'], B = series['float'] + 1)),
8891
int = DataFrame(dict(A = series['int'] , B = series['int'] + 1)),
89-
mixed = DataFrame(dict([ (k,data[k]) for k in ['A','B','C','D']])))
92+
mixed = DataFrame(dict([ (k,data[k]) for k in ['A','B','C','D']])),
93+
mi = DataFrame(dict(A = np.arange(5).astype(np.float64), B = np.arange(5).astype(np.int64)),
94+
index=MultiIndex.from_tuples(tuple(zip(*[['bar','bar','baz','baz','baz'],
95+
['one','two','one','two','three']])),
96+
names=['first','second'])))
9097
panel = dict(float = Panel(dict(ItemA = frame['float'], ItemB = frame['float']+1)))
9198

9299

pandas/io/tests/test_pickle.py

+35-13
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,34 @@
1515
from pandas import Index
1616
from pandas.sparse.tests import test_sparse
1717
from pandas import compat
18+
from pandas.compat import u
1819
from pandas.util.misc import is_little_endian
1920
import pandas
2021

22+
def _read_pickle(vf, encoding=None, compat=False):
23+
from pandas.compat import pickle_compat as pc
24+
with open(vf,'rb') as fh:
25+
pc.load(fh, encoding=encoding, compat=compat)
26+
2127
class TestPickle(unittest.TestCase):
2228
_multiprocess_can_split_ = True
2329

2430
def setUp(self):
2531
from pandas.io.tests.generate_legacy_pickles import create_data
2632
self.data = create_data()
33+
self.path = u('__%s__.pickle' % tm.rands(10))
34+
35+
def compare_element(self, typ, result, expected):
36+
if isinstance(expected,Index):
37+
self.assert_(expected.equals(result))
38+
return
39+
40+
if typ.startswith('sp_'):
41+
comparator = getattr(test_sparse,"assert_%s_equal" % typ)
42+
comparator(result,expected,exact_indices=False)
43+
else:
44+
comparator = getattr(tm,"assert_%s_equal" % typ)
45+
comparator(result,expected)
2746

2847
def compare(self, vf):
2948

@@ -36,19 +55,12 @@ def compare(self, vf):
3655

3756
for typ, dv in data.items():
3857
for dt, result in dv.items():
39-
40-
expected = self.data[typ][dt]
41-
42-
if isinstance(expected,Index):
43-
self.assert_(expected.equals(result))
58+
try:
59+
expected = self.data[typ][dt]
60+
except (KeyError):
4461
continue
4562

46-
if typ.startswith('sp_'):
47-
comparator = getattr(test_sparse,"assert_%s_equal" % typ)
48-
comparator(result,expected,exact_indices=False)
49-
else:
50-
comparator = getattr(tm,"assert_%s_equal" % typ)
51-
comparator(result,expected)
63+
self.compare_element(typ, result, expected)
5264

5365
def read_pickles(self, version):
5466
if not is_little_endian():
@@ -68,8 +80,18 @@ def test_read_pickles_0_11_0(self):
6880
def test_read_pickles_0_12_0(self):
6981
self.read_pickles('0.12.0')
7082

71-
def test_read_pickles_0_13_0(self):
72-
self.read_pickles('0.13.0')
83+
def test_round_trip_current(self):
84+
85+
for typ, dv in self.data.items():
86+
87+
for dt, expected in dv.items():
88+
89+
with tm.ensure_clean(self.path) as path:
90+
91+
pd.to_pickle(expected,path)
92+
93+
result = pd.read_pickle(path)
94+
self.compare_element(typ, result, expected)
7395

7496
if __name__ == '__main__':
7597
import nose

setup.py

-1
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,6 @@ def pxd(name):
527527
'tests/data/legacy_pickle/0.10.1/*.pickle',
528528
'tests/data/legacy_pickle/0.11.0/*.pickle',
529529
'tests/data/legacy_pickle/0.12.0/*.pickle',
530-
'tests/data/legacy_pickle/0.13.0/*.pickle',
531530
'tests/data/*.csv',
532531
'tests/data/*.dta',
533532
'tests/data/*.txt',

0 commit comments

Comments
 (0)