Skip to content

Commit 49efedf

Browse files
committed
make _reduce an always defined method on Base; raise TypeError if invoked and not overriden, test the same
1 parent fddf938 commit 49efedf

File tree

9 files changed

+87
-24
lines changed

9 files changed

+87
-24
lines changed

pandas/core/arrays/base.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,29 @@ def _ndarray_values(self):
679679
"""
680680
return np.array(self)
681681

682+
def _reduce(self, name, skipna=True, **kwargs):
683+
"""Return a scalar result of performing the op
684+
685+
Parameters
686+
----------
687+
name : str
688+
name of the function
689+
axis : int, default 0
690+
axis over which to apply, defined as 0 currently
691+
skipna : bool, default True
692+
if True, skip NaN values
693+
kwargs : dict
694+
695+
Returns
696+
-------
697+
scalar
698+
699+
Raises
700+
------
701+
TypeError : subclass does not define reductions
702+
"""
703+
raise TypeError
704+
682705

683706
class ExtensionOpsMixin(object):
684707
"""
@@ -717,25 +740,6 @@ def _add_comparison_ops(cls):
717740
cls.__le__ = cls._create_comparison_method(operator.le)
718741
cls.__ge__ = cls._create_comparison_method(operator.ge)
719742

720-
def _reduce(self, name, skipna=True, **kwargs):
721-
"""Return a scalar result of performing the op
722-
723-
Parameters
724-
----------
725-
name : str
726-
name of the function
727-
axis : int, default 0
728-
axis over which to apply, defined as 0 currently
729-
skipna : bool, default True
730-
if True, skip NaN values
731-
kwargs : dict
732-
733-
Returns
734-
-------
735-
scalar
736-
"""
737-
raise AbstractMethodError(self)
738-
739743

740744
class ExtensionScalarOpsMixin(ExtensionOpsMixin):
741745
"""A mixin for defining the arithmetic and logical operations on

pandas/tests/extension/arrow/test_bool.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ def test_from_dtype(self, data):
3939
pytest.skip("GH-22666")
4040

4141

42+
class TestReduce(base.BaseNoReduceTests):
43+
pass
44+
45+
4246
def test_is_bool_dtype(data):
4347
assert pd.api.types.is_bool_dtype(data)
4448
assert pd.core.common.is_bool_indexer(data)

pandas/tests/extension/base/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class TestMyDtype(BaseDtypeTests):
4848
from .interface import BaseInterfaceTests # noqa
4949
from .methods import BaseMethodsTests # noqa
5050
from .ops import BaseArithmeticOpsTests, BaseComparisonOpsTests, BaseOpsUtil # noqa
51-
from .reduce import BaseNumericReduceTests, BaseBooleanReduceTests # noqa
51+
from .reduce import BaseNoReduceTests, BaseNumericReduceTests, BaseBooleanReduceTests # noqa
5252
from .missing import BaseMissingTests # noqa
5353
from .reshaping import BaseReshapingTests # noqa
5454
from .setitem import BaseSetitemTests # noqa

pandas/tests/extension/base/reduce.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,26 @@ def check_reduce(self, s, op_name, skipna):
1616
tm.assert_almost_equal(result, expected)
1717

1818

19+
class BaseNoReduceTests(BaseReduceTests):
20+
""" we don't define any reductions """
21+
22+
@pytest.mark.parametrize('skipna', [True, False])
23+
def test_reduce_series_numeric(self, data, all_numeric_reductions, skipna):
24+
op_name = all_numeric_reductions
25+
s = pd.Series(data)
26+
27+
with pytest.raises(TypeError):
28+
getattr(s, op_name)(skipna=skipna)
29+
30+
@pytest.mark.parametrize('skipna', [True, False])
31+
def test_reduce_series_boolean(self, data, all_boolean_reductions, skipna):
32+
op_name = all_boolean_reductions
33+
s = pd.Series(data)
34+
35+
with pytest.raises(TypeError):
36+
getattr(s, op_name)(skipna=skipna)
37+
38+
1939
class BaseNumericReduceTests(BaseReduceTests):
2040

2141
@pytest.mark.parametrize('skipna', [True, False])

pandas/tests/extension/decimal/array.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,14 @@ def _concat_same_type(cls, to_concat):
136136

137137
def _reduce(self, name, axis=0, skipna=True, **kwargs):
138138

139-
# select the nan* ops
140139
if skipna:
141-
op = getattr(self.data, 'nan' + name)
140+
raise NotImplementedError("decimal does not support skipna=True")
142141

143-
# don't skip nans
144-
else:
142+
try:
145143
op = getattr(self.data, name)
144+
except AttributeError:
145+
raise NotImplementedError("decimal does not support "
146+
"the {} operation".format(name))
146147
return op(axis=axis)
147148

148149

pandas/tests/extension/decimal/test_decimal.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,28 @@ class TestMissing(BaseDecimal, base.BaseMissingTests):
131131
pass
132132

133133

134+
class Reduce:
135+
136+
def check_reduce(self, s, op_name, skipna):
137+
138+
if skipna or op_name in ['median']:
139+
with pytest.raises(NotImplementedError):
140+
getattr(s, op_name)(skipna=skipna)
141+
142+
else:
143+
result = getattr(s, op_name)(skipna=skipna)
144+
expected = getattr(np.asarray(s), op_name)()
145+
tm.assert_almost_equal(result, expected)
146+
147+
148+
class TestNumericReduce(Reduce, base.BaseNumericReduceTests):
149+
pass
150+
151+
152+
class TestBooleanReduce(Reduce, base.BaseBooleanReduceTests):
153+
pass
154+
155+
134156
class TestMethods(BaseDecimal, base.BaseMethodsTests):
135157
@pytest.mark.parametrize('dropna', [True, False])
136158
@pytest.mark.xfail(reason="value_counts not implemented yet.")

pandas/tests/extension/json/test_json.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ def test_fillna_frame(self):
160160
reason="Dictionary order unstable")
161161

162162

163+
class TestReduce(base.BaseNoReduceTests):
164+
pass
165+
166+
163167
class TestMethods(BaseJSON, base.BaseMethodsTests):
164168
@unhashable
165169
def test_value_counts(self, all_data, dropna):

pandas/tests/extension/test_categorical.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ def test_fillna_limit_backfill(self):
164164
pass
165165

166166

167+
class TestReduce(base.BaseNoReduceTests):
168+
pass
169+
170+
167171
class TestMethods(base.BaseMethodsTests):
168172
pass
169173

pandas/tests/extension/test_interval.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ class TestInterface(BaseInterval, base.BaseInterfaceTests):
9898
pass
9999

100100

101+
class TestReduce(base.BaseNoReduceTests):
102+
pass
103+
104+
101105
class TestMethods(BaseInterval, base.BaseMethodsTests):
102106

103107
@pytest.mark.skip(reason='addition is not defined for intervals')

0 commit comments

Comments
 (0)