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

Pandas DataFrame数据补全与利润计算:正确性及性能优化问询

问题描述

数据结构

df1(主数据)

S. No.QuantityProductOptionPrice
12XF1100.0
21ZG1150.0

df2(补全数据)

注:df2存在重复列名Option,以下分析默认取第一列Option用于匹配

ProductOptionOptionBuy_price
XF1S180.0
ZG1H190.0

任务要求

利用df2的Buy_price为df1创建profit列,规则如下:

  • 若Price为0,则利润为0;
  • 存在Product+Option精确匹配时,使用对应Buy_price计算利润(利润=Price - Buy_price);
  • 无精确匹配时,使用该Option对应的Buy_price均值计算利润;
  • 仍无匹配时,取Price的70%作为利润。

现有实现代码

df1['profit'] = float('nan')
# 步骤1:Price<=0时利润设为0
df1.loc[df1['Price']<=0,'profit'] = 0.0
# 步骤2:按Product+Option分组处理
df2_gropued_P_O = df2.groupby(['Product','Option']).size().reset_index(name="Time")
for row in df2_gropued_P_O.iterrows():
  mean_sale = df2.loc[(df2['Product'] == row[1]['Product']) & (df2['Option'] == row[1]['Option']) & (df2['Price']>0)]['Price'].mean()
  df2.loc[(df2['Product'] == row[1]['Product']) & (df2['Option'] == row[1]['Option']) & (df2['Price']>0),['profit']] = mean_sale - row[1]['Buy_price']
# 步骤3:按Option分组处理
df2_gropued_O = fd2.groupby(['Option'])['Buy_price'].mean().reset_index()
option_intersect = set.intersection(set(df2_gropued_O['Option']),set(df1[df1['df2_gropued_O'].isna()]['Option']))
for i in options_intersect:
  df1.loc[(df1['profit'].isna()) & (df1['Option']==i),['profit']] = option_o_cost_gf.loc[option_o_cost_gf['Option']==i,['Buy_price']].values[0][0]
# 步骤4:填充剩余NaN值
for index in df2[df2['profit'].isna()].index.values.tolist():
  df2.loc[index,'profit'] = 0.70 * df2.loc[index,'Price']

当前疑问

  1. 上述实现方法是否正确?
  2. 步骤4处理约650K行数据时速度极慢,如何优化以提升效率?

解答

一、现有代码的错误点

现有代码存在多处逻辑错误和笔误,完全不符合任务要求:

  1. 对象混淆:任务目标是给df1添加profit列,但代码中大量操作df2的profit字段,完全偏离需求;
  2. 笔误问题:步骤3中fd2应为df2,option_o_cost_gf未定义,df1['df2_gropued_O'].isna()是错误的列引用;
  3. 逻辑偏差:
    • 步骤2中计算mean_sale - row[1]['Buy_price']不符合任务规则(任务要求用df1的Price减去对应Buy_price);
    • 步骤3中直接赋值Buy_price均值,未用df1的Price减去该均值;
    • 步骤4错误操作df2而非df1。

二、正确实现思路(矢量化操作,避免循环)

Pandas的核心优势是矢量化操作,完全不需要遍历行,以下是符合规则的高效实现:

1. 预处理df2(解决重复列名问题)

# 重命名df2的重复Option列
df2.columns = ['Product', 'Option', 'Option_2', 'Buy_price']
# 预先计算两种匹配所需的Buy_price映射
# ① Product+Option精确匹配的Buy_price(取第一个匹配值,若有重复可按需调整)
po_buy_price = df2.drop_duplicates(subset=['Product', 'Option']).set_index(['Product', 'Option'])['Buy_price']
# ② Option分组的Buy_price均值
option_buy_mean = df2.groupby('Option')['Buy_price'].mean()

2. 按规则计算df1的profit

import pandas as pd

# 初始化profit列为空值
df1['profit'] = pd.NA

# 规则1:Price<=0时利润为0
df1.loc[df1['Price'] <= 0, 'profit'] = 0.0

# 规则2:精确匹配Product+Option,计算profit=Price - Buy_price
match_mask = df1.set_index(['Product', 'Option']).index.isin(po_buy_price.index)
df1.loc[match_mask, 'profit'] = df1.loc[match_mask, 'Price'] - df1.loc[match_mask].apply(
    lambda x: po_buy_price.loc[(x['Product'], x['Option'])], axis=1
)

# 规则3:无精确匹配但Option存在于df2中,用Option的Buy_price均值计算
remaining_mask = df1['profit'].isna()
option_match_mask = remaining_mask & df1['Option'].isin(option_buy_mean.index)
df1.loc[option_match_mask, 'profit'] = df1.loc[option_match_mask, 'Price'] - df1.loc[option_match_mask, 'Option'].map(option_buy_mean)

# 规则4:剩余空值用Price的70%填充
df1['profit'] = df1['profit'].fillna(df1['Price'] * 0.7)

三、效率优化说明

你步骤4用循环遍历650K行是Pandas的大忌——行级循环的时间复杂度为O(n),且每一次loc操作都会触发数据查找,速度极慢。优化核心是用矢量化操作替代循环:

  1. 所有规则通过掩码(mask)、map、fillna等矢量化方法实现,时间复杂度大幅降低;
  2. 预先计算好映射表(如po_buy_price、option_buy_mean),避免重复计算;
  3. 利用Pandas的C底层优化处理大规模数据,650K行的处理时间会缩短到几秒甚至更短。

内容的提问来源于stack exchange,提问作者Mohit Kapoor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 09:25:24