如何高效为Pandas DataFrame添加依赖其他列的多列
优化Pandas多新列生成的性能瓶颈
你现在遇到的问题我太熟悉了——多次调用df.apply(axis=1)生成新列,本质上是重复遍历整个DataFrame,当新列数量多的时候,这个开销会被直接放大。结合Python3.6的环境,给你两个针对性的优化方案:
方案1:单次遍历生成所有新列
把所有新列的计算逻辑合并到一个函数里,让这个函数返回包含所有新列值的pd.Series,这样只需要遍历一次DataFrame就能搞定所有新列,直接把时间复杂度从O(n*k)降到O(n)(n是行数,k是新列数)。
示例代码
import pandas as pd # 假设你的全局变量和已有列是这些 some_dictionary = {"A": 10, "B": 20} some_default_value = 0 def compute_all_new_cols(row): # 计算NewColumn1的逻辑(简化了原有的if-else) new_col1 = some_dictionary.get(row["SomeExistingColumn"], some_default_value) # 计算NewColumn2的示例逻辑(替换成你的实际业务逻辑) new_col2 = row["ExistingCol2"] + row["ExistingCol3"] * 0.5 # 返回Series,索引对应新列名,确保顺序和后续赋值一致 return pd.Series([new_col1, new_col2], index=["NewColumn1", "NewColumn2"]) # 一次性生成所有新列,仅遍历一次DataFrame df[["NewColumn1", "NewColumn2"]] = df.apply(compute_all_new_cols, axis=1)
方案2:向量化逻辑适配assign(性能最优)
你之前用assign失败是因为它的lambda接收的是整个DataFrame,不是单行,所以必须返回完整的列数据。大多数行级逻辑都可以改造成向量化操作(利用Pandas/Numpy的内置函数,底层是C实现,性能比Python循环快得多),这样就能正常用assign,还能拿到最好的性能。
示例:字典映射的向量化改造
你提供的compute_new_column1_value完全可以用pd.Series.map()实现向量化,比apply快几个数量级:
df = df.assign( NewColumn1=lambda df: df["SomeExistingColumn"].map(some_dictionary).fillna(some_default_value), NewColumn2=lambda df: df["ExistingCol2"] + df["ExistingCol3"] * 0.5 )
map()会批量处理整个列,fillna()自动处理不在字典里的默认值,完全不需要逐行遍历。
复杂条件的向量化思路
如果遇到多分支判断这类复杂逻辑,可以用这些工具:
numpy.where():处理二元条件pd.Series.where()/pd.Series.mask():基于条件替换值numpy.select():处理多分支条件
比如多分支逻辑的向量化实现:
import numpy as np # 定义多个条件和对应的结果 conditions = [ df["ExistingCol"] > 100, df["ExistingCol"] > 50, df["ExistingCol"] <= 50 ] choices = [ df["ExistingCol"] * 2, df["ExistingCol"] * 1.5, df["ExistingCol"] ] # 用assign生成新列 df = df.assign( NewColumn3=np.select(conditions, choices, default=0) )
方案选择建议
- 如果你的行级逻辑特别复杂(比如涉及多行依赖、复杂的Python控制流),优先用方案1,减少遍历次数;
- 只要逻辑能拆解成Pandas/Numpy的向量化操作,一定要选方案2,这是性能最优的选择;
- 绝对不要用
for idx, row in df.iterrows()这种手动循环,性能最差,完全没必要。
内容的提问来源于stack exchange,提问作者Dennis Soemers
相关产品推荐
相关产品推荐

