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

如何加速Pandas代码执行?百万级DataFrame查询优化求助

问题描述

我有一个包含1,917,866条记录的address_objects DataFrame,列与数据类型如下:

{'Id': dtype('int64'), 'ObjectId': dtype('int64'), 'ObjectGuid': dtype('O'), 'ChangeId': dtype('int64'), 'Name': dtype('O'), 'TypeName': dtype('O'), 'Level': dtype('int64'), 'OperationType': dtype('int64'), 'PrevId': dtype('int64'), 'NextId': dtype('int64'), 'UpdateDate': dtype('O'), 'StartDate': dtype('O'), 'EndDate': dtype('O'), 'IsActual': dtype('int64'), 'IsActive': dtype('int64'), 'changed_name': dtype('O')}

另有一个bests_paths字典,示例如下:

{'78545 78727 74726 86145 84657': 4, '78545 78727 81007 101612304': 4, '78545 78727 81007 101612305': 4, '78545 78727 81007 101612306': 3, '78545 78727 81007 101612307': 3}

字典的键是由上述DataFrame中ObjectId以空格分隔组成的字符串(对应某个邮寄地址),值为数字。

我的任务是根据字典的键从address_objects中获取对应地址,执行简单计算后将结果写入新DataFrame。但当前代码性能很差:处理815个字典键值对需约15秒,处理2000个需约40秒,无法满足需求。

当前代码如下:

for path, power in bests_paths.items():
            path = list(map(int, path.split()))
            address = address_objects.loc[address_objects['ObjectId'].isin(path)]
            sort = address.sort_values(by=['IsActual', 'IsActive'], ascending=[False, False])
            idx = sort['ObjectId'].drop_duplicates(keep='first').index 
            address = sort.loc[idx, :] 
            
            missing_components = a + b #some simple calculations

            address['Path'] = '.'.join(map(str, path))
            address['PowerPath'] = power - missing_components
            address['MissingComponent'] = missing_components
            addresses_administrative_hierarchy = pd.concat([addresses_administrative_hierarchy, address])
性能瓶颈分析
  • 循环内重复全表查询:每次循环都用isin(path)扫描190万行的DataFrame,isin是线性扫描操作,815次循环就会重复815次全表扫描,这是最大性能消耗点。
  • 重复排序与去重:每个循环都要对筛选出的小数据集做排序、去重,这类操作本身有计算开销,重复执行完全没必要。
  • 多次pd.concat拼接:每次循环都用concat生成新DataFrame,会触发内存复制,循环次数越多,内存开销和时间成本线性增长。
  • 逐次修改临时DataFrame:每次循环给临时address添加列,累积起来也会增加不必要的开销。
优化方案

1. 预建索引加速查询

给address_objects的ObjectId列建立索引,把单条记录查询速度从O(n)降到O(1):

address_objects = address_objects.set_index('ObjectId', drop=False)

2. 一次性提取所有需要的ObjectId

把bests_paths里所有ObjectId收集起来,一次性查询出所有相关记录,避免多次全表扫描:

# 收集所有ObjectId和对应的路径信息
all_object_ids = []
path_info_list = []
for path_str, power in bests_paths.items():
    path = list(map(int, path_str.split()))
    all_object_ids.extend(path)
    # 计算当前路径的缺失组件数(替换成你实际的计算逻辑)
    missing_components = a + b
    path_dot = '.'.join(map(str, path))
    # 给每个ObjectId绑定路径信息
    for obj_id in path:
        path_info_list.append({
            'ObjectId': obj_id,
            'Path': path_dot,
            'PowerPath': power - missing_components,
            'MissingComponent': missing_components
        })

# 转成DataFrame方便后续关联
path_info_df = pd.DataFrame(path_info_list)
# 一次性查询所有相关地址记录
filtered_addresses = address_objects.loc[all_object_ids].copy()

3. 批量排序与去重

对一次性提取的所有记录,只做一次排序和去重,替代循环内的重复操作:

# 按优先级排序,确保有效记录排在前面
filtered_addresses_sorted = filtered_addresses.sort_values(by=['IsActual', 'IsActive'], ascending=[False, False])
# 全局去重,保留每个ObjectId的第一条有效记录
filtered_addresses_unique = filtered_addresses_sorted.drop_duplicates(subset='ObjectId', keep='first')

4. 关联路径信息生成最终结果

用merge把去重后的地址记录和路径信息关联,一次性得到最终DataFrame:

addresses_administrative_hierarchy = filtered_addresses_unique.merge(path_info_df, on='ObjectId', how='left')

额外优化提示

  • 如果missing_components的计算依赖路径长度,可提前在收集路径时计算,比如missing_components = len(path) - 某个固定值(根据实际逻辑调整)。
  • 确保a和b的计算是批量操作,不要放在循环里重复计算。
优化效果说明

这种方式把原来O(kn)的循环操作变成O(n + km)(k为字典键数量,m为单路径平均ObjectId数),所有查询、排序、去重只做一次,能大幅压缩处理时间,2000个键值对的处理时间可降到几秒以内。

内容的提问来源于stack exchange,提问作者Александр Попов

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 07:55:37