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
我正在使用以下 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