讲师中心 微信公众号

工具推荐

视频效率加速
立刻MV
立刻MV

低成本歌曲 MV、视频生成

AniShort
AniShort

AI短剧协同制作平台

剧云
剧云

中文短剧剧本制作平台

updream

一键生成高质量推广成片

Pandas高效查找:基于条件获取最新历史索引的优化方法

夜伟大大_4377

夜伟大大_4377

发布时间:2025-11-06 12:34:01

|

638人浏览过

|

来源于php中文网

原创

Pandas高效查找:基于条件获取最新历史索引的优化方法

在pandas数据处理中,当需要根据当前行数据,高效查找满足特定条件(如`lower >= upper`)的最新历史记录索引时,传统的`apply`方法因其逐行迭代的特性而效率低下。本文将介绍如何利用python内置的`bisect`模块,结合二分查找策略,大幅提升此类操作的性能,实现对大型数据集的快速处理,避免内存溢出并显著缩短计算时间。

引言

在数据分析和处理中,尤其是在处理时间序列或具有顺序依赖性的数据时,我们经常会遇到需要“回溯”查找历史记录的场景。例如,给定一个包含lower和upper两列的DataFrame,并以日期作为索引,我们可能需要为每一行找到其之前所有行中,lower值大于或等于当前行upper值的最新记录的日期索引。这种操作的挑战在于其固有的顺序依赖性:每一行的结果都可能取决于其之前所有行的状态。对于小型数据集,简单的迭代方法尚可接受,但面对百万级甚至更大数据量时,性能问题会变得尤为突出。

低效的基线方法:DataFrame.apply()

最初解决此类问题的直观方法通常是使用DataFrame.apply()结合一个自定义函数。这个函数会为每一行执行以下操作:

  1. 筛选出当前行之前的所有记录。
  2. 在这些历史记录中,进一步筛选出满足特定条件(例如lower >= current_upper)的记录。
  3. 从满足条件的记录中,找出最新的日期索引。

以下是一个示例代码片段,展示了这种基于apply的基线方法:

import pandas as pd
import numpy as np

# 示例DataFrame
data = {'lower': [7, 1, 6, 1, 1, 1, 1, 11, 1, 1],
        'upper': [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}
df = pd.DataFrame(data=data)
df['DATE'] = pd.date_range('2020-01-01', periods=len(data['lower']), freq="min")
df.set_index('DATE', inplace=True)

def get_most_recent_index_baseline(row, dataframe):
    # 筛选当前行之前的记录
    # row.name - pd.Timedelta(minutes=1) 确保不包含当前行
    previous_rows = dataframe.loc[:row.name - pd.Timedelta(minutes=1)]  
    # 找出满足条件的记录
    recent_matches = previous_rows[previous_rows['lower'] >= row['upper']]
    # 返回最新记录的索引
    if not recent_matches.empty:
        return recent_matches.index.max()
    return pd.NaT # 如果没有匹配项,返回NaT

# 应用函数来创建新列
# df['prev_baseline'] = df.apply(lambda row: get_most_recent_index_baseline(row, df), axis=1) 
# print(df)

性能分析: 这种apply方法在逻辑上清晰,但效率非常低下。其主要性能瓶颈在于:

  1. 逐行迭代: apply本质上是一个Python级别的循环,无法充分利用Pandas底层C/Cython的优化。
  2. 重复数据切片: 在每次迭代中,dataframe.loc[:row.name - pd.Timedelta(minutes=1)]都会对DataFrame进行一次切片操作,这涉及到数据的复制或创建视图,开销巨大。
  3. 重复条件筛选: previous_rows[previous_rows['lower'] >= row['upper']]同样是重复的筛选操作。

对于拥有数万甚至数十万行的DataFrame,这种方法可能需要数分钟甚至数小时才能完成,严重影响开发和分析效率。

优化方案:利用bisect进行二分查找

尽管这类“依赖于过去状态”的问题难以实现完全的向量化,但我们可以通过结合高效的数据结构和算法来显著提升性能。Python标准库中的bisect模块提供了二分查找功能,可以帮助我们在有序列表中快速定位元素。

Python Hardener
Python Hardener

通过完整的安全审查、代码整洁重构、健壮的错误处理、正确的日志记录以及完整的文档字符串覆盖,强化 Python 脚本。

下载

核心思想: 为了避免重复的DataFrame切片和筛选,我们可以将相关数据提取为Python列表,并通过维护一个已排序的lower值集合和一个记录lower值最新出现日期的字典,利用二分查找来加速匹配过程。

算法步骤:

  1. 数据准备: 将DataFrame的lower、upper列和日期索引转换为Python列表,方便迭代。
  2. 维护有序的lower值集合: 创建一个已排序且去重后的lower值列表(uniq_lower)。这个列表将用于bisect_left进行二分查找。
  3. 维护last_seen字典: 创建一个字典last_seen,用于存储每个lower值最近一次出现的日期。
  4. 逐行迭代处理:
    • 对于当前行的lower值(l)、upper值(u)和日期(d):
    • 使用bisect_left(uniq_lower, u)在uniq_lower中找到第一个大于或等于当前u值的lower值的索引。
    • 从该索引开始,遍历uniq_lower中所有大于或等于u的lower值(lv)。
    • 对于每个lv,检查它是否在last_seen字典中存在。如果存在,这意味着该lower值在当前行之前出现过。比较last_seen[lv]与当前已找到的max_date,取两者中较新的日期。
    • 将找到的最新日期作为当前行的结果。
    • 更新last_seen: 将当前行的l值和d日期更新到last_seen字典中,以便后续行可以使用。

代码实现:

from bisect import bisect_left

def get_prev_optimized(lower_series, upper_series, date_index):
    # 将Pandas Series和Index转换为Python列表以提高迭代效率
    lower_list = lower_series.tolist()
    upper_list = upper_series.tolist()
    date_list = date_index.tolist()

    # 存储所有出现过的lower值,并保持排序
    # 使用set去重后排序,确保uniq_lower是有序的,以便进行二分查找
    uniq_lower = sorted(list(set(lower_list)))

    # 存储每个lower值最近一次出现的日期
    # 键为lower值,值为对应的日期
    last_seen = {}
    results = [] # 存储每行的结果

    # 遍历每一行数据
    for l, u, d in zip(lower_list, upper_list, date_list):
        max_date = pd.NaT # 初始化当前行的结果为NaT (Not a Time)

        # 使用bisect_left在uniq_lower中找到第一个大于或等于当前upper值的索引
        # 这大大减少了需要检查的lower值数量
        idx = bisect_left(uniq_lower, u)

        # 遍历所有可能满足条件(即 >= u)的lower值
        for lv in uniq_lower[idx:]:
            if lv in last_seen:
                # 如果该lower值之前出现过,比较日期,取最新值
                if pd.isna(max_date) or last_seen[lv] > max_date:
                    max_date = last_seen[lv]

        results.append(max_date) # 添加当前行的结果

        # 更新当前lower值最近一次出现的日期
        # 确保last_seen始终保存最新的日期
        last_seen[l] = d

    # 将结果列表转换为Pandas Series,并确保数据类型正确
    return pd.Series(results, index=date_index, dtype='datetime64[ns]')

# 假设df已定义并包含'lower', 'upper'列和日期索引
# df['prev_optimized'] = get_prev_optimized(df["lower"], df["upper"], df.index)
# print(df)

解释: 此优化方案通过以下方式提升了性能:

  • 避免DataFrame操作: 将核心逻辑转移到Python列表和字典操作,避免了Pandas DataFrame昂贵的切片和索引操作。
  • 二分查找: bisect_left在有序的uniq_lower列表中进行查找,时间复杂度为O(log N),而非O(N)。虽然之后仍需遍历符合条件的lower值,但通常这个子集会比整个历史数据小得多。
  • 字典快速查找: last_seen字典提供了O(1)的平均时间复杂度来获取特定lower值对应的最新日期。

性能对比

实际测试结果表明,bisect优化方法在处理大数据集时具有显著的性能优势。以下是在包含100,000行数据的DataFrame上进行测试的性能对比(基于原始问题中的数据):

方法 平均运行时间 备注
基线 (df.apply) 约 1分35秒 逐行迭代,效率低下
bisect 优化方法 约 1.76秒 性能最佳,利用二分查找
enumerate 迭代方法 约 1分13秒 仍为Python级别迭代,但避免了DataFrame切片
pyjanitor.conditional_join 内存分配错误 大数据量下可能导致内存溢出

从上述数据可以看出,bisect优化方法将计算时间从分钟级别缩短到了秒级别,提升了约50倍以上,使其成为处理此类问题的首选方案。

注意事项与最佳实践

  1. 数据类型一致性: 确保日期列为datetime类型,以便进行准确的日期比较和处理。pd.NaT是Pandas中表示缺失时间值的标准方式。
  2. 内存管理: 虽然pyjanitor等库在某些复杂连接场景下非常强大,但在处理海量数据且条件复杂时,它们可能因创建中间结果而消耗大量内存,甚至导致内存溢出。手动优化算法通常能更好地控制内存使用。
  3. 问题特性: 这种“依赖于过去状态”的问题本质上是顺序的,很难实现完全的并行或向量化。因此,优化迭代过程本身是解决性能问题的关键。
  4. bisect适用场景: bisect模块非常适合在已排序的序列中进行快速查找和插入操作。当你的问题可以转化为在一个有序集合中寻找满足条件的元素时,bisect是一个值得考虑的工具。
  5. 代码可读性: 尽管优化后的代码可能比简单的apply更复杂,但通过清晰的函数命名、注释和逻辑拆分,仍然可以保持其可读性和可维护性。

总结

在Pandas数据处理中,当需要根据当前行数据高效地回溯查找满足特定条件的最新历史记录时,传统的df.apply()方法因其逐行迭代和重复数据操作而效率低下。通过将问题转化为Python列表操作,并巧妙地利用bisect模块进行二分查找,我们可以大幅提升处理大型数据集的性能。这种优化策略不仅将计算时间从分钟级别缩短到秒级别,还为解决其他类似的复杂数据回溯问题提供了高效且内存友好的解决方案。在面对性能瓶颈时,深入理解数据结构和算法往往能带来意想不到的突破。

热门AI工具

更多
讯飞智文

一款面向学习和办公场景的AI文档创作工具,可辅助生成PPT与Word文档,提高资料整理和内容制作效率。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

函子Zion

一款支持AI搭建、可视化编辑和多端发布的全栈无代码开发平台。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

相关专题

更多
Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

360

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

292

2026.01.31

Python数据分析与Pandas高级实战
Python数据分析与Pandas高级实战

本专题围绕 Python 数据分析展开,系统讲解 Pandas 的高级用法,包括数据清洗、透视表、时间序列分析以及多表合并与分组操作。通过实战案例,帮助开发者掌握高效处理与分析数据的方法,提高数据处理效率与分析能力。

931

2026.04.13

数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3511

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

574

2025.10.31

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

582

2026.02.12

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2901

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

416

2025.12.22

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

0

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn