如何用groupby和rolling统计患者阳性结果并保留原DataFrame结构?
解决方案
数据预处理
先确保日期格式和阳性结果数值化,同时排序保证窗口计算逻辑正确:
import pandas as pd # 转换日期列为datetime类型 df['ORDER_INST'] = pd.to_datetime(df['ORDER_INST']) # 将布尔型阳性结果转为1/0(True→1,False→0) df['ALL_RESULTS_NUM'] = df['ALL_RESULTS'].astype(int) # 按患者ID、检测类型、日期排序,确保滚动窗口按时间顺序计算 df = df.sort_values(["PAT_MRN_ID", "TEST_TYPE", "ORDER_INST"])
方法1:Merge方式保留所有字段并添加滚动求和列
单独计算滚动结果后合并回原表,规避索引不匹配问题:
# 计算90天内滚动阳性数,重置索引以便和原表合并 rolling_sum = df.groupby(["PAT_MRN_ID", "TEST_TYPE"]) \ .rolling('90d', min_periods=2, on='ORDER_INST')['ALL_RESULTS_NUM'] \ .sum() \ .reset_index() \ .rename(columns={'ALL_RESULTS_NUM': 'SUM'}) # 合并回原DataFrame,保留所有原始字段 df = df.merge(rolling_sum, on=["PAT_MRN_ID", "TEST_TYPE", "ORDER_INST"], how='left')
方法2:Apply+索引对齐直接生成新列
通过分组内设置日期为索引,执行滚动计算后对齐原表索引:
# 直接生成SUM列,全程保留原表所有字段 df['SUM'] = df.groupby(["PAT_MRN_ID", "TEST_TYPE"]) \ .apply(lambda group: group.set_index('ORDER_INST')['ALL_RESULTS_NUM'] .rolling('90d', min_periods=2).sum()) \ .reset_index(level=[0, 1], drop=True)
移除90天内阳性≥2次的患者
先定位需要移除的患者-检测类型组合,再过滤对应记录:
# 提取存在90天内阳性≥2次的患者-检测类型对 to_remove = df[df['SUM'] >= 2][["PAT_MRN_ID", "TEST_TYPE"]].drop_duplicates() # 过滤掉这些患者的所有记录 df_clean = df[~df.apply(lambda row: (row['PAT_MRN_ID'], row['TEST_TYPE']) in to_remove.values, axis=1)]
报错原因说明
ValueError: cannot handle a non-unique multi-index!
你在groupby时使用了as_index=False,导致rolling生成的结果索引与原DataFrame无法对齐,且形成的复合索引不唯一,无法直接赋值到新列。去掉as_index=False并通过reset_index/merge处理即可解决。ValueError: transform must return a scalar value for each group
transform要求每个分组返回与组内行数一致的序列或单个标量。你之前的写法中,rolling的on参数在分组子DataFrame中无法正确识别,且返回结构不符合transform的要求。改用apply配合索引重置的方式可以避免这个问题。
内容的提问来源于stack exchange,提问作者Harlemworld
相关产品推荐
相关产品推荐

