@@ -1559,6 +1559,88 @@ def _mode(self, dropna: bool = True):
1559
1559
npmodes = cast (np .ndarray , npmodes )
1560
1560
return self ._from_backing_data (npmodes )
1561
1561
1562
+ # ------------------------------------------------------------------
1563
+ # GroupBy Methods
1564
+
1565
+ def _groupby_op (
1566
+ self ,
1567
+ * ,
1568
+ how : str ,
1569
+ has_dropped_na : bool ,
1570
+ min_count : int ,
1571
+ ngroups : int ,
1572
+ ids : npt .NDArray [np .intp ],
1573
+ ** kwargs ,
1574
+ ):
1575
+ dtype = self .dtype
1576
+ if dtype .kind == "M" :
1577
+ # Adding/multiplying datetimes is not valid
1578
+ if how in ["sum" , "prod" , "cumsum" , "cumprod" , "var" , "skew" ]:
1579
+ raise TypeError (f"datetime64 type does not support { how } operations" )
1580
+ if how in ["any" , "all" ]:
1581
+ # GH#34479
1582
+ warnings .warn (
1583
+ f"'{ how } ' with datetime64 dtypes is deprecated and will raise in a "
1584
+ f"future version. Use (obj != pd.Timestamp(0)).{ how } () instead." ,
1585
+ FutureWarning ,
1586
+ stacklevel = find_stack_level (),
1587
+ )
1588
+
1589
+ elif isinstance (dtype , PeriodDtype ):
1590
+ # Adding/multiplying Periods is not valid
1591
+ if how in ["sum" , "prod" , "cumsum" , "cumprod" , "var" , "skew" ]:
1592
+ raise TypeError (f"Period type does not support { how } operations" )
1593
+ if how in ["any" , "all" ]:
1594
+ # GH#34479
1595
+ warnings .warn (
1596
+ f"'{ how } ' with PeriodDtype is deprecated and will raise in a "
1597
+ f"future version. Use (obj != pd.Period(0, freq)).{ how } () instead." ,
1598
+ FutureWarning ,
1599
+ stacklevel = find_stack_level (),
1600
+ )
1601
+ else :
1602
+ # timedeltas we can add but not multiply
1603
+ if how in ["prod" , "cumprod" , "skew" ]:
1604
+ raise TypeError (f"timedelta64 type does not support { how } operations" )
1605
+
1606
+ # All of the functions implemented here are ordinal, so we can
1607
+ # operate on the tz-naive equivalents
1608
+ npvalues = self ._ndarray .view ("M8[ns]" )
1609
+
1610
+ from pandas .core .groupby .ops import WrappedCythonOp
1611
+
1612
+ kind = WrappedCythonOp .get_kind_from_how (how )
1613
+ op = WrappedCythonOp (how = how , kind = kind , has_dropped_na = has_dropped_na )
1614
+
1615
+ res_values = op ._cython_op_ndim_compat (
1616
+ npvalues ,
1617
+ min_count = min_count ,
1618
+ ngroups = ngroups ,
1619
+ comp_ids = ids ,
1620
+ mask = None ,
1621
+ ** kwargs ,
1622
+ )
1623
+
1624
+ if op .how in op .cast_blocklist :
1625
+ # i.e. how in ["rank"], since other cast_blocklist methods don't go
1626
+ # through cython_operation
1627
+ return res_values
1628
+
1629
+ # We did a view to M8[ns] above, now we go the other direction
1630
+ assert res_values .dtype == "M8[ns]"
1631
+ if how in ["std" , "sem" ]:
1632
+ from pandas .core .arrays import TimedeltaArray
1633
+
1634
+ if isinstance (self .dtype , PeriodDtype ):
1635
+ raise TypeError ("'std' and 'sem' are not valid for PeriodDtype" )
1636
+ self = cast ("DatetimeArray | TimedeltaArray" , self )
1637
+ new_dtype = f"m8[{ self .unit } ]"
1638
+ res_values = res_values .view (new_dtype )
1639
+ return TimedeltaArray (res_values )
1640
+
1641
+ res_values = res_values .view (self ._ndarray .dtype )
1642
+ return self ._from_backing_data (res_values )
1643
+
1562
1644
1563
1645
class DatelikeOps (DatetimeLikeArrayMixin ):
1564
1646
"""
0 commit comments