如何在Python中高效实现多列字符串搜索并生成药物编码与频率列?
问题描述
有如下结构的pandas DataFrame,包含ID、多组药物名称(substance_1/2/3)及对应使用频率列(substance_1/2/3_freq):
import pandas as pd import numpy as np # 修正原始数据语法错误:substance_1中'Alcohol'与'Crack'需添加逗号 ID = ['001', '002', '003', '004', '005', '006', '007'] substance_1 = ['Alcohol', 'Meth', 'Alcohol', 'Marijuana', 'Alcohol', 'Crack', 'Heroin'] substance_1_freq = ['Daily', 'Weekly', 'Daily', 'Daily', 'Daily', 'Monthly', 'Weekly'] substance_2 = ['Meth', 'Alcohol', 'Marijuana', 'Heroin', 'Crack', 'Alcohol', 'Alcohol'] substance_2_freq = ['Weekly', 'Daily', 'Daily', 'Monthly', 'Monthly', 'Daily', 'Weekly'] substance_3 = ['Crack', None, None, 'Alcohol', 'Marijuana', None, None] substance_3_freq = ['Weekly', None, None, 'Daily', 'Daily', None, None] df1 = pd.DataFrame(list(zip(ID,substance_1, substance_1_freq, substance_2, substance_2_freq, substance_3, substance_3_freq)), columns = ['ID','substance_1', 'substance_1_freq', 'substance_2', 'substance_2_freq', 'substance_3', 'substance_3_freq'])
需求为:为每种药物创建独热编码列(存在标记为1,不存在标记为0),同时生成对应频率列(存在则填对应频率,否则为None),最终输出格式示例如下:
| ID | Alcohol | Alcohol_freq | Meth | Meth_freq | Crack | Crack_freq | ... |
|---|---|---|---|---|---|---|---|
| 001 | 1 | Daily | 1 | Weekly | 1 | Weekly | ... |
| 002 | 1 | Daily | 1 | Weekly | 0 | None | ... |
要求避免为每种药物、每个药物列编写np.where语句,寻求高效实现方式。
高效实现方法
步骤1:将宽表转为长格式
通过合并多组药物-频率列,把宽表转换为长格式,避免逐个处理每一组列:
# 定义药物列与对应频率列的配对 col_pairs = [('substance_1', 'substance_1_freq'), ('substance_2', 'substance_2_freq'), ('substance_3', 'substance_3_freq')] # 生成多组长格式数据并合并 dfs = [] for sub_col, freq_col in col_pairs: temp_df = df1[['ID', sub_col, freq_col]].rename(columns={sub_col: 'substance', freq_col: 'freq'}) dfs.append(temp_df) long_df = pd.concat(dfs, ignore_index=True) # 剔除无药物记录的行 long_df = long_df.dropna(subset=['substance'])
步骤2:创建标识列并透视数据
为药物添加存在标识,再通过透视分别提取药物存在状态和对应频率:
# 添加独热标识列 long_df['present'] = 1 # 透视得到药物存在状态(按ID分组,取最大值表示是否存在) substance_pivot = long_df.pivot_table( index='ID', columns='substance', values='present', fill_value=0, aggfunc='max' ) # 透视得到药物频率(按ID分组,取首个非空值;若需处理多频率可调整aggfunc) freq_pivot = long_df.pivot_table( index='ID', columns='substance', values='freq', aggfunc='first' ) # 重命名频率列,与药物列对应 freq_pivot.columns = [f"{col}_freq" for col in freq_pivot.columns]
步骤3:合并结果并整理列顺序
合并药物状态与频率列,按「药物列+对应频率列」的顺序排列:
# 合并两个透视表 result_df = pd.concat([substance_pivot, freq_pivot], axis=1) # 整理列顺序,将药物与对应频率列相邻放置 sorted_cols = [] for drug in substance_pivot.columns: sorted_cols.append(drug) sorted_cols.append(f"{drug}_freq") result_df = result_df[sorted_cols].reset_index() # 填充频率列缺失值为None result_df = result_df.fillna(value={col: None for col in freq_pivot.columns})
最终结果示例
运行代码后result_df的输出如下(部分列):
ID Alcohol Alcohol_freq Crack Crack_freq Heroin Heroin_freq Marijuana Marijuana_freq Meth Meth_freq 0 001 1 Daily 1 Weekly 0 None 0 None 1 Weekly 1 002 1 Daily 0 None 0 None 0 None 1 Weekly 2 003 1 Daily 0 None 0 None 1 Daily 0 None 3 004 1 Daily 0 None 1 Monthly 1 Daily 0 None 4 005 1 Daily 1 Monthly 0 None 1 Daily 0 None 5 006 1 Daily 1 Monthly 0 None 0 None 0 None 6 007 1 Weekly 0 None 1 Weekly 0 None 0 None
说明
- 该方法通过数据重塑避免了重复编写
np.where,新增药物组时只需修改col_pairs即可适配。 - 若同一ID下同一药物存在多个频率记录,可调整
pivot_table的aggfunc参数(如'last'取最后一条、lambda x: ', '.join(x)合并所有频率)满足需求。
内容的提问来源于stack exchange,提问作者brandooo23
相关产品推荐
相关产品推荐

