Pandas中基于多条件和时间窗口匹配关联数据的策略

Pandas中基于多条件和时间窗口匹配关联数据的策略

本教程探讨如何在Pandas中高效地将一个DataFrame中的事件与另一个DataFrame中特定时间窗口(例如7天内)内的相关事件进行匹配和聚合。针对merge_asof的局限性,我们将介绍两种主要方法:利用pyjanitor库的conditional_join功能实现多条件高效连接,以及纯Pandas的merge结合筛选和聚合方案。文章将通过具体示例代码,详细阐述两种方法的实现步骤、适用场景及其性能考量。

问题场景与挑战

在数据分析中,我们经常需要关联两个数据集,其中一个数据集的记录(例如交易记录)需要匹配另一个数据集(例如浏览历史)中发生在特定时间窗口内的所有相关记录。考虑以下两个pandas dataframe:

trade DataFrame:包含交易日期、人员ID、商品代码和交易价值。

import pandas as pdimport janitor # 用于方案一trade = pd.DataFrame({'date': ['2019-08-31', '2019-09-01', '2019-09-04'],                      'person': [1, 1, 2],                      'code': [123, 123, 456],                      'value1': [1, 2, 3]})

view DataFrame:包含浏览日期、人员ID、商品代码和浏览价值。

view = pd.DataFrame({'date': ['2019-08-29', '2019-08-29', '2019-08-30', '2019-08-31', '2019-09-01', '2019-09-01', '2019-09-01', '2019-09-02', '2019-09-03'],                      'person': [1, 1, 1, 2, 1, 2, 2, 1, 2],                      'code': [123, 456, 123, 456, 123, 123, 456, 123, 456],                      'value': [1, 2, 3, 4, 5, 6, 7, 8, 9]})

我们的目标是为每一笔交易,找出其发生前7天内(含交易当天)所有匹配person和code的浏览记录,并将这些浏览记录的日期和价值聚合为列表。

直接使用Pandas的merge_asof函数在这种场景下存在局限性。merge_asof设计用于“最近匹配”,它会为左DataFrame的每一行找到右DataFrame中最近的匹配行,且通常只匹配一次。这不符合我们“获取所有在时间窗口内的匹配项”的需求。例如,merge_asof可能会将view中的[1, 3]分配给第一笔交易,但对于第二笔交易,它可能只会分配[5],而忽略了同样在时间窗口内的[1, 3]。

方案一:使用pyjanitor.conditional_join进行高效多条件连接

pyjanitor库提供了一个强大的conditional_join函数,它允许基于多个自定义条件进行连接,包括非等值条件(如时间范围)。这使得它成为解决此类问题的理想选择,尤其是在处理大型数据集时,其效率通常高于纯Pandas的通用合并再筛选方案。

实现步骤

日期列转换:确保所有涉及日期比较的列都是Pandas的datetime类型。创建辅助时间列:在trade DataFrame中,为每笔交易计算一个“开始日期”,即交易日期减去7天。这将作为时间窗口的下限。执行条件连接:使用conditional_join连接trade和view DataFrame。连接条件包括:view的日期在trade的start_date之后或等于。view的日期在trade的date之前或等于。person列相等。code列相等。数据聚合:连接完成后,结果DataFrame中每行将是trade记录与一个匹配的view记录的组合。我们需要根据原始trade的列进行分组,然后将匹配到的view_dates和view_values聚合成列表。格式化输出:将聚合后的日期列表转换回字符串格式。

代码示例

# 确保日期列为datetime类型trade['date'] = pd.to_datetime(trade['date'])view['date'] = pd.to_datetime(view['date'])# 方案一:使用janitor.conditional_joinout_janitor = (trade  .assign(start_date=lambda d: d['date'].sub(pd.DateOffset(days=7))) # 计算时间窗口的开始日期  .conditional_join(view.rename(columns={'date': 'view_dates', 'value': 'view_values'}), # 重命名view的列以避免冲突                    # 定义连接条件:(左列, 右列, 操作符)                    ('start_date', 'view_dates', '= start_date                    ('date', 'view_dates', '>='),       # view_dates <= trade_date                    ('person', 'person', '=='),         # person相等                    ('code', 'code', '=='),             # code相等                    right_columns=['view_dates', 'view_values'] # 只保留view中需要的列                   )  .drop(columns='start_date') # 移除辅助列  .assign(view_dates=lambda d: d['view_dates'].dt.strftime('%Y-%m-%d')) # 格式化日期为字符串  .groupby(list(trade.columns), as_index=False).agg(list) # 按原始trade列分组并聚合为列表)print("--- 使用 janitor.conditional_join 的结果 ---")print(out_janitor)

优势分析

conditional_join在内部实现了优化的算法来处理非等值连接,特别是涉及到范围查询时,其性能通常优于先进行笛卡尔积式合并再筛选的纯Pandas方法,尤其适用于大数据集。

方案二:纯Pandas的通用合并与筛选

虽然conditional_join更高效,但也可以纯粹使用Pandas的merge和loc进行筛选来实现相同的功能。这种方法在概念上更直接,但可能在性能上有所牺牲,因为它首先会生成一个更大的中间DataFrame。

实现步骤

日期列转换:同方案一,确保日期列为datetime类型。通用合并:使用merge函数基于person和code列进行内连接。这将生成trade和view中所有person和code匹配的组合。时间窗口筛选:使用loc根据时间条件(view_dates在trade_date前7天内)对合并后的结果进行筛选。数据聚合:同方案一,根据原始trade的列进行分组,然后将匹配到的view_dates和view_values聚合成列表。格式化输出:将聚合后的日期列表转换回字符串格式。

代码示例

# 确保日期列为datetime类型trade['date'] = pd.to_datetime(trade['date'])view['date'] = pd.to_datetime(view['date'])# 方案二:纯Pandas解决方案out_pandas = (trade .merge(view.rename(columns={'date': 'view_dates', 'value': 'view_values'}), # 重命名view的列        on=['person', 'code']) # 基于person和code进行合并 .loc[lambda d: d['date'].gt(d['view_dates']) & # 交易日期必须晚于浏览日期      d['date'].sub(pd.DateOffset(days=7)).le(d['view_dates']) # 浏览日期必须在交易日期前7天内     ] .assign(view_dates=lambda d: d['view_dates'].dt.strftime('%Y-%m-%d')) # 格式化日期为字符串 .groupby(list(trade.columns), as_index=False).agg(list) # 按原始trade列分组并聚合为列表)print("n--- 纯Pandas解决方案的结果 ---")print(out_pandas)

局限性分析

纯Pandas方案首先会生成一个包含所有person和code匹配组合的中间DataFrame。如果trade和view中存在大量相同person和code的记录,这个中间DataFrame可能会非常大,导致内存消耗增加和计算时间延长。随后进行的时间条件筛选会减少数据量,但前期合并的开销是不可避免的。

总结与最佳实践

两种方法都能达到预期的结果:

        date  person  code  value1                            view_dates view_values0 2019-08-31       1   123       1              [2019-08-29, 2019-08-30]      [1, 3]1 2019-09-01       1   123       2  [2019-08-29, 2019-08-30, 2019-09-01]   [1, 3, 5]2 2019-09-04       2   456       3  [2019-08-31, 2019-09-01, 2019-09-03]   [4, 7, 9]

对于小型数据集或对性能要求不高的场景:纯Pandas解决方案简单直观,易于理解和实现。对于大型数据集或对性能有严格要求的场景:强烈推荐使用pyjanitor.conditional_join。它通过更优化的算法处理非等值连接,能够显著提高效率并减少内存消耗。

注意事项:

日期类型一致性:在进行任何日期比较或计算之前,务必确保所有日期列都已正确转换为Pandas datetime类型。时间窗口定义:根据具体业务需求精确定义时间窗口(例如,pd.DateOffset(days=7)表示7天,可以根据需要调整为hours、minutes等)。列名冲突:在合并前,如果两个DataFrame中有相同名称但含义不同的列,请务必重命名以避免混淆。在示例中,我们重命名了view中的date和value列。安装pyjanitor:如果选择使用pyjanitor,请确保已通过pip install pyjanitor安装该库。

通过上述两种方法,我们可以灵活地在Pandas中处理复杂的基于多条件和时间窗口的数据关联任务,选择最适合当前项目需求和数据规模的方案。

以上就是Pandas中基于多条件和时间窗口匹配关联数据的策略的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1369641.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月14日 09:50:12
下一篇 2025年12月14日 09:50:27

相关推荐

  • Pandas中基于多条件和时间窗口关联数据的高效方法

    本教程探讨如何在Pandas中高效地关联两个数据集,特别是当关联条件涉及多个键和时间窗口时。我们将介绍两种方法:利用pyjanitor库的conditional_join实现高性能多条件连接,以及纯Pandas的解决方案。通过实例代码,详细展示如何将交易数据与特定时间范围内的浏览历史进行匹配,并将结…

    2025年12月14日
    000
  • Pandas中基于多条件和时间窗口匹配并聚合多条记录

    本教程探讨了如何在Pandas中,根据多个匹配条件和一个指定的时间窗口(例如7天内),从一个DataFrame中关联并聚合所有符合条件的记录到另一个DataFrame。文章详细介绍了两种实现方法:一种是利用pyjanitor库的conditional_join功能,该方法在处理复杂条件时更为高效;另…

    2025年12月14日
    000
  • PyMongo游标处理:避免InvalidOperation错误与安全访问数据

    本文旨在解决PyMongo游标操作中常见的pymongo.errors.InvalidOperation: cannot set options after executing query错误。我们将深入探讨PyMongo游标的特性,解释为何该错误会发生,并提供两种安全、高效地检查游标是否为空以及访…

    2025年12月14日
    000
  • SymPy 牛顿法 ValueError 深度解析与修正:符号变量与数值求值

    本文深入解析了在 SymPy 中使用牛顿法求解多项式根时遇到的 ValueError: First variable cannot be a number 错误。该错误主要源于符号变量与局部数值变量的混淆。教程详细阐述了如何正确区分 SymPy 符号和数值,恰当使用 subs 和 diff 方法,并…

    2025年12月14日
    000
  • 使用 Python 格式化输出列表和嵌套列表数据,使其以表格形式呈现

    本文介绍了如何使用 Python 格式化输出列表和嵌套列表数据,使其以清晰美观的表格形式呈现。我们将利用 zip() 函数将国家名称和奖牌计数对应起来,并结合字符串格式化方法,实现无需导入额外模块即可生成表格的功能。文章提供了详细的代码示例和解释,帮助读者理解和掌握表格输出的核心技巧。 在数据处理和…

    2025年12月14日
    000
  • Python:利用集合交集与列表推导式高效统计嵌套列表中的公共元素

    本文详细介绍了如何在Python中高效统计一个由元组组成的列表中,每个元组内部两个嵌套列表之间的公共元素数量。通过结合Python的集合(set)数据结构的交集操作(&)和列表推导式(list comprehension),可以简洁且高效地解决此类问题。文章不仅提供了核心代码示例,还解释了其…

    2025年12月14日
    000
  • 使用 Python 格式化输出列表和嵌套列表,生成表格形式的数据展示

    本文介绍如何使用 Python 语言,在不引入额外模块的前提下,将列表和嵌套列表中的数据以表格形式进行格式化输出。通过 zip() 函数将国家名称和奖牌计数进行关联,并结合字符串格式化方法,最终实现美观且易于阅读的表格数据展示。 在数据处理和展示中,将列表数据以表格形式输出是一种常见的需求。Pyth…

    2025年12月14日
    000
  • 使用 Python 格式化输出列表和嵌套列表,创建表格形式的数据展示

    本文旨在介绍如何使用 Python 编程语言,在不依赖任何外部模块的前提下,将列表和嵌套列表的数据以表格形式进行格式化输出。文章将详细讲解如何利用 zip() 函数以及字符串格式化技巧,实现美观且易于阅读的表格数据呈现,并提供完整的代码示例和解释。 在数据处理和展示中,将数据以表格形式呈现是一种常见…

    2025年12月14日
    000
  • Python教程:将列表数据高效格式化为表格输出

    本教程旨在指导读者如何利用Python内置功能,将分散在多个列表(包括嵌套列表)中的数据高效地关联起来,并以整洁、对齐的表格形式输出,而无需依赖任何外部模块。核心技巧包括使用zip()函数进行数据配对,以及利用字符串格式化功能实现精确的列对齐和标题展示。 1. 数据结构与挑战 在处理数据时,我们经常…

    2025年12月14日
    000
  • 使用 Python 格式化输出列表和嵌套列表为表格

    本文介绍了如何使用 Python 将列表和嵌套列表的数据格式化为表格形式输出,重点讲解了 zip() 函数的妙用,以及如何利用字符串的 format() 方法实现美观的对齐效果,无需导入额外的模块即可轻松实现。 使用 Python 格式化输出表格数据 在数据处理和展示中,将数据以表格形式呈现是一种常…

    2025年12月14日
    000
  • Pandas数据匹配与列扩展:基于多列字符串的动态数据集成

    本教程详细介绍了如何使用Python Pandas库,根据一个文件中特定列的字符串值,在另一个文件中匹配并添加多个新列。通过构建映射字典和利用map()函数,我们能够高效地将源文件的条形码信息,精准地关联到目标文件的多个结构列,从而实现复杂的数据集成与扩展,提升数据处理效率。 1. 问题场景描述 在…

    2025年12月14日
    000
  • PySpark中高效移除重复数据的两种策略

    本文详细阐述了在PySpark环境中处理重复数据的两种主要方法:针对原生PySpark SQL DataFrame的dropDuplicates()和针对PySpark Pandas DataFrame的drop_duplicates()。文章深入分析了这两种函数的用法、适用场景及关键区别,并通过代…

    2025年12月14日
    000
  • Python字典美化输出:实现键值对的整齐对齐

    本教程旨在解决Python字典在打印时键值对不对齐的问题。通过利用F-string的格式化能力,结合计算最长键的长度,我们可以实现字典输出的整齐对齐,使数据展示更加清晰和专业。文章将详细介绍如何计算最大键长并运用左对齐格式化输出,确保冒号和值在垂直方向上保持一致。 在python开发中,我们经常需要…

    2025年12月14日
    000
  • 使用 Python 格式化字符串对齐字典输出

    本文介绍了如何使用 Python 格式化字符串的方法,解决字典键值对输出时,由于键的长度不一致导致对齐混乱的问题。通过计算最长键的长度,并利用 f-string 的格式化功能,可以轻松实现美观、整齐的字典输出效果,提高代码的可读性。 在 Python 中,字典是一种非常常用的数据结构。当我们需要将字…

    2025年12月14日
    000
  • Python字典数据美观输出:实现键值对的对齐显示

    本教程旨在解决Python字典在打印输出时,由于键(key)长度不一导致显示不整齐的问题。通过利用Python的f-string格式化功能,结合动态计算最长键的长度,我们可以实现键值对的冒号对齐,从而生成结构清晰、易于阅读的表格化输出,提升数据展示的专业性和美观度。 在数据处理和展示中,我们经常需要…

    2025年12月14日
    000
  • Pandas DataFrame高效提取Top N值及其行列坐标

    本文详细介绍了如何利用Pandas的stack()和nlargest()方法,高效地从DataFrame中提取指定数量的最大值,并获取这些值对应的行和列坐标。通过专业示例代码,读者将学会如何快速定位数据中的关键点,优化数据分析流程。 在数据分析中,我们经常需要从大型pandas dataframe中…

    2025年12月14日
    000
  • Python字典键值对齐输出:利用f-string实现动态宽度格式化

    本文旨在解决Python字典在默认打印时键值对齐不整齐的问题。通过利用f-string格式化字符串和动态计算最长键的长度,我们可以实现键值对的完美对齐,从而显著提升输出的可读性和美观性。教程将详细介绍实现方法,并提供示例代码和注意事项。 字典输出对齐问题分析 在python中,当我们需要遍历并打印字…

    2025年12月14日
    000
  • Python字符串中处理撇号(单引号)的实用技巧

    在Python中打印含有撇号(单引号)的字符串时,常因引号冲突导致语法错误。本教程将介绍两种有效的解决方案:一是使用双引号 ” 来定义包含单引号 ‘ 的字符串,避免冲突;二是利用转义字符 对字符串内部的单引号进行转义。掌握这些方法能帮助开发者,特别是初学者,确保字符串内容的正…

    2025年12月14日
    000
  • 掌握USDA食品数据API分页获取完整营养信息教程

    本教程详细介绍了如何通过Python有效地从USDA食品数据API获取完整的营养事实数据。针对API默认返回结果受限(如50条)的问题,文章深入探讨了API分页机制,并提供了利用pageSize和pageNumber参数迭代获取所有数据项的解决方案。教程包含示例代码、错误处理和最佳实践,旨在帮助开发…

    2025年12月14日
    000
  • Python怎么使用Pandas库_Pandas数据处理入门指南

    Pandas数据清洗常用技巧包括处理缺失值、重复值、异常值、文本数据、日期时间及数据标准化。具体为:用dropna()或fillna()处理缺失值;drop_duplicates()去除重复数据;通过IQR或标准差识别异常值并合理处理;利用str方法清洗文本,如去空格、大小写转换;用to_datet…

    2025年12月14日
    000

发表回复

登录后才能评论
关注微信