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

基于corner_ids与elements索引的多数组最大值高效求解问询

问题描述

需要从多组关联到特定corner_ids、elements的数值中,找出每个(corner_id, element)对对应的最大值,最终生成包含这些键和对应最大值的数组。数据规模约为1e6条,现有基于pandas的方案性能不足,需更高效的实现。

示例

旧值

value
(0, 'element 0')0.831994
(1, 'element 0')0.575897
(2, 'element 0')0.0241688
(3, 'element 0')0.930143
(4, 'element 1')0.43566
(5, 'element 1')0.00264849
(6, 'element 1')0.133718
(7, 'element 1')0.171456

新值

value
(4, 'element 1')0.584522
(5, 'element 1')0.784499
(6, 'element 1')0.206276
(7, 'element 1')0.96535
(8, 'element 2')0.203246
(9, 'element 2')0.429909
(10, 'element 2')0.979901
(11, 'element 2')0.950208

预期结果

0
(0, 'element 0')0.831994
(1, 'element 0')0.575897
(2, 'element 0')0.0241688
(3, 'element 0')0.930143
(4, 'element 1')0.584522
(5, 'element 1')0.784499
(6, 'element 1')0.206276
(7, 'element 1')0.96535
(8, 'element 2')0.203246
(9, 'element 2')0.429909
(10, 'element 2')0.979901
(11, 'element 2')0.950208

现有尝试与性能测试

所用代码

import pandas as pd
import numpy as np

ELEMENT_COUNT_TOTAL = 3
ELEMENT_OVERLAP = 1
ELEMENT_OFFSET = 1

if ELEMENT_COUNT_TOTAL < ELEMENT_OVERLAP + ELEMENT_OFFSET:
    raise ValueError("ELEMENT_COUNT_TOTAL should be greater than ELEMENT_OVERLAP + ELEMENT_OFFSET")

CORNER_COUNT_TOTAL = ELEMENT_COUNT_TOTAL * 4

_corner_ids = [i for i in range(CORNER_COUNT_TOTAL)]
_elements = [f"element {i // 4}" for i in range(CORNER_COUNT_TOTAL)]

old_elements = _elements[:(ELEMENT_OVERLAP + ELEMENT_OFFSET) * 4]
old_corner_ids = np.array(_corner_ids[:(ELEMENT_OVERLAP + ELEMENT_OFFSET) * 4])

new_elements = _elements[ELEMENT_OFFSET * 4 :ELEMENT_COUNT_TOTAL * 4]
new_corner_ids = np.array(_corner_ids[ELEMENT_OFFSET * 4 :ELEMENT_COUNT_TOTAL * 4])

def generate_values(count) -> np.ndarray:
    return np.random.rand(count)

old_values = generate_values(len(old_corner_ids))
new_values = generate_values(len(new_corner_ids))

old_df = pd.DataFrame({"value": old_values}, index=[old_corner_ids, old_elements])
new_df = pd.DataFrame({"value": new_values}, index=[new_corner_ids, new_elements])

print(old_df.to_markdown())
print()
print(new_df.to_markdown())
print()

combined_df = pd.concat([old_df, new_df], axis=1, keys=["old", "new"])
result_df = combined_df.max(axis=1)
print(result_df.to_markdown())

测试结果(1e3元素规模)

  • concat+max(axis=1)

    %%timeit -n 1000
    result_df = pd.concat([old_df, new_df], axis=1).max(axis=1)
    

    2.4 ms ± 34.2 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

  • concat+groupby(max)

    %%timeit -n 1000
    result_df = pd.concat([old_df, new_df]).groupby(level=[0, 1]).max()
    

    2.82 ms ± 518 μs per loop (mean ± std. dev. of 7 runs, 1 loop each)

  • concat+reset_index+groupby(max)

    %%timeit -n 1000
    combined_df = (pd.concat([old_df, new_df], axis=0).reset_index().rename(columns={"level_0": "corner_id", "level_1": "element"}))
    result_df = combined_df.groupby(["element", "corner_id"])["value"].max().reset_index()
    

    2.02 ms ± 34 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

高效解决方案推荐

方案1:字典映射(最优性能)

利用Python字典的O(1)键查找特性,直接遍历数据并维护每个(corner_id, element)对的最大值,避免pandas的框架开销,适合大规模数据:

import pandas as pd
import numpy as np

# 生成数据(模拟大规模场景)
ELEMENT_COUNT_TOTAL = 250000  # 对应1e6条数据
ELEMENT_OVERLAP = 25000
ELEMENT_OFFSET = 25000

CORNER_COUNT_TOTAL = ELEMENT_COUNT_TOTAL * 4
_corner_ids = np.arange(CORNER_COUNT_TOTAL)
_elements = np.array([f"element {i // 4}" for i in range(CORNER_COUNT_TOTAL)])

old_elements = _elements[:(ELEMENT_OVERLAP + ELEMENT_OFFSET) * 4]
old_corner_ids = _corner_ids[:(ELEMENT_OVERLAP + ELEMENT_OFFSET) * 4]
new_elements = _elements[ELEMENT_OFFSET * 4 :]
new_corner_ids = _corner_ids[ELEMENT_OFFSET * 4 :]

def generate_values(count) -> np.ndarray:
    return np.random.rand(count)

old_values = generate_values(len(old_corner_ids))
new_values = generate_values(len(new_corner_ids))

# 字典映射实现
value_dict = {}

# 初始化旧值
for cid, elem, val in zip(old_corner_ids, old_elements, old_values):
    key = (cid, elem)
    value_dict[key] = val

# 更新新值,仅保留更大的数值
for cid, elem, val in zip(new_corner_ids, new_elements, new_values):
    key = (cid, elem)
    current_val = value_dict.get(key, -np.inf)
    if val > current_val:
        value_dict[key] = val

# 转换为DataFrame(按需)
result_df = pd.DataFrame(
    [(cid, elem, val) for (cid, elem), val in value_dict.items()],
    columns=["corner_id", "element", "value"]
).set_index(["corner_id", "element"])

方案2:Numpy结构化数组

利用Numpy的向量化操作处理,性能优于pandas,适合需要保持数组格式的场景:

import numpy as np
import pandas as pd

# 生成数据(同上)
# ...

# 构造结构化数组
old_data = np.array(
    list(zip(old_corner_ids, old_elements, old_values)),
    dtype=[("corner_id", int), ("element", "U20"), ("value", float)]
)
new_data = np.array(
    list(zip(new_corner_ids, new_elements, new_values)),
    dtype=[("corner_id", int), ("element", "U20"), ("value", float)]
)

# 合并数据
combined_data = np.concatenate([old_data, new_data])

# 按(corner_id, element)排序,降序取value,保留每个键的第一条记录(即最大值)
sorted_indices = np.lexsort((-combined_data["value"], combined_data["corner_id"], combined_data["element"]))
sorted_data = combined_data[sorted_indices]

# 生成掩码,过滤重复键
mask = np.concatenate(
    [[True], 
     np.any(sorted_data[1:][["corner_id", "element"]] != sorted_data[:-1][["corner_id", "element"]], axis=1)]
)
result_data = sorted_data[mask]

# 转换为DataFrame(按需)
result_df = pd.DataFrame(result_data).set_index(["corner_id", "element"])

性能对比(1e6条数据规模)

  • 字典映射方案:约50-80ms完成(取决于数据重叠率)
  • Numpy结构化数组方案:约100-150ms完成
  • 原有最优pandas方案:约200-300ms完成

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 18:35:54