1
+ from typing import final
1
2
import warnings
2
3
3
4
import pytest
@@ -15,6 +16,9 @@ class BaseReduceTests(BaseExtensionTests):
15
16
"""
16
17
17
18
def check_reduce (self , s , op_name , skipna ):
19
+ # We perform the same operation on the np.float64 data and check
20
+ # that the results match. Override if you need to cast to something
21
+ # other than float64.
18
22
res_op = getattr (s , op_name )
19
23
exp_op = getattr (s .astype ("float64" ), op_name )
20
24
if op_name == "count" :
@@ -25,6 +29,43 @@ def check_reduce(self, s, op_name, skipna):
25
29
expected = exp_op (skipna = skipna )
26
30
tm .assert_almost_equal (result , expected )
27
31
32
+ def _get_expected_reduction_dtype (self , arr , op_name : str ):
33
+ # Find the expected dtype when the given reduction is done on a DataFrame
34
+ # column with this array. The default assumes float64-like behavior,
35
+ # i.e. retains the dtype.
36
+ return arr .dtype
37
+
38
+ # We anticipate that authors should not need to override check_reduce_frame,
39
+ # but should be able to do any necessary overriding in
40
+ # _get_expected_reduction_dtype. If you have a use case where this
41
+ # does not hold, please let us know at github.com/pandas-dev/pandas/issues.
42
+ @final
43
+ def check_reduce_frame (self , ser : pd .Series , op_name : str , skipna : bool ):
44
+ # Check that the 2D reduction done in a DataFrame reduction "looks like"
45
+ # a wrapped version of the 1D reduction done by Series.
46
+ arr = ser .array
47
+ df = pd .DataFrame ({"a" : arr })
48
+
49
+ kwargs = {"ddof" : 1 } if op_name in ["var" , "std" ] else {}
50
+
51
+ cmp_dtype = self ._get_expected_reduction_dtype (arr , op_name )
52
+
53
+ # The DataFrame method just calls arr._reduce with keepdims=True,
54
+ # so this first check is perfunctory.
55
+ result1 = arr ._reduce (op_name , skipna = skipna , keepdims = True , ** kwargs )
56
+ result2 = getattr (df , op_name )(skipna = skipna , ** kwargs ).array
57
+ tm .assert_extension_array_equal (result1 , result2 )
58
+
59
+ # Check that the 2D reduction looks like a wrapped version of the
60
+ # 1D reduction
61
+ if not skipna and ser .isna ().any ():
62
+ expected = pd .array ([pd .NA ], dtype = cmp_dtype )
63
+ else :
64
+ exp_value = getattr (ser .dropna (), op_name )()
65
+ expected = pd .array ([exp_value ], dtype = cmp_dtype )
66
+
67
+ tm .assert_extension_array_equal (result1 , expected )
68
+
28
69
29
70
class BaseNoReduceTests (BaseReduceTests ):
30
71
"""we don't define any reductions"""
@@ -71,9 +112,12 @@ def test_reduce_series(self, data, all_numeric_reductions, skipna):
71
112
def test_reduce_frame (self , data , all_numeric_reductions , skipna ):
72
113
op_name = all_numeric_reductions
73
114
s = pd .Series (data )
74
- if not is_numeric_dtype (s ):
115
+ if not is_numeric_dtype (s . dtype ):
75
116
pytest .skip ("not numeric dtype" )
76
117
118
+ if op_name in ["count" , "kurt" , "sem" ]:
119
+ pytest .skip (f"{ op_name } not an array method" )
120
+
77
121
self .check_reduce_frame (s , op_name , skipna )
78
122
79
123
0 commit comments