Pandas 使用第一个有效索引按组删除 nan

Pandas drop nan using first valid index by group

我正在使用以下 DataFrame:

         Date    Id    Amount
   0    201301    1      nan
   1    201302    1      nan
   2    201303    1      100
   3    201304    1      120
   4    201305    1      nan
   5    201306    1      120
   6    201302    2      nan
   7    201303    2      150
   8    201304    2      180

我正在尝试通过 Id 获取 Amount 的第一个有效索引。由于某些原因,这不起作用:

df.groupby('Id').Amount.first_valid_index()

我也在尝试这个:

df.groupby('Id').Amount.apply(lambda x: x.first_valid_index())

但我的数据集有 2000 万行以上,所以它花费的时间太长,对我来说不起作用。

有没有更快的方法来按组查找第一个索引?

我想要的输出是:

first_idx = [2,7]

甚至更好:

         Date    Id    Amount

   2    201303    1      100
   3    201304    1      120
   4    201305    1      nan
   5    201306    1      120
   7    201303    2      150
   8    201304    2      180

编辑:df.groupby('Id').Amount.apply(lambda x: x.first_valid_index())确实有效,但我觉得必须有一个更快的选择,问题似乎没有那么复杂。

选项 1: 仅获取第一个索引:

df[df.Amount.notna()].groupby('Id').Date.idxmin()
# 1.42 ms ± 14.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

输出:

Id
1    2
2    7
Name: Date, dtype: int64

选项 2: 要获取其他行,请在 notna()

上使用 cumsum
df[df['Amount'].notna().groupby(df['Id']).cumsum().gt(0)]
# 2.09 ms ± 220 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

选项3:你可以ffill()组内选择未填写的:

df[df.groupby('Id').Amount.ffill().notna()]
# 831 µs ± 14.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

输出:

     Date  Id  Amount
2  201303   1   100.0
3  201304   1   120.0
4  201305   1     NaN
5  201306   1   120.0
7  201303   2   150.0
8  201304   2   180.0

结论:方案3最快!


更新: 使用选项 3 过滤两端:

amt_group = df.groupby('Id').Amount
df[amt_group.bfill().notna() & amt_group.ffill().notna()]

使用 .notnull + .cumsum 创建掩码以获取组内第一个非空 Amount 之后的所有内容。然后切片。

m = df.Amount.notnull().groupby(df.Id).cumsum().ge(1)

df.loc[m]
     Date  Id  Amount
2  201303   1   100.0
3  201304   1   120.0
4  201305   1     NaN
5  201306   1   120.0
7  201303   2   150.0
8  201304   2   180.0