Skip to content

Commit

Permalink
BUG: Subclassed DataFrame slicing may return normal Series
Browse files Browse the repository at this point in the history
closes #11559

Author: sinhrks <sinhrks@gmail.com>

Closes #12906 from sinhrks/subclass_slice and squashes the following commits:

fa112f6 [sinhrks] BUG: Subclassed DataFrame slicing may return normal Series
  • Loading branch information
sinhrks authored and jreback committed Apr 17, 2016
1 parent 6c692ae commit e0ee3a1
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 8 deletions.
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v0.18.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ Bug Fixes




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


- Bug in ``.str`` accessor methods may raise ``ValueError`` if input has ``name`` and the result is ``DataFrame`` or ``MultiIndex`` (:issue:`12617`)
Expand Down
46 changes: 39 additions & 7 deletions pandas/tests/frame/test_subclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@

from pandas import DataFrame, Series, MultiIndex, Panel
import pandas as pd

from pandas.util.testing import (assert_frame_equal,
SubclassedDataFrame)

import pandas.util.testing as tm

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

def test_dataframe_metadata(self):
df = SubclassedDataFrame({'X': [1, 2, 3], 'Y': [1, 2, 3]},
index=['a', 'b', 'c'])
df = tm.SubclassedDataFrame({'X': [1, 2, 3], 'Y': [1, 2, 3]},
index=['a', 'b', 'c'])
df.testattr = 'XXX'

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

# GH10553
unpickled = self.round_trip_pickle(df)
assert_frame_equal(df, unpickled)
tm.assert_frame_equal(df, unpickled)
self.assertEqual(df._metadata, unpickled._metadata)
self.assertEqual(df.testattr, unpickled.testattr)

def test_indexing_sliced(self):
# GH 11559
df = tm.SubclassedDataFrame({'X': [1, 2, 3],
'Y': [4, 5, 6],
'Z': [7, 8, 9]},
index=['a', 'b', 'c'])
res = df.loc[:, 'X']
exp = tm.SubclassedSeries([1, 2, 3], index=list('abc'), name='X')
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

res = df.iloc[:, 1]
exp = tm.SubclassedSeries([4, 5, 6], index=list('abc'), name='Y')
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

res = df.ix[:, 'Z']
exp = tm.SubclassedSeries([7, 8, 9], index=list('abc'), name='Z')
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

res = df.loc['a', :]
exp = tm.SubclassedSeries([1, 4, 7], index=list('XYZ'), name='a')
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

res = df.iloc[1, :]
exp = tm.SubclassedSeries([2, 5, 8], index=list('XYZ'), name='b')
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

res = df.ix['c', :]
exp = tm.SubclassedSeries([3, 6, 9], index=list('XYZ'), name='c')
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

def test_to_panel_expanddim(self):
# GH 9762

Expand Down
33 changes: 33 additions & 0 deletions pandas/tests/series/test_subclass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# coding=utf-8
# pylint: disable-msg=E1101,W0612

import pandas.util.testing as tm


class TestSeriesSubclassing(tm.TestCase):

_multiprocess_can_split_ = True

def test_indexing_sliced(self):
s = tm.SubclassedSeries([1, 2, 3, 4], index=list('abcd'))
res = s.loc[['a', 'b']]
exp = tm.SubclassedSeries([1, 2], index=list('ab'))
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

res = s.iloc[[2, 3]]
exp = tm.SubclassedSeries([3, 4], index=list('cd'))
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

res = s.ix[['a', 'b']]
exp = tm.SubclassedSeries([1, 2], index=list('ab'))
tm.assert_series_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedSeries)

def test_to_frame(self):
s = tm.SubclassedSeries([1, 2, 3, 4], index=list('abcd'), name='xxx')
res = s.to_frame()
exp = tm.SubclassedDataFrame({'xxx': [1, 2, 3, 4]}, index=list('abcd'))
tm.assert_frame_equal(res, exp)
tm.assertIsInstance(res, tm.SubclassedDataFrame)
16 changes: 16 additions & 0 deletions pandas/util/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2419,13 +2419,29 @@ def inner(*args, **kwargs):
return wrapper


class SubclassedSeries(Series):
_metadata = ['testattr']

@property
def _constructor(self):
return SubclassedSeries

@property
def _constructor_expanddim(self):
return SubclassedDataFrame


class SubclassedDataFrame(DataFrame):
_metadata = ['testattr']

@property
def _constructor(self):
return SubclassedDataFrame

@property
def _constructor_sliced(self):
return SubclassedSeries


@contextmanager
def patch(ob, attr, value):
Expand Down

0 comments on commit e0ee3a1

Please sign in to comment.