Skip to content

Commit 3d87ed7

Browse files
committed
API: define _constructor_expanddim for subclassing Series and DataFrame
1 parent b246920 commit 3d87ed7

File tree

6 files changed

+61
-15
lines changed

6 files changed

+61
-15
lines changed

pandas/core/frame.py

+11-8
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ def _constructor(self):
191191

192192
_constructor_sliced = Series
193193

194+
@property
195+
def _constructor_expanddim(self):
196+
from pandas.core.panel import Panel
197+
return Panel
198+
194199
def __init__(self, data=None, index=None, columns=None, dtype=None,
195200
copy=False):
196201
if data is None:
@@ -1064,8 +1069,6 @@ def to_panel(self):
10641069
-------
10651070
panel : Panel
10661071
"""
1067-
from pandas.core.panel import Panel
1068-
10691072
# only support this kind for now
10701073
if (not isinstance(self.index, MultiIndex) or # pragma: no cover
10711074
len(self.index.levels) != 2):
@@ -1103,7 +1106,7 @@ def to_panel(self):
11031106
shape=shape,
11041107
ref_items=selfsorted.columns)
11051108

1106-
return Panel(new_mgr)
1109+
return self._constructor_expanddim(new_mgr)
11071110

11081111
to_wide = deprecate('to_wide', to_panel)
11091112

@@ -4413,12 +4416,12 @@ def mode(self, axis=0, numeric_only=False):
44134416
"""
44144417
Gets the mode(s) of each element along the axis selected. Empty if nothing
44154418
has 2+ occurrences. Adds a row for each mode per label, fills in gaps
4416-
with nan.
4417-
4419+
with nan.
4420+
44184421
Note that there could be multiple values returned for the selected
4419-
axis (when more than one item share the maximum frequency), which is the
4420-
reason why a dataframe is returned. If you want to impute missing values
4421-
with the mode in a dataframe ``df``, you can just do this:
4422+
axis (when more than one item share the maximum frequency), which is the
4423+
reason why a dataframe is returned. If you want to impute missing values
4424+
with the mode in a dataframe ``df``, you can just do this:
44224425
``df.fillna(df.mode().iloc[0])``
44234426
44244427
Parameters

pandas/core/generic.py

+4
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ def _local_dir(self):
154154
def _constructor_sliced(self):
155155
raise NotImplementedError
156156

157+
@property
158+
def _constructor_expanddim(self):
159+
raise NotImplementedError
160+
157161
#----------------------------------------------------------------------
158162
# Axis
159163

pandas/core/series.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,11 @@ def from_array(cls, arr, index=None, name=None, dtype=None, copy=False,
236236
def _constructor(self):
237237
return Series
238238

239+
@property
240+
def _constructor_expanddim(self):
241+
from pandas.core.frame import DataFrame
242+
return DataFrame
243+
239244
# types
240245
@property
241246
def _can_hold_na(self):
@@ -1047,11 +1052,10 @@ def to_frame(self, name=None):
10471052
-------
10481053
data_frame : DataFrame
10491054
"""
1050-
from pandas.core.frame import DataFrame
10511055
if name is None:
1052-
df = DataFrame(self)
1056+
df = self._constructor_expanddim(self)
10531057
else:
1054-
df = DataFrame({name: self})
1058+
df = self._constructor_expanddim({name: self})
10551059

10561060
return df
10571061

pandas/tests/test_frame.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import pandas.core.common as com
3232
import pandas.core.format as fmt
3333
import pandas.core.datetools as datetools
34-
from pandas import (DataFrame, Index, Series, notnull, isnull,
34+
from pandas import (DataFrame, Index, Series, Panel, notnull, isnull,
3535
MultiIndex, DatetimeIndex, Timestamp, date_range,
3636
read_csv, timedelta_range, Timedelta,
3737
option_context)
@@ -14057,6 +14057,26 @@ def test_assign_bad(self):
1405714057
with tm.assertRaises(KeyError):
1405814058
df.assign(C=df.A, D=lambda x: x['A'] + x['C'])
1405914059

14060+
def test_to_panel_expanddim(self):
14061+
14062+
class SubclassedFrame(DataFrame):
14063+
@property
14064+
def _constructor_expanddim(self):
14065+
return SubclassedPanel
14066+
14067+
class SubclassedPanel(Panel):
14068+
pass
14069+
14070+
index = MultiIndex.from_tuples([(0, 0), (0, 1), (0, 2)])
14071+
df = SubclassedFrame({'X':[1, 2, 3], 'Y': [4, 5, 6]}, index=index)
14072+
result = df.to_panel()
14073+
self.assertTrue(isinstance(result, SubclassedPanel))
14074+
expected = SubclassedPanel([[[1, 2, 3]], [[4, 5, 6]]],
14075+
items=['X', 'Y'], major_axis=[0],
14076+
minor_axis=[0, 1, 2])
14077+
tm.assert_panel_equal(result, expected)
14078+
14079+
1406014080
def skip_if_no_ne(engine='numexpr'):
1406114081
if engine == 'numexpr':
1406214082
try:

pandas/tests/test_series.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -6684,6 +6684,21 @@ def test_searchsorted_sorter(self):
66846684
e = np.array([0, 2])
66856685
tm.assert_array_equal(r, e)
66866686

6687+
def test_to_frame_expanddim(self):
6688+
6689+
class SubclassedSeries(Series):
6690+
@property
6691+
def _constructor_expanddim(self):
6692+
return SubclassedFrame
6693+
6694+
class SubclassedFrame(DataFrame):
6695+
pass
6696+
6697+
s = SubclassedSeries([1, 2, 3], name='X')
6698+
result = s.to_frame()
6699+
self.assertTrue(isinstance(result, SubclassedFrame))
6700+
expected = SubclassedFrame({'X': [1, 2, 3]})
6701+
assert_frame_equal(result, expected)
66876702

66886703

66896704
class TestSeriesNonUnique(tm.TestCase):
@@ -6837,7 +6852,7 @@ def test_repeat(self):
68376852
def test_unique_data_ownership(self):
68386853
# it works! #1807
68396854
Series(Series(["a", "c", "b"]).unique()).sort()
6840-
6855+
68416856
def test_datetime_timedelta_quantiles(self):
68426857
# covers #9694
68436858
self.assertTrue(pd.isnull(Series([],dtype='M8[ns]').quantile(.5)))

vb_suite/panel_methods.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
# shift
99

1010
setup = common_setup + """
11-
index = date_range(start="2000", freq="D", periods=1000)
12-
panel = Panel(np.random.randn(100, len(index), 1000))
11+
index = date_range(start="2000", freq="D", periods=100)
12+
panel = Panel(np.random.randn(100, len(index), 100))
1313
"""
1414

1515
panel_shift = Benchmark('panel.shift(1)', setup,

0 commit comments

Comments
 (0)