Skip to content

Commit b7e7ebb

Browse files
committed
ERR: consistent error messages for unsupported reduction operations
1 parent b2a6d74 commit b7e7ebb

File tree

12 files changed

+109
-46
lines changed

12 files changed

+109
-46
lines changed

pandas/core/arrays/base.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,22 @@ def argmax(self, skipna: bool = True) -> int:
958958
raise ValueError("Encountered an NA value with skipna=False")
959959
return nargminmax(self, "argmax")
960960

961+
def _supports_reduction(self, op_name: str) -> bool:
962+
"""
963+
Return whether the reduction operation is supported for this array.
964+
965+
Parameters
966+
----------
967+
op_name : str
968+
Name of the reduction operation (e.g., 'sum', 'mean', 'min', etc.)
969+
970+
Returns
971+
-------
972+
bool
973+
True if supported, False otherwise.
974+
"""
975+
return False
976+
961977
def interpolate(
962978
self,
963979
*,
@@ -2186,8 +2202,7 @@ def _reduce(
21862202
meth = getattr(self, name, None)
21872203
if meth is None:
21882204
raise TypeError(
2189-
f"'{type(self).__name__}' with dtype {self.dtype} "
2190-
f"does not support operation '{name}'"
2205+
f"operation '{name}' is not supported for dtype '{self.dtype}'"
21912206
)
21922207
result = meth(skipna=skipna, **kwargs)
21932208
if keepdims:

pandas/core/arrays/categorical.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,9 +1933,7 @@ def check_for_ordered(self, op) -> None:
19331933
"""assert that we are ordered"""
19341934
if not self.ordered:
19351935
raise TypeError(
1936-
f"Categorical is not ordered for operation {op}\n"
1937-
"you can use .as_ordered() to change the "
1938-
"Categorical to an ordered one\n"
1936+
f"operation '{op}' is not supported for dtype '{self.dtype}'"
19391937
)
19401938

19411939
def argsort(
@@ -2419,9 +2417,17 @@ def _reverse_indexer(self) -> dict[Hashable, npt.NDArray[np.intp]]:
24192417
# ------------------------------------------------------------------
24202418
# Reductions
24212419

2420+
def _supports_reduction(self, op_name: str) -> bool:
2421+
return op_name in {"min", "max", "mode"}
2422+
24222423
def _reduce(
24232424
self, name: str, *, skipna: bool = True, keepdims: bool = False, **kwargs
24242425
):
2426+
if not self._supports_reduction(name):
2427+
raise TypeError(
2428+
f"operation '{name}' is not supported for dtype '{self.dtype}'"
2429+
)
2430+
24252431
result = super()._reduce(name, skipna=skipna, keepdims=keepdims, **kwargs)
24262432
if name in ["argmax", "argmin"]:
24272433
# don't wrap in Categorical!
@@ -2566,7 +2572,9 @@ def _accumulate(self, name: str, skipna: bool = True, **kwargs) -> Self:
25662572
elif name == "cummax":
25672573
func = np.maximum.accumulate
25682574
else:
2569-
raise TypeError(f"Accumulation {name} not supported for {type(self)}")
2575+
raise TypeError(
2576+
f"operation '{name}' is not supported for dtype '{self.dtype}'"
2577+
)
25702578
self.check_for_ordered(name)
25712579

25722580
codes = self.codes.copy()
@@ -2766,12 +2774,12 @@ def _groupby_op(
27662774

27672775
dtype = self.dtype
27682776
if how in ["sum", "prod", "cumsum", "cumprod", "skew", "kurt"]:
2769-
raise TypeError(f"{dtype} type does not support {how} operations")
2777+
raise TypeError(f"operation '{how}' is not supported for dtype '{dtype}'")
27702778
if how in ["min", "max", "rank", "idxmin", "idxmax"] and not dtype.ordered:
27712779
# raise TypeError instead of NotImplementedError to ensure we
27722780
# don't go down a group-by-group path, since in the empty-groups
27732781
# case that would fail to raise
2774-
raise TypeError(f"Cannot perform {how} with non-ordered Categorical")
2782+
raise TypeError(f"operation '{how}' is not supported for dtype '{dtype}'")
27752783
if how not in [
27762784
"rank",
27772785
"any",
@@ -2784,8 +2792,10 @@ def _groupby_op(
27842792
"idxmax",
27852793
]:
27862794
if kind == "transform":
2787-
raise TypeError(f"{dtype} type does not support {how} operations")
2788-
raise TypeError(f"{dtype} dtype does not support aggregation '{how}'")
2795+
raise TypeError(
2796+
f"operation '{how}' is not supported for dtype '{dtype}'"
2797+
)
2798+
raise TypeError(f"operation '{how}' is not supported for dtype '{dtype}'")
27892799

27902800
result_mask = None
27912801
mask = self.isna()

pandas/core/arrays/datetimelike.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,7 +1365,9 @@ def _addsub_object_array(self, other: npt.NDArray[np.object_], op) -> np.ndarray
13651365

13661366
def _accumulate(self, name: str, *, skipna: bool = True, **kwargs) -> Self:
13671367
if name not in {"cummin", "cummax"}:
1368-
raise TypeError(f"Accumulation {name} not supported for {type(self)}")
1368+
raise TypeError(
1369+
f"operation '{name}' is not supported for dtype '{self.dtype}'"
1370+
)
13691371

13701372
op = getattr(datetimelike_accumulations, name)
13711373
result = op(self.copy(), skipna=skipna, **kwargs)
@@ -1697,12 +1699,13 @@ def _groupby_op(
16971699
if dtype.kind == "M":
16981700
# Adding/multiplying datetimes is not valid
16991701
if how in ["sum", "prod", "cumsum", "cumprod", "var", "skew", "kurt"]:
1700-
raise TypeError(f"datetime64 type does not support operation '{how}'")
1702+
raise TypeError(
1703+
f"operation '{how}' is not supported for dtype '{self.dtype}'"
1704+
)
17011705
if how in ["any", "all"]:
17021706
# GH#34479
17031707
raise TypeError(
1704-
f"'{how}' with datetime64 dtypes is no longer supported. "
1705-
f"Use (obj != pd.Timestamp(0)).{how}() instead."
1708+
f"operation '{how}' is not supported for dtype '{self.dtype}'"
17061709
)
17071710

17081711
elif isinstance(dtype, PeriodDtype):

pandas/core/arrays/datetimes.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ class DatetimeArray(dtl.TimelikeOps, dtl.DatelikeOps):
222222
Length: 2, dtype: datetime64[s]
223223
"""
224224

225+
def _supports_reduction(self, op_name: str) -> bool:
226+
return op_name in {"min", "max", "mean", "median", "std"}
227+
225228
_typ = "datetimearray"
226229
_internal_fill_value = np.datetime64("NaT", "ns")
227230
_recognized_scalars = (datetime, np.datetime64)
@@ -2298,6 +2301,11 @@ def to_julian_date(self) -> npt.NDArray[np.float64]:
22982301
def _reduce(
22992302
self, name: str, *, skipna: bool = True, keepdims: bool = False, **kwargs
23002303
):
2304+
if not self._supports_reduction(name):
2305+
raise TypeError(
2306+
f"operation '{name}' is not supported for dtype '{self.dtype}'"
2307+
)
2308+
23012309
result = super()._reduce(name, skipna=skipna, keepdims=keepdims, **kwargs)
23022310
if keepdims and isinstance(result, np.ndarray):
23032311
if name == "std":

pandas/core/arrays/period.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,9 +1133,17 @@ def _check_timedeltalike_freq_compat(self, other):
11331133
# ------------------------------------------------------------------
11341134
# Reductions
11351135

1136+
def _supports_reduction(self, op_name: str) -> bool:
1137+
return op_name in {"min", "max"}
1138+
11361139
def _reduce(
11371140
self, name: str, *, skipna: bool = True, keepdims: bool = False, **kwargs
11381141
):
1142+
if not self._supports_reduction(name):
1143+
raise TypeError(
1144+
f"operation '{name}' is not supported for dtype '{self.dtype}'"
1145+
)
1146+
11391147
result = super()._reduce(name, skipna=skipna, keepdims=keepdims, **kwargs)
11401148
if keepdims and isinstance(result, np.ndarray):
11411149
return self._from_sequence(result, dtype=self.dtype)

pandas/core/arrays/string_.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,9 @@ def astype(self, dtype, copy: bool = True):
897897

898898
return super().astype(dtype, copy)
899899

900+
def _supports_reduction(self, op_name: str) -> bool:
901+
return op_name in {"min", "max"}
902+
900903
def _reduce(
901904
self,
902905
name: str,
@@ -906,6 +909,10 @@ def _reduce(
906909
axis: AxisInt | None = 0,
907910
**kwargs,
908911
):
912+
if not self._supports_reduction(name):
913+
raise TypeError(
914+
f"operation '{name}' is not supported for dtype '{self.dtype}'"
915+
)
909916
if self.dtype.na_value is np.nan and name in ["any", "all"]:
910917
if name == "any":
911918
return nanops.nanany(self._ndarray, skipna=skipna)

pandas/core/arrays/timedeltas.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ def __iter__(self) -> Iterator:
387387
# ----------------------------------------------------------------
388388
# Reductions
389389

390+
def _supports_reduction(self, op_name: str) -> bool:
391+
return op_name in {"min", "max", "sum", "mean", "median", "std", "var"}
392+
390393
def sum(
391394
self,
392395
*,
@@ -398,6 +401,11 @@ def sum(
398401
skipna: bool = True,
399402
min_count: int = 0,
400403
):
404+
if not self._supports_reduction("sum"):
405+
raise TypeError(
406+
f"operation 'sum' is not supported for dtype '{self.dtype}'"
407+
)
408+
401409
nv.validate_sum(
402410
(), {"dtype": dtype, "out": out, "keepdims": keepdims, "initial": initial}
403411
)
@@ -417,6 +425,11 @@ def std(
417425
keepdims: bool = False,
418426
skipna: bool = True,
419427
):
428+
if not self._supports_reduction("std"):
429+
raise TypeError(
430+
f"operation 'std' is not supported for dtype '{self.dtype}'"
431+
)
432+
420433
nv.validate_stat_ddof_func(
421434
(), {"dtype": dtype, "out": out, "keepdims": keepdims}, fname="std"
422435
)
@@ -436,7 +449,9 @@ def _accumulate(self, name: str, *, skipna: bool = True, **kwargs):
436449

437450
return type(self)._simple_new(result, freq=None, dtype=self.dtype)
438451
elif name == "cumprod":
439-
raise TypeError("cumprod not supported for Timedelta.")
452+
raise TypeError(
453+
f"operation 'cumprod' is not supported for dtype '{self.dtype}'"
454+
)
440455

441456
else:
442457
return super()._accumulate(name, skipna=skipna, **kwargs)

pandas/core/indexes/base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7419,6 +7419,10 @@ def min(self, axis: AxisInt | None = None, skipna: bool = True, *args, **kwargs)
74197419
return self._na_value
74207420

74217421
if not self._is_multi and not isinstance(self._values, np.ndarray):
7422+
if not self._values._supports_reduction("min"):
7423+
raise TypeError(
7424+
f"operation 'min' is not supported for dtype '{self.dtype}'"
7425+
)
74227426
return self._values._reduce(name="min", skipna=skipna)
74237427

74247428
return nanops.nanmin(self._values, skipna=skipna)
@@ -7483,6 +7487,10 @@ def max(self, axis: AxisInt | None = None, skipna: bool = True, *args, **kwargs)
74837487
return self._na_value
74847488

74857489
if not self._is_multi and not isinstance(self._values, np.ndarray):
7490+
if not self._values._supports_reduction("max"):
7491+
raise TypeError(
7492+
f"operation 'max' is not supported for dtype '{self.dtype}'"
7493+
)
74867494
return self._values._reduce(name="max", skipna=skipna)
74877495

74887496
return nanops.nanmax(self._values, skipna=skipna)

pandas/core/nanops.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ def nanany(
523523

524524
if values.dtype.kind == "M":
525525
# GH#34479
526-
raise TypeError("datetime64 type does not support operation 'any'")
526+
raise TypeError(f"operation 'any' is not supported for dtype '{values.dtype}'")
527527

528528
values, _ = _get_values(values, skipna, fill_value=False, mask=mask)
529529

@@ -579,7 +579,7 @@ def nanall(
579579

580580
if values.dtype.kind == "M":
581581
# GH#34479
582-
raise TypeError("datetime64 type does not support operation 'all'")
582+
raise TypeError(f"operation 'all' is not supported for dtype '{values.dtype}'")
583583

584584
values, _ = _get_values(values, skipna, fill_value=True, mask=mask)
585585

pandas/tests/groupby/test_groupby.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ def test_raises_on_nuisance(df, using_infer_string):
670670
df = df.loc[:, ["A", "C", "D"]]
671671
df["E"] = datetime.now()
672672
grouped = df.groupby("A")
673-
msg = "datetime64 type does not support operation 'sum'"
673+
msg = "operation 'sum' is not supported for dtype 'datetime64[ns]'"
674674
with pytest.raises(TypeError, match=msg):
675675
grouped.agg("sum")
676676
with pytest.raises(TypeError, match=msg):

0 commit comments

Comments
 (0)