在Pandas的数据处理场景中,经常需要根据某一列的取值,从其他多列中动态选取对应列的数据。比如我们有一份学生成绩表,其中包含学生姓名、选择的考试科目,以及语文、数学、英语三门科目的成绩,需要为每个学生提取出他所选科目的对应成绩。

示例数据准备
首先我们构造一份示例数据,方便后续演示各种方法的使用:
import pandas as pd
# 构造示例DataFrame
data = {
'name': ['张三', '李四', '王五', '赵六'],
'choose_subject': ['语文', '数学', '英语', '语文'],
'语文': [85, 90, 78, 88],
'数学': [92, 88, 95, 76],
'英语': [80, 85, 90, 82]
}
df = pd.DataFrame(data)
print(df)
运行上述代码后得到的DataFrame结构如下:
| name | choose_subject | 语文 | 数学 | 英语 |
|---|---|---|---|---|
| 张三 | 语文 | 85 | 92 | 80 |
| 李四 | 数学 | 90 | 88 | 85 |
| 王五 | 英语 | 78 | 95 | 90 |
| 赵六 | 语文 | 88 | 76 | 82 |
方法一:使用apply函数逐行选取
apply函数可以对DataFrame的每一行或者每一列应用自定义函数,我们可以利用这个特性,对每一行根据choose_subject列的值选取对应的成绩列。
# 定义逐行选取的函数
def get_score(row):
# row['choose_subject']是所选科目,作为列名提取对应值
return row[row['choose_subject']]
# 新增目标列存储选取的结果
df['target_score'] = df.apply(get_score, axis=1)
print(df[['name', 'choose_subject', 'target_score']])
这种方法的逻辑非常直观,适合数据量不大、对性能要求不高的场景。但是apply函数本质是逐行循环,当数据量达到几十万甚至上百万行时,运行速度会比较慢。
方法二:使用向量化操作提升性能
向量化操作是Pandas推荐的优化方式,我们可以通过构造布尔掩码的方式,避免逐行循环,大幅提升运行效率。
# 初始化目标列,默认值为NaN
df['target_score'] = pd.NA
# 遍历所有可选的科目,分别匹配choose_subject列
for subject in ['语文', '数学', '英语']:
# 找到choose_subject等于当前科目的行
mask = df['choose_subject'] == subject
# 将这些行的target_score设置为对应科目的成绩
df.loc[mask, 'target_score'] = df.loc[mask, subject]
print(df[['name', 'choose_subject', 'target_score']])
这种方法的运行速度远快于apply逐行处理,适合处理大规模数据。不过当可选列的数量非常多时,需要手动遍历所有列,代码会稍显冗长。
方法三:使用lookup方法(适用于旧版本Pandas)
在Pandas 1.2.0之前的版本中,提供了lookup方法,可以直接根据行标签和列标签组成的数组提取对应的值,非常适合这种动态列选取的场景。
# 注意:lookup方法在Pandas 1.2.0之后被弃用,旧版本可以使用 # 第一个参数是行索引,第二个参数是需要选取的列名数组 df['target_score'] = df.lookup(df.index, df['choose_subject']) print(df[['name', 'choose_subject', 'target_score']])
如果使用较新版本的Pandas,运行上述代码会报错,此时可以用melt结合merge的方式实现类似效果:
# 将宽表转换为长表,科目列转换为行
melt_df = df.melt(
id_vars=['name', 'choose_subject'],
value_vars=['语文', '数学', '英语'],
var_name='subject',
value_name='score'
)
# 匹配所选科目和科目列,提取对应成绩
result = pd.merge(
df[['name', 'choose_subject']],
melt_df,
left_on=['name', 'choose_subject'],
right_on=['name', 'subject']
)[['name', 'choose_subject', 'score']]
print(result)
不同方法的适用场景总结
- 数据量小、逻辑简单,优先选择apply函数,代码可读性高
- 数据量大、对性能要求高,优先选择向量化遍历的方法
- 使用旧版本Pandas且列数量不多,可以尝试lookup方法
- 列数量动态变化、需要灵活适配场景,可以选择melt加merge的方式
在实际使用中,可以根据数据规模和业务需求选择最合适的方法,达到效率和代码简洁度的平衡。