如何在Python/Pandas中按字符串范围筛选ICD10编码并聚合数据?
解决Pandas中ICD10编码范围筛选及聚合问题
核心问题
直接用>=/<=对ICD10字符串做范围筛选会因为字符排序逻辑(比如C97之后会匹配CA00)导致错误,这里提供几种无需复杂解析器的简便方案,同时结合性别、年龄段完成聚合。
方案1:拆分编码前缀与数字段(通用首选)
ICD10编码规则是「字母前缀+数字编码」,拆分这两部分分别筛选,完美模拟SQL的BETWEEN效果:
- 提取前缀和数字部分
import pandas as pd # 假设你的数据表是df df['icd_prefix'] = df['ICD10'].str[0] # 提取第一个字符作为前缀 # 提取编码中的数字部分并转为整数(适配C00、E10这类编码格式) df['icd_num'] = df['ICD10'].str[1:].str.extract('(\d+)').astype(int).squeeze()
- 组合筛选条件(以C00-C97、20-29岁为例)
# ICD10范围筛选:C开头,数字00-97 icd_mask = (df['icd_prefix'] == 'C') & (df['icd_num'].between(0, 97)) # 年龄段筛选:20-29岁 age_mask = df['Age'].between(20, 29) # 性别筛选(假设POL字段值为'男'/'女') pol_mask = df['POL'].isin(['男', '女']) # 合并所有筛选条件 filtered_df = df[icd_mask & age_mask & pol_mask]
- 按要求聚合生成报告
# 按性别、年龄段分组,统计唯一患者数 report = filtered_df.groupby( ['POL', pd.cut(df['Age'], bins=[20, 30], labels=['20-29'])] )['PATIENTS_ID'].nunique().reset_index() # 重命名列名贴合报告需求 report.columns = ['性别', '年龄段', '患者人数']
方案2:正则表达式精准匹配
针对特定ICD10范围,直接写正则表达式匹配,无需拆分字段:
比如匹配C00-C97的正则:^C(0[0-9]|[1-8][0-9]|9[0-7])$,匹配E10-E14的正则:^E1[0-4]$
# 筛选C00-C97 + 20-29岁 + 所有性别 filtered_df = df[ df['ICD10'].str.match(r'^C(0[0-9]|[1-8][0-9]|9[0-7])$') & df['Age'].between(20, 29) & df['POL'].isin(['男', '女']) ] # 聚合逻辑同方案1 report = filtered_df.groupby(['POL', pd.cut(df['Age'], bins=[20,30], labels=['20-29'])])['PATIENTS_ID'].nunique().reset_index()
方案3:预定义分类变量(适合固定编码集合)
如果你的ICD10范围是固定的,可以把编码转为有序分类,直接用between:
# 预定义C00-C97的所有编码(示例,可根据实际生成完整列表) c_codes = [f'C{i:02d}' for i in range(0, 98)] # 将ICD10转为有序分类,仅包含目标编码 df['ICD10_cat'] = pd.Categorical(df['ICD10'], categories=c_codes, ordered=True) # 筛选C00-C97的编码 icd_mask = df['ICD10_cat'].between('C00', 'C97') # 后续筛选、聚合同方案1
多ICD10范围批量处理
如果需要同时处理多个ICD10范围生成报告,可以用字典批量循环:
# 定义需要处理的ICD10范围:键是报告中的范围名称,值是(前缀, 起始数字, 结束数字) icd_ranges = { '恶性肿瘤(C00-C97)': ('C', 0, 97), '糖尿病(E10-E14)': ('E', 10, 14) } final_report = [] for range_name, (prefix, start_num, end_num) in icd_ranges.items(): # 生成当前范围的筛选条件 mask = (df['ICD10'].str[0] == prefix) & (df['ICD10'].str[1:].str.extract('(\d+)').astype(int).squeeze().between(start_num, end_num)) filtered = df[mask & df['Age'].between(20,29) & df['POL'].isin(['男','女'])] # 聚合 agg_result = filtered.groupby(['POL', pd.cut(filtered['Age'], bins=[20,30], labels=['20-29'])])['PATIENTS_ID'].nunique().reset_index() agg_result['ICD10范围'] = range_name final_report.append(agg_result) # 合并所有结果 final_report = pd.concat(final_report, ignore_index=True) final_report.columns = ['性别', '年龄段', '患者人数', 'ICD10范围']
内容的提问来源于stack exchange,提问作者Imdelok
相关产品推荐
相关产品推荐

