Skip to content

Commit e0ee3a1

Browse files
sinhrksjreback
authored andcommitted
BUG: Subclassed DataFrame slicing may return normal Series
closes #11559 Author: sinhrks <[email protected]> Closes #12906 from sinhrks/subclass_slice and squashes the following commits: fa112f6 [sinhrks] BUG: Subclassed DataFrame slicing may return normal Series
1 parent 6c692ae commit e0ee3a1

File tree

4 files changed

+89
-8
lines changed

4 files changed

+89
-8
lines changed

doc/source/whatsnew/v0.18.1.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ Bug Fixes
298298

299299

300300

301-
301+
- Bug in slicing subclassed ``DataFrame`` defined to return subclassed ``Series`` may return normal ``Series`` (:issue:`11559`)
302302

303303

304304
- Bug in ``.str`` accessor methods may raise ``ValueError`` if input has ``name`` and the result is ``DataFrame`` or ``MultiIndex`` (:issue:`12617`)

pandas/tests/frame/test_subclass.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44

55
from pandas import DataFrame, Series, MultiIndex, Panel
66
import pandas as pd
7-
8-
from pandas.util.testing import (assert_frame_equal,
9-
SubclassedDataFrame)
10-
117
import pandas.util.testing as tm
128

139
from pandas.tests.frame.common import TestData
@@ -75,8 +71,8 @@ def custom_frame_function(self):
7571
self.assertTrue(isinstance(cdf_multi2['A'], CustomSeries))
7672

7773
def test_dataframe_metadata(self):
78-
df = SubclassedDataFrame({'X': [1, 2, 3], 'Y': [1, 2, 3]},
79-
index=['a', 'b', 'c'])
74+
df = tm.SubclassedDataFrame({'X': [1, 2, 3], 'Y': [1, 2, 3]},
75+
index=['a', 'b', 'c'])
8076
df.testattr = 'XXX'
8177

8278
self.assertEqual(df.testattr, 'XXX')
@@ -89,10 +85,46 @@ def test_dataframe_metadata(self):
8985

9086
# GH10553
9187
unpickled = self.round_trip_pickle(df)
92-
assert_frame_equal(df, unpickled)
88+
tm.assert_frame_equal(df, unpickled)
9389
self.assertEqual(df._metadata, unpickled._metadata)
9490
self.assertEqual(df.testattr, unpickled.testattr)
9591

92+
def test_indexing_sliced(self):
93+
# GH 11559
94+
df = tm.SubclassedDataFrame({'X': [1, 2, 3],
95+
'Y': [4, 5, 6],
96+
'Z': [7, 8, 9]},
97+
index=['a', 'b', 'c'])
98+
res = df.loc[:, 'X']
99+
exp = tm.SubclassedSeries([1, 2, 3], index=list('abc'), name='X')
100+
tm.assert_series_equal(res, exp)
101+
tm.assertIsInstance(res, tm.SubclassedSeries)
102+
103+
res = df.iloc[:, 1]
104+
exp = tm.SubclassedSeries([4, 5, 6], index=list('abc'), name='Y')
105+
tm.assert_series_equal(res, exp)
106+
tm.assertIsInstance(res, tm.SubclassedSeries)
107+
108+
res = df.ix[:, 'Z']
109+
exp = tm.SubclassedSeries([7, 8, 9], index=list('abc'), name='Z')
110+
tm.assert_series_equal(res, exp)
111+
tm.assertIsInstance(res, tm.SubclassedSeries)
112+
113+
res = df.loc['a', :]
114+
exp = tm.SubclassedSeries([1, 4, 7], index=list('XYZ'), name='a')
115+
tm.assert_series_equal(res, exp)
116+
tm.assertIsInstance(res, tm.SubclassedSeries)
117+
118+
res = df.iloc[1, :]
119+
exp = tm.SubclassedSeries([2, 5, 8], index=list('XYZ'), name='b')
120+
tm.assert_series_equal(res, exp)
121+
tm.assertIsInstance(res, tm.SubclassedSeries)
122+
123+
res = df.ix['c', :]
124+
exp = tm.SubclassedSeries([3, 6, 9], index=list('XYZ'), name='c')
125+
tm.assert_series_equal(res, exp)
126+
tm.assertIsInstance(res, tm.SubclassedSeries)
127+
96128
def test_to_panel_expanddim(self):
97129
# GH 9762
98130

pandas/tests/series/test_subclass.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# coding=utf-8
2+
# pylint: disable-msg=E1101,W0612
3+
4+
import pandas.util.testing as tm
5+
6+
7+
class TestSeriesSubclassing(tm.TestCase):
8+
9+
_multiprocess_can_split_ = True
10+
11+
def test_indexing_sliced(self):
12+
s = tm.SubclassedSeries([1, 2, 3, 4], index=list('abcd'))
13+
res = s.loc[['a', 'b']]
14+
exp = tm.SubclassedSeries([1, 2], index=list('ab'))
15+
tm.assert_series_equal(res, exp)
16+
tm.assertIsInstance(res, tm.SubclassedSeries)
17+
18+
res = s.iloc[[2, 3]]
19+
exp = tm.SubclassedSeries([3, 4], index=list('cd'))
20+
tm.assert_series_equal(res, exp)
21+
tm.assertIsInstance(res, tm.SubclassedSeries)
22+
23+
res = s.ix[['a', 'b']]
24+
exp = tm.SubclassedSeries([1, 2], index=list('ab'))
25+
tm.assert_series_equal(res, exp)
26+
tm.assertIsInstance(res, tm.SubclassedSeries)
27+
28+
def test_to_frame(self):
29+
s = tm.SubclassedSeries([1, 2, 3, 4], index=list('abcd'), name='xxx')
30+
res = s.to_frame()
31+
exp = tm.SubclassedDataFrame({'xxx': [1, 2, 3, 4]}, index=list('abcd'))
32+
tm.assert_frame_equal(res, exp)
33+
tm.assertIsInstance(res, tm.SubclassedDataFrame)

pandas/util/testing.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2419,13 +2419,29 @@ def inner(*args, **kwargs):
24192419
return wrapper
24202420

24212421

2422+
class SubclassedSeries(Series):
2423+
_metadata = ['testattr']
2424+
2425+
@property
2426+
def _constructor(self):
2427+
return SubclassedSeries
2428+
2429+
@property
2430+
def _constructor_expanddim(self):
2431+
return SubclassedDataFrame
2432+
2433+
24222434
class SubclassedDataFrame(DataFrame):
24232435
_metadata = ['testattr']
24242436

24252437
@property
24262438
def _constructor(self):
24272439
return SubclassedDataFrame
24282440

2441+
@property
2442+
def _constructor_sliced(self):
2443+
return SubclassedSeries
2444+
24292445

24302446
@contextmanager
24312447
def patch(ob, attr, value):

0 commit comments

Comments
 (0)