diff --git a/doc/source/whatsnew/v0.18.1.txt b/doc/source/whatsnew/v0.18.1.txt index cc84347313b42..249235820e4ae 100644 --- a/doc/source/whatsnew/v0.18.1.txt +++ b/doc/source/whatsnew/v0.18.1.txt @@ -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`) diff --git a/pandas/tests/frame/test_subclass.py b/pandas/tests/frame/test_subclass.py index a7458f5335ec4..cdc35290863d6 100644 --- a/pandas/tests/frame/test_subclass.py +++ b/pandas/tests/frame/test_subclass.py @@ -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 @@ -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') @@ -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 diff --git a/pandas/tests/series/test_subclass.py b/pandas/tests/series/test_subclass.py new file mode 100644 index 0000000000000..016113961ec74 --- /dev/null +++ b/pandas/tests/series/test_subclass.py @@ -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) diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 31f6d19552de3..89200ef79dac9 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -2419,6 +2419,18 @@ 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'] @@ -2426,6 +2438,10 @@ class SubclassedDataFrame(DataFrame): def _constructor(self): return SubclassedDataFrame + @property + def _constructor_sliced(self): + return SubclassedSeries + @contextmanager def patch(ob, attr, value):