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

Pandas多列快速向量求和优化:避免DataFrame拷贝提速问询

优化大型DataFrame多列组合求和的性能瓶颈

问题分析

原代码的核心瓶颈在于[df[x] for x in combos]步骤:每次从DataFrame中选取列子集都会创建副本(尤其是当列不连续时),大量副本的生成和内存占用会拖慢整体速度。你提到的通过引用方式计算求和完全可行,关键是避免创建不必要的DataFrame子集副本,同时利用数学简化减少计算量。

优化方案

  • 利用组合互补性减少计算量:你的场景是从100列中选98列求和,这等价于每行的总列和 减去 被排除的2列的和。这个转换能把需要处理的列数从98降到2,计算量和内存占用都会大幅降低。
  • 直接操作底层numpy数组避免副本:Pandas DataFrame的底层存储是numpy数组,直接操作数组可以避免创建DataFrame子集副本,利用numpy的高效计算能力提升速度。

优化后的代码

import itertools as it
import numpy as np
import pandas as pd

def sum_combo_ranks(df, combos: list[str]) -> pd.DataFrame:
    """Returns the sum of the composite ranks for each combination of columns..

    Returns:
        DataFrame
            Each column contains a single system
            Each row is a date/ticker
            Each cell contains the aggregated rank for that stock on that date.
    """

    # Each DataFrame in lst is the sum of a single combination of selected columns;
    # This step is a bottleneck.
    # 2.9s
    lst = [df[x] for x in combos]

    # For each subset, add each row across.
    # 2.1s
    sums = [x.sum(axis=1)
            for x in lst]

    # Join the list of df's into a single DataFrame.
    # 0.3s
    new_df = pd.DataFrame(sums)

    return new_df

def sum_combo_ranks_optimized(df, combos: list[list[int]]) -> pd.DataFrame:
    # 获取底层numpy数组,避免创建DataFrame副本
    arr = df.to_numpy()
    # 先计算每行的总列和(只算一次)
    total_row_sum = arr.sum(axis=1)
    
    # 转换思路:每个98列组合对应唯一的2列排除组合
    # 生成每个组合对应的排除列(比遍历98列更高效)
    all_cols = set(range(df.shape[1]))
    excluded_combos = []
    for combo in combos:
        excluded = list(all_cols - set(combo))
        excluded_combos.append(excluded)
    
    # 计算每个排除组合的行和,再用总行和减去这个值,得到98列的和
    sums = []
    for excluded in excluded_combos:
        # 直接对numpy数组切片求和,无副本创建
        excluded_sum = arr[:, excluded].sum(axis=1)
        sums.append(total_row_sum - excluded_sum)
    
    # 转置后生成结果DataFrame(与原代码输出结构一致)
    new_df = pd.DataFrame(sums)
    return new_df

# 测试代码
df = pd.DataFrame(np.zeros([6000, 100]))       
combos = list(list(x) for x in it.combinations(range(100), 98))[:1000]  

# 对比原函数和优化函数的速度
import time
start = time.time()
result_old = sum_combo_ranks(df, combos)
print(f"原函数耗时: {time.time() - start:.2f}s")

start = time.time()
result_new = sum_combo_ranks_optimized(df, combos)
print(f"优化后耗时: {time.time() - start:.2f}s")

# 验证结果一致性
print(f"结果是否一致: {result_old.equals(result_new)}")

进一步矢量化优化(可选)

如果需要处理的组合数量很大,可以用numpy广播一次性计算所有结果,彻底消除Python循环的开销:

def sum_combo_ranks_vectorized(df, combos: list[list[int]]) -> pd.DataFrame:
    arr = df.to_numpy()
    total_row_sum = arr.sum(axis=1)
    
    # 把所有排除组合转换成numpy数组(shape=(N,2),N为组合数量)
    all_cols = set(range(df.shape[1]))
    excluded_arr = np.array([list(all_cols - set(combo)) for combo in combos])
    
    # 一次性计算所有排除组合的行和:利用广播实现批量运算
    excluded_sums = arr[:, excluded_arr].sum(axis=2)
    # 总行和减去每个排除组合的和,得到所有98列的和
    sums = total_row_sum[:, np.newaxis] - excluded_sums
    
    # 转置后生成结果(与原代码输出结构一致)
    new_df = pd.DataFrame(sums.T)
    return new_df

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:05:18