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

在Pandas DataFrame中按特定索引列组求平均并规避性能警告

问题描述

我有一个包含2150列的大型DataFrame,需要按指定列区间求平均生成新列,最终将列数缩减至约500列。列区间范围不固定,我用存储各列组起始索引的列表来实现逻辑。

当前代码能得到预期结果,但触发性能警告:

PerformanceWarning: DataFrame is highly fragmented. This is usually the result of calling frame.insert many times, which has poor performance. Consider joining all columns at once using pd.concat(axis=1) instead. To get a de-fragmented frame, use newframe = frame.copy()
df1[str(val)] = df[combine].mean(axis=1)

我不清楚如何在最后一次性用pd.concat完成所有列组的均值计算,同时作为编程新手,希望得到代码风格的改进建议,特别是循环终止部分的写法。

实际代码

import pandas as pd
df = pd.read_csv("some file location")

new_cols = list(range(350, 702, 3)) + list(range(707, 1398, 6)) + \
    list(range(1407, 2098, 10)) + list(range(2112, 2488, 15)) + [2501]
cols = list(map(int, list(df.columns)[1:]))
df1 = df.copy()
for i, val in enumerate(new_cols):
    if val == 2501:
        break
    combine = list(map(str, range(new_cols[i], new_cols[i+1])))
    print(combine)
    df1 = df1.drop(combine, axis=1, inplace=False)
    df1[str(val)] = df[combine].mean(axis=1)
    
df1.to_csv("data_reduced_precision.csv", index=False)

print("Finished")

极简示例(小数据量无性能警告)

df1 = pd.DataFrame({'1': [1, 2, 3, 4], 
                    '2': [5, 6, 7, 8], 
                    '3': [9, 10, 11, 12], 
                    '4': [13, 14, 15, 16],
                    '5': [17, 18, 19, 20], 
                    '6': [21, 22, 23, 24], 
                    '7': [25, 26, 27, 28]})
df2 = df1.copy()
# df2 最终应有列1、2、5,分别是df1列[1]、[2,3,4]、[5,6,7]的均值
new_cols = [1, 2, 5, 8]
for i, val in enumerate(new_cols):
    if val == 8:
        break
    # 所有列名都是字符串类型的整数
    combine = list(map(str, range(new_cols[i], new_cols[i+1])))
    df2 = df2.drop(combine, axis=1, inplace=False)
    df2[str(val)] = df1[combine].mean(axis=1)
print(df2)
# 输出:
#      1     2     5
# 0  1.0   9.0  21.0
# 1  2.0  10.0  22.0
# 2  3.0  11.0  23.0
# 3  4.0  12.0  24.0

解决方案

1. 用pd.concat一次性生成所有新列,避免碎片化

核心思路是先批量计算所有需要的均值列,把结果存在一个列表里,最后一次性和原始DataFrame中需要保留的列(比如第一列)拼接,彻底避免循环插入列导致的碎片化问题。

优化后的代码

import pandas as pd

df = pd.read_csv("some file location")

# 定义各列组的起始索引
new_cols = list(range(350, 702, 3)) + list(range(707, 1398, 6)) + \
    list(range(1407, 2098, 10)) + list(range(2112, 2488, 15)) + [2501]

# 存储所有生成的均值列
mean_columns = []

# 遍历列组区间:只需要到倒数第二个元素,因为每个元素要和下一个元素组成区间
for i in range(len(new_cols) - 1):
    start = new_cols[i]
    end = new_cols[i+1]
    # 生成要合并的列名(字符串类型)
    combine = list(map(str, range(start, end)))
    # 计算均值并命名为对应起始索引的字符串
    mean_col = df[combine].mean(axis=1).rename(str(start))
    mean_columns.append(mean_col)

# 拼接所有均值列,如果原始DataFrame第一列需要保留,就加上df.iloc[:, [0]]
df_reduced = pd.concat([df.iloc[:, [0]]] + mean_columns, axis=1)

# 保存结果
df_reduced.to_csv("data_reduced_precision.csv", index=False)

print("Finished")

2. 代码风格改进建议

循环终止部分优化

原来用if val == 2501: break的写法依赖特定值,灵活性差。优化后直接用range(len(new_cols)-1),因为我们只需要遍历到倒数第二个元素(每个元素和下一个元素组成区间),不管new_cols最后一个元素是什么,逻辑都能正常运行,可读性和鲁棒性更强。

其他细节优化

  • 去掉不必要的复制:原来提前复制整个df到df1再循环删列,既浪费内存又拖慢速度,优化后直接从原DataFrame计算均值,最后拼接即可。
  • 变量命名更清晰:把val改成start,让变量含义一目了然,提升代码可读性。
  • 删除无用代码:原来的cols = list(map(int, list(df.columns)[1:]))在代码中没有实际作用,可以直接删掉。

极简示例的优化版本

import pandas as pd

df1 = pd.DataFrame({'1': [1, 2, 3, 4], 
                    '2': [5, 6, 7, 8], 
                    '3': [9, 10, 11, 12], 
                    '4': [13, 14, 15, 16],
                    '5': [17, 18, 19, 20], 
                    '6': [21, 22, 23, 24], 
                    '7': [25, 26, 27, 28]})

new_cols = [1, 2, 5, 8]
mean_columns = []

for i in range(len(new_cols)-1):
    start = new_cols[i]
    end = new_cols[i+1]
    combine = list(map(str, range(start, end)))
    mean_col = df1[combine].mean(axis=1).rename(str(start))
    mean_columns.append(mean_col)

df2 = pd.concat(mean_columns, axis=1)
print(df2)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 14:35:40