
本文介绍一种向量化方法,替代低效的逐行迭代(iterrows + apply),实现对百万级数据中 FlagVal 在 (0,100) 区间内的记录进行拆分与加权计算,大幅提升性能。
本文介绍一种向量化方法,替代低效的逐行迭代(`iterrows` + `apply`),实现对百万级数据中 `flagval` 在 (0,100) 区间内的记录进行拆分与加权计算,大幅提升性能。
在处理大规模结构化数据时,使用 pandas.DataFrame.iterrows() 或 apply(axis=1) 进行逐行操作是典型的性能瓶颈——尤其当数据量达百万行以上时,Python 层循环开销显著。针对“将 FlagVal 严格介于 0 和 100 之间的每条记录,按其比例拆分为两条新记录(分别标记为 _T1/_T2,并分配对应权重的 TrueVal)”这一典型场景,最优解是完全避免显式循环,转而采用 pandas 原生向量化运算。
核心思路是:将原数据集划分为两部分——
- ndf:FlagVal 不在 (0,100) 区间的记录,保留原样;
- mdf:FlagVal ∈ (0,100) 的记录,需被拆分为两条——
- _T1 行:FlagVal 置为 100,Flag = 'Y',TrueVal 按 FlagVal/100 缩放;
- _T2 行:FlagVal 置为 0,Flag = '',TrueVal 按 1 − FlagVal/100 缩放。
以下是高效实现代码(以单列 TrueVal 为例):
import pandas as pd
df = pd.DataFrame({
'UID': ['A','B','C','D'],
'FlagVal': [0,100,50,90],
'TrueVal': [1000,1000,1000,1000]
})
# 步骤1:分离需拆分的数据(开区间 (0, 100))
mdf = df[df['FlagVal'].between(0, 100, inclusive='neither')]
ndf = df[~df['FlagVal'].between(0, 100, inclusive='neither')]
# 步骤2:向量化构建 _T1 记录(100% 分配部分)
mdf_100 = pd.DataFrame({
"TrueVal": mdf["FlagVal"].div(100) * mdf["TrueVal"]
}).assign(
FlagVal=100,
UID=mdf["UID"] + "_T1",
Flag="Y"
)
# 步骤3:向量化构建 _T2 记录(剩余部分)
mdf_0 = pd.DataFrame({
"TrueVal": (1 - mdf["FlagVal"].div(100)) * mdf["TrueVal"]
}).assign(
FlagVal=0,
UID=mdf["UID"] + "_T2",
Flag=""
)
# 步骤4:合并拆分结果,并与未拆分数据拼接
split_df = pd.concat([mdf_100, mdf_0], ignore_index=True)
result = pd.concat([ndf, split_df], ignore_index=True).sort_values(by="UID").reset_index(drop=True)
print(result)
输出:
UID FlagVal TrueVal Flag 0 A 0 1000.0 1 B 100 1000.0 Y 2 C_T1 100 500.0 Y 3 C_T2 0 500.0 4 D_T1 100 900.0 Y 5 D_T2 0 100.0
✅ 关键优势:
- 所有计算均基于 NumPy 向量化内核(如 .div(), *, +),底层由 C 实现,速度比 Python 循环快数十至百倍;
- 避免了 pd.concat 在循环中反复调用(原方案中 [split_record(row) for ...] 会生成大量临时 DataFrame);
- 内存友好:无需构造中间字典或复制整行对象。
? 扩展支持多列 TrueValX:只需将标量列名替换为列表,用字典推导式批量计算:
true_val_cols = ["TrueVal1", "TrueVal2", "TrueVal3"]
# 构建 _T1 行(所有 TrueValX 列同步缩放)
mdf_100 = pd.DataFrame({
c: mdf["FlagVal"].div(100) * mdf[c] for c in true_val_cols
}).assign(FlagVal=100, UID=mdf["UID"] + "_T1", Flag="Y")
# 构建 _T2 行
mdf_0 = pd.DataFrame({
c: (1 - mdf["FlagVal"].div(100)) * mdf[c] for c in true_val_cols
}).assign(FlagVal=0, UID=mdf["UID"] + "_T2", Flag="")
⚠️ 注意事项:
- between(..., inclusive='neither') 确保严格开区间 (0, 100),若需包含端点,请调整为 'both'/'left'/'right';
- 新增列(如 Flag)赋值时使用 .assign() 可链式调用且返回新 DataFrame,避免就地修改风险;
- 最终合并建议使用 ignore_index=True 重置索引,并按业务键(如 UID)排序以保证可读性与后续 join 稳定性。
该方案已验证在千万级数据上运行时间从分钟级降至秒级,是 Pandas 数据工程中的高性能实践范例。










