如何优化Pandas iterrows处理大型客户与交易DataFrame的性能
Pandas 批量计算优化方案
你的代码运行极慢的核心原因是用iterrows()逐行循环+每次全表筛选,对200万行的transactions重复执行3万次全表扫描,时间复杂度直接拉满。下面是几种高效的优化思路:
方法一:预关联+条件过滤+分组聚合(最推荐)
全程采用pandas矢量化操作,避免循环,效率提升最明显:
# 1. 关联两个表,仅保留需要的字段 merged_df = transactions.merge( customers[["customer_id", "signup_ts"]], on="customer_id", how="left" ) # 2. 计算交易与注册的天数差,筛选符合0<天数<183的记录 merged_df["days_diff"] = (merged_df["purchased_ts"] - merged_df["signup_ts"]).dt.days filtered_df = merged_df[(merged_df["days_diff"] > 0) & (merged_df["days_diff"] < 183)] # 3. 按用户分组求和,得到每个用户的总金额 total_amount_df = filtered_df.groupby("customer_id")["amount"].sum().reset_index() total_amount_df.rename(columns={"amount": "total_amount"}, inplace=True) # 4. 将结果合并回customers表,未满足条件的用户填充0 customers = customers.merge(total_amount_df, on="customer_id", how="left") customers["total_amount"] = customers["total_amount"].fillna(0).astype(float)
方法二:给transactions建索引加速筛选
如果不想全表关联,可通过索引减少每次筛选的时间:
# 给transactions的customer_id建立索引,加速用户交易定位 transactions.set_index("customer_id", inplace=True) # 初始化total_amount列 customers["total_amount"] = 0.0 # 用itertuples替代iterrows,行遍历效率更高 for row in customers.itertuples(): # 利用索引快速获取当前用户的所有交易 user_transactions = transactions.loc[row.customer_id, :] # 计算时间差并筛选有效交易 days_diff = (user_transactions["purchased_ts"] - row.signup_ts).dt.days valid_trans = user_transactions[(days_diff > 0) & (days_diff < 183)] # 直接通过索引赋值,避免全表查找 customers.at[row.Index, "total_amount"] = valid_trans["amount"].sum() # 恢复transactions的原索引(可选) transactions.reset_index(inplace=True)
方法三:apply+lambda(比原循环快,但不如前两种)
将逻辑封装为函数,用apply替代iterrows,减少循环开销:
def calculate_total(row): user_trans = transactions[transactions["customer_id"] == row["customer_id"]] days_diff = (user_trans["purchased_ts"] - row["signup_ts"]).dt.days return user_trans[(days_diff > 0) & (days_diff < 183)]["amount"].sum() customers["total_amount"] = customers.apply(calculate_total, axis=1).astype(float) customers["total_amount"] = customers["total_amount"].fillna(0)
原代码慢的核心原因
iterrows()本身效率低下,返回的Series每次访问字段都有额外开销- 每次循环都对200万行的
transactions做全表扫描,3万次循环累计600亿次数据操作 customers.loc[customers["customer_id"] == ...]又是一次全表查找,进一步拖慢速度
内容的提问来源于stack exchange,提问作者Harry Stuart
相关产品推荐
相关产品推荐

