@@ -993,7 +993,7 @@ def _flox_reduce(
993
993
dim : Dims ,
994
994
keep_attrs : bool | None = None ,
995
995
** kwargs : Any ,
996
- ):
996
+ ) -> T_Xarray :
997
997
"""Adaptor function that translates our groupby API to that of flox."""
998
998
import flox
999
999
from flox .xarray import xarray_reduce
@@ -1116,6 +1116,8 @@ def _flox_reduce(
1116
1116
# flox always assigns an index so we must drop it here if we don't need it.
1117
1117
to_drop .append (grouper .name )
1118
1118
continue
1119
+ # TODO: We can't simply use `self.encoded.coords` here because it corresponds to `unique_coord`,
1120
+ # NOT `full_index`. We would need to construct a new Coordinates object, that corresponds to `full_index`.
1119
1121
new_coords .append (
1120
1122
# Using IndexVariable here ensures we reconstruct PandasMultiIndex with
1121
1123
# all associated levels properly.
@@ -1361,7 +1363,12 @@ def where(self, cond, other=dtypes.NA) -> T_Xarray:
1361
1363
"""
1362
1364
return ops .where_method (self , cond , other )
1363
1365
1364
- def _first_or_last (self , op , skipna , keep_attrs ):
1366
+ def _first_or_last (
1367
+ self ,
1368
+ op : Literal ["first" | "last" ],
1369
+ skipna : bool | None ,
1370
+ keep_attrs : bool | None ,
1371
+ ):
1365
1372
if all (
1366
1373
isinstance (maybe_slice , slice )
1367
1374
and (maybe_slice .stop == maybe_slice .start + 1 )
@@ -1372,17 +1379,65 @@ def _first_or_last(self, op, skipna, keep_attrs):
1372
1379
return self ._obj
1373
1380
if keep_attrs is None :
1374
1381
keep_attrs = _get_keep_attrs (default = True )
1375
- return self .reduce (
1376
- op , dim = [self ._group_dim ], skipna = skipna , keep_attrs = keep_attrs
1377
- )
1382
+ if (
1383
+ module_available ("flox" , minversion = "0.10.0" )
1384
+ and OPTIONS ["use_flox" ]
1385
+ and contains_only_chunked_or_numpy (self ._obj )
1386
+ ):
1387
+ result = self ._flox_reduce (
1388
+ dim = None , func = op , skipna = skipna , keep_attrs = keep_attrs
1389
+ )
1390
+ else :
1391
+ result = self .reduce (
1392
+ getattr (duck_array_ops , op ),
1393
+ dim = [self ._group_dim ],
1394
+ skipna = skipna ,
1395
+ keep_attrs = keep_attrs ,
1396
+ )
1397
+ return result
1378
1398
1379
- def first (self , skipna : bool | None = None , keep_attrs : bool | None = None ):
1380
- """Return the first element of each group along the group dimension"""
1381
- return self ._first_or_last (duck_array_ops .first , skipna , keep_attrs )
1399
+ def first (
1400
+ self , skipna : bool | None = None , keep_attrs : bool | None = None
1401
+ ) -> T_Xarray :
1402
+ """
1403
+ Return the first element of each group along the group dimension
1382
1404
1383
- def last (self , skipna : bool | None = None , keep_attrs : bool | None = None ):
1384
- """Return the last element of each group along the group dimension"""
1385
- return self ._first_or_last (duck_array_ops .last , skipna , keep_attrs )
1405
+ Parameters
1406
+ ----------
1407
+ skipna : bool or None, optional
1408
+ If True, skip missing values (as marked by NaN). By default, only
1409
+ skips missing values for float dtypes; other dtypes either do not
1410
+ have a sentinel missing value (int) or ``skipna=True`` has not been
1411
+ implemented (object, datetime64 or timedelta64).
1412
+ keep_attrs : bool or None, optional
1413
+ If True, ``attrs`` will be copied from the original
1414
+ object to the new one. If False, the new object will be
1415
+ returned without attributes.
1416
+
1417
+ """
1418
+ return self ._first_or_last ("first" , skipna , keep_attrs )
1419
+
1420
+ def last (
1421
+ self , skipna : bool | None = None , keep_attrs : bool | None = None
1422
+ ) -> T_Xarray :
1423
+ """
1424
+ Return the last element of each group along the group dimension
1425
+
1426
+ Parameters
1427
+ ----------
1428
+ skipna : bool or None, optional
1429
+ If True, skip missing values (as marked by NaN). By default, only
1430
+ skips missing values for float dtypes; other dtypes either do not
1431
+ have a sentinel missing value (int) or ``skipna=True`` has not been
1432
+ implemented (object, datetime64 or timedelta64).
1433
+ keep_attrs : bool or None, optional
1434
+ If True, ``attrs`` will be copied from the original
1435
+ object to the new one. If False, the new object will be
1436
+ returned without attributes.
1437
+
1438
+
1439
+ """
1440
+ return self ._first_or_last ("last" , skipna , keep_attrs )
1386
1441
1387
1442
def assign_coords (self , coords = None , ** coords_kwargs ):
1388
1443
"""Assign coordinates by group.
0 commit comments