如何为DataFrame按ID统计满足指定条件的观测行数
问题
我有一个包含多个变量的DataFrame,每个受试者(ID)对应多条观测行。需要为每个ID创建新变量,统计满足以下条件的观测行数:
- AVG_STOCK_DEN > 100
- DIM(观测天数)介于-14与-1之间
我尝试了两种实现方式,但未达到预期效果,代码如下:
1)
for group in dfy_pcp.groupby('ID'): if (dfy_pcp['DIM'] >= -14) & (dfy_pcp['DIM'] <= -1) & (dfy_pcp['avg_stock_den'] > 100): dfy_pcp['days_over_100_14'] = dfy_pcp['avg_stock_den'].count().any() else: dfy_pcp['days_over_100_14'] = 0
dfy_pcp['dfy_pcp_over_100_14'] = dfy_pcp[(dfy_pcp['DIM'] >= -14) & (dfy_pcp['DIM'] <= -1) & dfy_pcp['avg_stock_den'] > 100 ].groupby('ID')['avg_stock_den'].count().any()
解决方案
原代码问题说明
- 第一种循环写法:遍历分组时未针对单个ID处理,直接操作整个DataFrame导致所有行被统一赋值;且
count().any()逻辑错误,count()返回总数,any()会将非零值转为布尔值,无法得到符合条件的行数。 - 第二种写法:
count().any()会把分组统计结果转为单个布尔值(只要有分组有计数就返回True),赋值后整个列所有行都是同一个值,无法实现每个ID对应自身统计数的需求。
正确实现方法
方法1:使用transform(推荐)
# 标记符合条件的行 dfy_pcp['is_valid'] = (dfy_pcp['DIM'].between(-14, -1)) & (dfy_pcp['avg_stock_den'] > 100) # 按ID分组统计符合条件的行数,将结果映射到每一行 dfy_pcp['days_over_100_14'] = dfy_pcp.groupby('ID')['is_valid'].transform('sum')
说明:between(-14, -1)等价于DIM >= -14 且 DIM <= -1;sum()会将布尔值True(1)和False(0)求和,得到每个ID符合条件的行数;transform保证每个ID的所有行都能获取对应的统计值。
方法2:先统计再合并
# 筛选符合条件的行,按ID统计行数 count_df = dfy_pcp[(dfy_pcp['DIM'].between(-14, -1)) & (dfy_pcp['avg_stock_den'] > 100)] \ .groupby('ID')['avg_stock_den'].count().reset_index(name='days_over_100_14') # 合并到原DataFrame,无符合条件的ID填充0 dfy_pcp = dfy_pcp.merge(count_df, on='ID', how='left').fillna({'days_over_100_14': 0})
说明:先筛选出符合条件的行并分组统计数量,再通过merge将统计结果合并回原DataFrame;how='left'确保所有原ID都保留,无符合条件的ID用fillna填充0。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

