@@ -1165,6 +1165,27 @@ def _apply_filter(self, indices, dropna):
11651165OutputFrameOrSeries = TypeVar ("OutputFrameOrSeries" , bound = NDFrame )
11661166
11671167
1168+ def get_loc_notna (obj : "Series" , * , loc : int ):
1169+ """Find the value in position ``loc`` after filtering ``obj`` for nan values.
1170+
1171+ if ``obj`` is empty or has only nan values, np.nan er returned.
1172+
1173+ Examples
1174+ --------
1175+ >>> ser = pd.Series([np.nan, np.nan, 1, 2, np.nan])
1176+ >>> get_loc_notna(ser, loc=0) # get first non-na
1177+ 1
1178+ >>> get_loc_notna(ser, loc=-1) # get last non-na
1179+ 2
1180+ """
1181+ x = obj .to_numpy ()
1182+ x = x [notna (x )]
1183+
1184+ if len (x ) == 0 :
1185+ return np .nan
1186+ return x [loc ]
1187+
1188+
11681189class GroupBy (_GroupBy [FrameOrSeries ]):
11691190 """
11701191 Class for grouping and aggregating relational data.
@@ -1510,26 +1531,15 @@ def max(self, numeric_only: bool = False, min_count: int = -1):
15101531 numeric_only = numeric_only , min_count = min_count , alias = "max" , npfunc = np .max
15111532 )
15121533
1513- @staticmethod
1514- def _get_loc (x , axis : int = 0 , * , loc : int ):
1515- """Helper function for first/last item that isn't NA.
1516- """
1517-
1518- def get_loc_notna (x , loc : int ):
1519- x = x .to_numpy ()
1520- x = x [notna (x )]
1521- if len (x ) == 0 :
1522- return np .nan
1523- return x [loc ]
1524-
1525- if isinstance (x , DataFrame ):
1526- return x .apply (get_loc_notna , axis = axis , loc = loc )
1527- else :
1528- return get_loc_notna (x , loc = loc )
1529-
15301534 @doc (_groupby_agg_method_template , fname = "first" , no = False , mc = - 1 )
15311535 def first (self , numeric_only : bool = False , min_count : int = - 1 ):
1532- first_compat = partial (self ._get_loc , loc = 0 )
1536+ def first_compat (x , axis : int = 0 ):
1537+ """Helper function for first item that isn't NA.
1538+ """
1539+ if isinstance (x , DataFrame ):
1540+ return x .apply (get_loc_notna , axis = axis , loc = 0 )
1541+ else :
1542+ return get_loc_notna (x , loc = 0 )
15331543
15341544 return self ._agg_general (
15351545 numeric_only = numeric_only ,
@@ -1540,7 +1550,13 @@ def first(self, numeric_only: bool = False, min_count: int = -1):
15401550
15411551 @doc (_groupby_agg_method_template , fname = "last" , no = False , mc = - 1 )
15421552 def last (self , numeric_only : bool = False , min_count : int = - 1 ):
1543- last_compat = partial (self ._get_loc , loc = - 1 )
1553+ def last_compat (x , axis : int = 0 ):
1554+ """Helper function for last item that isn't NA.
1555+ """
1556+ if isinstance (x , DataFrame ):
1557+ return x .apply (get_loc_notna , axis = axis , loc = - 1 )
1558+ else :
1559+ return get_loc_notna (x , loc = - 1 )
15441560
15451561 return self ._agg_general (
15461562 numeric_only = numeric_only ,
0 commit comments