You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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),最终输出格式示例如下:

IDAlcoholAlcohol_freqMethMeth_freqCrackCrack_freq...
0011Daily1Weekly1Weekly...
0021Daily1Weekly0None...

要求避免为每种药物、每个药物列编写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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 08:12:53