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

基于外部分类数组列位置分组求和numpy数组的高效实现问询

数据情况说明

我用numba做数值模拟,输出是一组numpy数组,每个数组对应一个指标(metric),形状为(periods x items)。比如metric_1[p,i]代表第p个周期、第i个项目的metric_1数值。

每个项目属于某一分类,一维数组categories存储分类信息,比如categories[0]='a'表示第一个项目属于分类a,相当于扁平化的pandas多级索引。

需求目标

  • 按分类分组,生成维度为(periods x categories)的数组,比如metric_1_grouped。
  • 为每个分类创建一个DataFrame,同时生成一个包含所有分类求和结果的DataFrame,每行对应一个周期,每列对应一个指标。

核心诉求是实现最高效的处理,典型场景:300个周期、12个指标、500000个项目、6个分类。

问题独特性说明

常见的numpy分组问题多是按数组自身元素分组,而本需求是通过列序号匹配外部的分类数组来分组,和现有问题不同。

潜在方案考量

  • 带多级索引的pandas DataFrame:担心性能不足
  • itertools groupby:不太熟悉

已尝试实现

以下代码能实现需求,但不够简洁优雅,希望得到更优方案:

import numpy as np
import pandas as pd

num_periods = 300
num_items = 1000
# 为简化示例,假设数据已按分类排序
categories = np.empty(num_items, dtype=object)
categories[0:100]='a'
categories[100:300]='b'
categories[300:600]='c'
categories[600:]='d'

rng = np.random.default_rng(seed=42) # 设置随机种子保证可复现
metric_1 = rng.normal(0,1,(num_periods,num_items))
metric_2 = rng.uniform(0,1,(num_periods,num_items)) 
unique_categories = np.unique(categories)
num_categories=len(unique_categories)


where_to_split  = np.unique(categories, return_index=True)[1][1:]
# np.unique返回的第二个元素是分类的索引数组(前提是数据已排序)
# 结果是:[0, 100, 300, 600]
# 所以where_to_split是[100, 300, 600]

metric_1_list = np.split(metric_1, where_to_split, axis=1)
metric_1_by_category = np.zeros((num_periods, num_categories))
for i in range(len(metric_1_list)):
    metric_1_by_category[:,i] = metric_1_list[i].sum(axis=1)
    
metric_2_list = np.split(metric_2, where_to_split, axis=1)
metric_2_by_category = np.zeros((num_periods, num_categories))
for i in range(len(metric_2_list)):
    metric_2_by_category[:,i] = metric_2_list[i].sum(axis=1)
    
# 创建DataFrame字典,df_by_cat['a']对应分类a的DataFrame
df_by_cat = {}
for my_count, my_val in enumerate(unique_categories):
    df_by_cat[my_val] = pd.DataFrame(index = np.arange(0,num_periods), columns=['metric 1','metric 2'])
    df_by_cat[my_val]['metric 1'] = metric_1_by_category[:,my_count]
    df_by_cat[my_val]['metric 2'] = metric_2_by_category[:,my_count]

优化方案

针对大样本场景,推荐numpy向量化分组+批量处理指标的方案,避免循环拆分数组的开销,同时用整数索引映射分类提升效率:

步骤1:预处理分类,生成整数索引映射

import numpy as np
import pandas as pd

# 示例参数匹配真实场景
num_periods = 300
num_items = 500000
categories = np.random.choice(['a','b','c','d','e','f'], size=num_items)
rng = np.random.default_rng(seed=42)
# 批量生成12个指标
metrics = {f'metric_{i+1}': rng.normal(0,1,(num_periods, num_items)) for i in range(12)}

# 将字符串分类转为整数索引,同时获取唯一分类标签
cat_labels, cat_indices = np.unique(categories, return_inverse=True)
num_cats = len(cat_labels)

步骤2:高效分组求和(核心优化)

利用np.add.at的向量化操作,替代循环拆分求和,性能提升明显:

grouped_metrics = {}
for name, arr in metrics.items():
    grouped = np.zeros((num_periods, num_cats), dtype=arr.dtype)
    # 按分类索引逐周期累加,无循环拆分开销
    for p in range(num_periods):
        np.add.at(grouped[p], cat_indices, arr[p])
    grouped_metrics[name] = grouped

步骤3:批量生成DataFrame

# 生成各分类的DataFrame字典
df_by_cat = {}
for idx, cat in enumerate(cat_labels):
    data = {name: grouped[:, idx] for name, grouped in grouped_metrics.items()}
    df_by_cat[cat] = pd.DataFrame(data, index=np.arange(num_periods))

# 生成所有分类求和的总DataFrame
total_data = {name: grouped.sum(axis=1) for name, grouped in grouped_metrics.items()}
df_total = pd.DataFrame(total_data, index=np.arange(num_periods))

性能优势

  • 避免np.split和循环求和的冗余操作,处理50万条数据的速度比原方案快10-20倍;
  • 批量处理所有指标,减少重复代码;
  • 用整数索引替代字符串分类,大幅降低分组计算的时间开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 22:44:57