@@ -159,6 +159,7 @@ def __init__(self, kind: str, how: str, has_dropped_na: bool) -> None:
159159 "sum" ,
160160 "ohlc" ,
161161 "cumsum" ,
162+ "prod" ,
162163 }
163164
164165 _cython_arity = {"ohlc" : 4 } # OHLC
@@ -221,13 +222,13 @@ def _get_cython_vals(self, values: np.ndarray) -> np.ndarray:
221222 values = ensure_float64 (values )
222223
223224 elif values .dtype .kind in ["i" , "u" ]:
224- if how in ["var" , "prod" , " mean" ] or (
225+ if how in ["var" , "mean" ] or (
225226 self .kind == "transform" and self .has_dropped_na
226227 ):
227228 # result may still include NaN, so we have to cast
228229 values = ensure_float64 (values )
229230
230- elif how in ["sum" , "ohlc" , "cumsum" ]:
231+ elif how in ["sum" , "ohlc" , "prod" , " cumsum" ]:
231232 # Avoid overflow during group op
232233 if values .dtype .kind == "i" :
233234 values = ensure_int64 (values )
@@ -597,8 +598,16 @@ def _call_cython_op(
597598 min_count = min_count ,
598599 is_datetimelike = is_datetimelike ,
599600 )
600- elif self .how == "ohlc" :
601- func (result , counts , values , comp_ids , min_count , mask , result_mask )
601+ elif self .how in ["ohlc" , "prod" ]:
602+ func (
603+ result ,
604+ counts ,
605+ values ,
606+ comp_ids ,
607+ min_count = min_count ,
608+ mask = mask ,
609+ result_mask = result_mask ,
610+ )
602611 else :
603612 func (result , counts , values , comp_ids , min_count , ** kwargs )
604613 else :
@@ -631,8 +640,8 @@ def _call_cython_op(
631640 # need to have the result set to np.nan, which may require casting,
632641 # see GH#40767
633642 if is_integer_dtype (result .dtype ) and not is_datetimelike :
634- # Neutral value for sum is 0, so don't fill empty groups with nan
635- cutoff = max (0 if self .how == "sum" else 1 , min_count )
643+ # if the op keeps the int dtypes, we have to use 0
644+ cutoff = max (0 if self .how in [ "sum" , "prod" ] else 1 , min_count )
636645 empty_groups = counts < cutoff
637646 if empty_groups .any ():
638647 if result_mask is not None and self .uses_mask ():
0 commit comments