Skip to content

Commit cb0499c

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

File tree

9 files changed

+87
-24
lines changed

9 files changed

+87
-24
lines changed

pandas/core/arrays/base.py

+23-19
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,29 @@ def _ndarray_values(self):
674674
"""
675675
return np.array(self)
676676

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

678701
class ExtensionOpsMixin(object):
679702
"""
@@ -712,25 +735,6 @@ def _add_comparison_ops(cls):
712735
cls.__le__ = cls._create_comparison_method(operator.le)
713736
cls.__ge__ = cls._create_comparison_method(operator.ge)
714737

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

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

pandas/tests/extension/arrow/test_bool.py

+4
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

+1-1
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

+20
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

+5-4
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,14 @@ def _concat_same_type(cls, to_concat):
139139

140140
def _reduce(self, name, axis=0, skipna=True, **kwargs):
141141

142-
# select the nan* ops
143142
if skipna:
144-
op = getattr(self.data, 'nan' + name)
143+
raise NotImplementedError("decimal does not support skipna=True")
145144

146-
# don't skip nans
147-
else:
145+
try:
148146
op = getattr(self.data, name)
147+
except AttributeError:
148+
raise NotImplementedError("decimal does not support "
149+
"the {} operation".format(name))
149150
return op(axis=axis)
150151

151152

pandas/tests/extension/decimal/test_decimal.py

+22
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,28 @@ class TestMissing(BaseDecimal, base.BaseMissingTests):
140140
pass
141141

142142

143+
class Reduce:
144+
145+
def check_reduce(self, s, op_name, skipna):
146+
147+
if skipna or op_name in ['median']:
148+
with pytest.raises(NotImplementedError):
149+
getattr(s, op_name)(skipna=skipna)
150+
151+
else:
152+
result = getattr(s, op_name)(skipna=skipna)
153+
expected = getattr(np.asarray(s), op_name)()
154+
tm.assert_almost_equal(result, expected)
155+
156+
157+
class TestNumericReduce(Reduce, base.BaseNumericReduceTests):
158+
pass
159+
160+
161+
class TestBooleanReduce(Reduce, base.BaseBooleanReduceTests):
162+
pass
163+
164+
143165
class TestMethods(BaseDecimal, base.BaseMethodsTests):
144166
@pytest.mark.parametrize('dropna', [True, False])
145167
@pytest.mark.xfail(reason="value_counts not implemented yet.")

pandas/tests/extension/json/test_json.py

+4
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ def test_fillna_frame(self):
172172
reason="Dictionary order unstable")
173173

174174

175+
class TestReduce(base.BaseNoReduceTests):
176+
pass
177+
178+
175179
class TestMethods(BaseJSON, base.BaseMethodsTests):
176180
@unhashable
177181
def test_value_counts(self, all_data, dropna):

pandas/tests/extension/test_categorical.py

+4
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ def test_fillna_limit_backfill(self):
172172
pass
173173

174174

175+
class TestReduce(base.BaseNoReduceTests):
176+
pass
177+
178+
175179
class TestMethods(base.BaseMethodsTests):
176180
pass
177181

pandas/tests/extension/test_interval.py

+4
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ class TestInterface(BaseInterval, base.BaseInterfaceTests):
107107
pass
108108

109109

110+
class TestReduce(base.BaseNoReduceTests):
111+
pass
112+
113+
110114
class TestMethods(BaseInterval, base.BaseMethodsTests):
111115

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

0 commit comments

Comments
 (0)