我有一个包含500k+记录的数据框,我想按多列(字符串和日期的数据类型)分组,然后根据自定义条件在每个组中只选择少数记录。
基本上,我需要对记录进行分组(按first_roll_up
,date
,granular_timestamp
)以检查该组是否包含列top
的任何值,如果存在,则仅选择top
值的记录。同样,如果组中没有top
值的记录,则选择所有记录。
输入:
这里似乎根本不需要使用groupby()
,而是需要一些过滤和连接。
首先,我们将创建一个数据框,它可以帮助我们识别一行的top
值是否为T1
。
df_t1 = df[df['top'] == 'T1']
我们现在想要找到所有而不是包含T1
值的行,并通过first_roll_up
将其连接起来。这似乎只发生在sub
缺失时,这使得您的数据看起来是分层的(例如first_roll_up
—>sub
)。
df_want = pd.merge(df, df_t1, on='first_roll_up', how='outer', indicator=True, suffixes=('', '_drop'))
.query("_merge == 'left_only'")
.drop(columns='_merge')
这给了我们一个像这样的数据框架:
first_roll_up sub top ... date_drop granular_timestamp_drop values_drop
4 XYZ SUB_X_1 ... NaN NaN NaN
5 XYZ SUB_X_2 ... NaN NaN NaN
6 XYZ SUB_Y_1 ... NaN NaN NaN
由于我们连接的方式,我们有一组我们不关心的列。我们用_drop
作为它们的后缀。我们稍后会删除它们。
现在我们将T1
的值堆栈在我们刚刚创建的数据框上并删除我们不想要的列:
df_want = pd.concat([df_t1, df_want.loc[:, ~df_want.columns.str.endswith('_drop')]], ignore_index=True)
最终输出:
first_roll_up sub top date granular_timestamp values
0 ABC T1 2/10/2022 2/10/2022 10:00:00:000 .
1 XYZ SUB_X_1 2/12/2022 2/10/2022 11:00:00:000 .
2 XYZ SUB_X_2 2/12/2022 2/10/2022 11:00:00:000 .
3 XYZ SUB_Y_1 2/12/2022 2/10/2022 12:00:00:000 .
另一个可能的解决方案:
(df.groupby(['first_roll_up', 'date', 'granular_timestamp'], as_index=False)
.apply(lambda g: g if g.top.isna().all() else g.loc[g.top.notna()])
.reset_index(drop=True))
输出:
first_roll_up sub top date granular_timestamp values
0 ABC NaN T1 2/10/2022 2/10/2022 10:00:00:000 .
1 XYZ SUB_X_1 NaN 2/12/2022 2/10/2022 11:00:00:000 .
2 XYZ SUB_X_2 NaN 2/12/2022 2/10/2022 11:00:00:000 .
3 XYZ SUB_Y_1 NaN 2/12/2022 2/10/2022 12:00:00:000 .