Skip to content

Commit 3ce07cb

Browse files
authored
PERF: Series.any/all (pandas-dev#52381)
* PERF: Series.any/all * mypy fixup * different mypy i guess
1 parent 254720a commit 3ce07cb

File tree

3 files changed

+147
-50
lines changed

3 files changed

+147
-50
lines changed

pandas/core/generic.py

+47-9
Original file line numberDiff line numberDiff line change
@@ -11523,15 +11523,12 @@ def any(
1152311523
skipna: bool_t = True,
1152411524
**kwargs,
1152511525
):
11526-
return NDFrame.any(
11527-
self,
11528-
axis=axis,
11529-
bool_only=bool_only,
11530-
skipna=skipna,
11531-
**kwargs,
11526+
return self._logical_func(
11527+
"any", nanops.nanany, axis, bool_only, skipna, **kwargs
1153211528
)
1153311529

11534-
setattr(cls, "any", any)
11530+
if cls._typ == "dataframe":
11531+
setattr(cls, "any", any)
1153511532

1153611533
@doc(
1153711534
_bool_doc,
@@ -11550,9 +11547,12 @@ def all(
1155011547
skipna: bool_t = True,
1155111548
**kwargs,
1155211549
):
11553-
return NDFrame.all(self, axis, bool_only, skipna, **kwargs)
11550+
return self._logical_func(
11551+
"all", nanops.nanall, axis, bool_only, skipna, **kwargs
11552+
)
1155411553

11555-
setattr(cls, "all", all)
11554+
if cls._typ == "dataframe":
11555+
setattr(cls, "all", all)
1155611556

1155711557
@doc(
1155811558
_num_ddof_doc,
@@ -13022,3 +13022,41 @@ def _doc_params(cls):
1302213022
The required number of valid values to perform the operation. If fewer than
1302313023
``min_count`` non-NA values are present the result will be NA.
1302413024
"""
13025+
13026+
13027+
def make_doc(name: str, ndim: int) -> str:
13028+
"""
13029+
Generate the docstring for a Series/DataFrame reduction.
13030+
"""
13031+
if ndim == 1:
13032+
name1 = "scalar"
13033+
name2 = "Series"
13034+
axis_descr = "{index (0)}"
13035+
else:
13036+
name1 = "Series"
13037+
name2 = "DataFrame"
13038+
axis_descr = "{index (0), columns (1)}"
13039+
13040+
if name == "any":
13041+
desc = _any_desc
13042+
see_also = _any_see_also
13043+
examples = _any_examples
13044+
empty_value = False
13045+
elif name == "all":
13046+
desc = _all_desc
13047+
see_also = _all_see_also
13048+
examples = _all_examples
13049+
empty_value = True
13050+
else:
13051+
raise NotImplementedError
13052+
13053+
docstr = _bool_doc.format(
13054+
desc=desc,
13055+
name1=name1,
13056+
name2=name2,
13057+
axis_descr=axis_descr,
13058+
see_also=see_also,
13059+
examples=examples,
13060+
empty_value=empty_value,
13061+
)
13062+
return docstr

pandas/core/nanops.py

+12
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,12 @@ def nanany(
515515
>>> nanops.nanany(s.values)
516516
False
517517
"""
518+
if values.dtype.kind in "iub" and mask is None:
519+
# GH#26032 fastpath
520+
# error: Incompatible return value type (got "Union[bool_, ndarray]",
521+
# expected "bool")
522+
return values.any(axis) # type: ignore[return-value]
523+
518524
if needs_i8_conversion(values.dtype) and values.dtype.kind != "m":
519525
# GH#34479
520526
warnings.warn(
@@ -570,6 +576,12 @@ def nanall(
570576
>>> nanops.nanall(s.values)
571577
False
572578
"""
579+
if values.dtype.kind in "iub" and mask is None:
580+
# GH#26032 fastpath
581+
# error: Incompatible return value type (got "Union[bool_, ndarray]",
582+
# expected "bool")
583+
return values.all(axis) # type: ignore[return-value]
584+
573585
if needs_i8_conversion(values.dtype) and values.dtype.kind != "m":
574586
# GH#34479
575587
warnings.warn(

pandas/core/series.py

+88-41
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@
6868
is_integer,
6969
is_iterator,
7070
is_list_like,
71-
is_numeric_dtype,
7271
is_object_dtype,
7372
is_scalar,
7473
pandas_dtype,
@@ -101,7 +100,10 @@
101100
extract_array,
102101
sanitize_array,
103102
)
104-
from pandas.core.generic import NDFrame
103+
from pandas.core.generic import (
104+
NDFrame,
105+
make_doc,
106+
)
105107
from pandas.core.indexers import (
106108
disallow_ndim_indexing,
107109
unpack_1tuple,
@@ -4527,45 +4529,6 @@ def apply(
45274529
)
45284530
return SeriesApply(self, func, convert_dtype, args, kwargs).apply()
45294531

4530-
def _reduce(
4531-
self,
4532-
op,
4533-
name: str,
4534-
*,
4535-
axis: Axis = 0,
4536-
skipna: bool = True,
4537-
numeric_only: bool = False,
4538-
filter_type=None,
4539-
**kwds,
4540-
):
4541-
"""
4542-
Perform a reduction operation.
4543-
4544-
If we have an ndarray as a value, then simply perform the operation,
4545-
otherwise delegate to the object.
4546-
"""
4547-
delegate = self._values
4548-
4549-
if axis is not None:
4550-
self._get_axis_number(axis)
4551-
4552-
if isinstance(delegate, ExtensionArray):
4553-
# dispatch to ExtensionArray interface
4554-
return delegate._reduce(name, skipna=skipna, **kwds)
4555-
4556-
else:
4557-
# dispatch to numpy arrays
4558-
if numeric_only and not is_numeric_dtype(self.dtype):
4559-
kwd_name = "numeric_only"
4560-
if name in ["any", "all"]:
4561-
kwd_name = "bool_only"
4562-
# GH#47500 - change to TypeError to match other methods
4563-
raise TypeError(
4564-
f"Series.{name} does not allow {kwd_name}={numeric_only} "
4565-
"with non-numeric dtypes."
4566-
)
4567-
return op(delegate, skipna=skipna, **kwds)
4568-
45694532
def _reindex_indexer(
45704533
self,
45714534
new_index: Index | None,
@@ -6044,5 +6007,89 @@ def rdivmod(self, other, level=None, fill_value=None, axis: Axis = 0):
60446007
other, roperator.rdivmod, level=level, fill_value=fill_value, axis=axis
60456008
)
60466009

6010+
# ----------------------------------------------------------------------
6011+
# Reductions
6012+
6013+
def _reduce(
6014+
self,
6015+
op,
6016+
# error: Variable "pandas.core.series.Series.str" is not valid as a type
6017+
name: str, # type: ignore[valid-type]
6018+
*,
6019+
axis: Axis = 0,
6020+
skipna: bool = True,
6021+
numeric_only: bool = False,
6022+
filter_type=None,
6023+
**kwds,
6024+
):
6025+
"""
6026+
Perform a reduction operation.
6027+
6028+
If we have an ndarray as a value, then simply perform the operation,
6029+
otherwise delegate to the object.
6030+
"""
6031+
delegate = self._values
6032+
6033+
if axis is not None:
6034+
self._get_axis_number(axis)
6035+
6036+
if isinstance(delegate, ExtensionArray):
6037+
# dispatch to ExtensionArray interface
6038+
return delegate._reduce(name, skipna=skipna, **kwds)
6039+
6040+
else:
6041+
# dispatch to numpy arrays
6042+
if numeric_only and self.dtype.kind not in "iufcb":
6043+
# i.e. not is_numeric_dtype(self.dtype)
6044+
kwd_name = "numeric_only"
6045+
if name in ["any", "all"]:
6046+
kwd_name = "bool_only"
6047+
# GH#47500 - change to TypeError to match other methods
6048+
raise TypeError(
6049+
f"Series.{name} does not allow {kwd_name}={numeric_only} "
6050+
"with non-numeric dtypes."
6051+
)
6052+
return op(delegate, skipna=skipna, **kwds)
6053+
6054+
@Appender(make_doc("any", ndim=1))
6055+
# error: Signature of "any" incompatible with supertype "NDFrame"
6056+
def any( # type: ignore[override]
6057+
self,
6058+
*,
6059+
axis: Axis = 0,
6060+
bool_only=None,
6061+
skipna: bool = True,
6062+
**kwargs,
6063+
) -> bool:
6064+
nv.validate_logical_func((), kwargs, fname="any")
6065+
validate_bool_kwarg(skipna, "skipna", none_allowed=False)
6066+
return self._reduce(
6067+
nanops.nanany,
6068+
name="any",
6069+
axis=axis,
6070+
numeric_only=bool_only,
6071+
skipna=skipna,
6072+
filter_type="bool",
6073+
)
6074+
6075+
@Appender(make_doc("all", ndim=1))
6076+
def all(
6077+
self,
6078+
axis: Axis = 0,
6079+
bool_only=None,
6080+
skipna: bool = True,
6081+
**kwargs,
6082+
) -> bool:
6083+
nv.validate_logical_func((), kwargs, fname="all")
6084+
validate_bool_kwarg(skipna, "skipna", none_allowed=False)
6085+
return self._reduce(
6086+
nanops.nanall,
6087+
name="all",
6088+
axis=axis,
6089+
numeric_only=bool_only,
6090+
skipna=skipna,
6091+
filter_type="bool",
6092+
)
6093+
60476094

60486095
Series._add_numeric_operations()

0 commit comments

Comments
 (0)