0

0

Pandas高级合并:基于列表(对象列)子集关系的DataFrame连接

心靈之曲

心靈之曲

发布时间:2025-11-05 09:38:11

|

246人浏览过

|

来源于php中文网

原创

Pandas高级合并:基于列表(对象列)子集关系的DataFrame连接

本文探讨了如何在pandas中实现基于对象列(包含列表或numpy数组)的复杂dataframe合并操作。当合并条件涉及一个dataframe的列表型列中的所有元素必须是另一个dataframe的列表型列的子集时,传统的`merge`方法不再适用。文章提供了一种迭代遍历、应用集合子集判断并拼接结果的解决方案,并详细展示了其实现代码和注意事项,尤其强调了在大数据集下的性能考量。

Pandas DataFrame对象列的复杂合并策略

在数据处理中,我们经常需要合并(merge)两个或多个Pandas DataFrame。通常情况下,合并操作基于共享的列值,例如使用pd.merge()函数。然而,当合并条件变得复杂,特别是涉及到列中存储的是列表(或NumPy数组)这类“对象类型”数据,并且合并的逻辑是基于一个列表是否为另一个列表的“子集”关系时,标准的合并方法就无法直接应用。

本教程将详细介绍如何处理这类特定场景:给定两个DataFrame,df1包含详细的日期时间信息和一组描述符列表(specifiers),df2包含更通用的描述符列表和对应的值。我们的目标是将df2的每一行合并到df1中,条件是df2行中的所有描述符必须作为子集存在于df1行的描述符列表中。

问题场景描述

假设我们有两个DataFrame,结构如下:

df1 (详细数据): 包含datetime、value和specifiers列。specifiers列是对象类型,每行是一个列表,例如 ['P1', 'WEEKDAY', 'TUESDAY'],表示日期时间的特定属性。

               datetime  value              specifiers
0   2021-06-01 00:00:00  11.30  [P1, WEEKDAY, TUESDAY]
1   2021-06-01 00:30:00   9.00  [P2, WEEKDAY, TUESDAY]
...

df2 (合并源数据): 包含specifiers和value列。specifiers列同样是对象类型,每行也是一个列表,但可能包含更少或更通用的描述符,例如 ['P1'] 或 ['P4', 'WEEKDAY']。

           specifiers    value
0                [P1]     0.43
1                [P2]     0.41
...
95995  [WEEKEND, P46]     1.67

我们的目标是:对于df2中的每一行,找到df1中所有其specifiers列包含df2行中所有specifiers的行,并将它们合并起来。例如,df2中specifiers为 ['P1'] 的行应该与df1中所有包含 'P1' 的specifiers列表的行合并。

解决方案:迭代、筛选与拼接

由于Pandas的内置merge函数不支持这种基于列表子集关系的复杂条件,我们需要采用一种迭代式的方法。核心思路是:

  1. 遍历df2的每一行。
  2. 对于df2的当前行,提取其specifiers列表。
  3. 使用这个specifiers列表作为条件,筛选df1中所有满足子集关系的行。
  4. 将df2的当前行(重复多次)与筛选出的df1行进行横向拼接。
  5. 将所有这些小块的拼接结果累积起来,形成最终的合并DataFrame。

详细实现步骤与代码示例

以下是实现上述逻辑的Python代码:

import pandas as pd
import numpy as np

# 1. 准备示例数据
# df1:模拟包含详细描述符的DataFrame
df1_data = {
    'datetime': pd.to_datetime(['2021-06-01 00:00:00', '2021-06-01 00:30:00',
                                '2021-06-01 01:00:00', '2021-06-01 01:30:00',
                                '2021-06-01 02:00:00', '2021-06-01 02:30:00']),
    'value': [11.30, 9.00, 10.40, 8.50, 9.70, 12.00],
    'specifiers': [['P1', 'WEEKDAY', 'TUESDAY'],
                   ['P2', 'WEEKDAY', 'TUESDAY'],
                   ['P3', 'WEEKDAY', 'TUESDAY'],
                   ['P4', 'WEEKDAY', 'TUESDAY'],
                   ['P5', 'WEEKDAY', 'TUESDAY'],
                   ['P6', 'WEEKEND', 'SATURDAY']] # 增加一个周末的示例
}
df1 = pd.DataFrame(df1_data)

# df2:模拟包含合并条件的DataFrame
df2_data = {
    'specifiers': [['P1'], ['P2'], ['P3'], ['P4', 'WEEKDAY'], ['P5', 'TUESDAY'], ['WEEKEND', 'P6']],
    'values_from_df2': [0.43, 0.51, 0.62, 0.73, 0.84, 0.99]
}
df2 = pd.DataFrame(df2_data)

print("--- df1 原始数据 ---")
print(df1)
print("\n--- df2 原始数据 ---")
print(df2)

# 2. 执行合并操作
merged_df = pd.DataFrame() # 初始化一个空的DataFrame来存储结果

# 遍历df2的每一行。itertuples()比iterrows()更高效,因为它返回命名元组。
for row_df2 in df2.itertuples(index=False):
    # row_df2.specifiers 是df2当前行的specifiers列表
    # 将其转换为集合以便进行高效的子集判断
    df2_specifiers_set = set(row_df2.specifiers)

    # 筛选df1中满足条件的行:
    # df1['specifiers'].apply(...) 对df1的specifiers列的每个元素应用一个函数
    # lambda x: df2_specifiers_set.issubset(set(x)) 检查df2的specifiers集合是否是df1当前行specifiers集合的子集
    matching_rows_df1 = df1[df1['specifiers'].apply(
        lambda x: df2_specifiers_set.issubset(set(x))
    )]

    # 如果找到了匹配的行
    if not matching_rows_df1.empty:
        # 创建一个与matching_rows_df1行数相同的DataFrame,每行都是df2的当前行数据
        # 这样做是为了在横向拼接时,df2的数据能与df1的匹配数据一一对应
        df2_row_repeated = pd.DataFrame([row_df2] * len(matching_rows_df1))

        # 横向拼接df2的当前行数据和df1的匹配行数据
        # reset_index(drop=True) 确保索引重置,避免拼接时因索引不匹配导致的问题
        combined_row_data = pd.concat([df2_row_repeated, matching_rows_df1.reset_index(drop=True)], axis=1)

        # 将当前拼接结果添加到最终的merged_df中
        merged_df = pd.concat([merged_df, combined_row_data], ignore_index=True)

print("\n--- 合并后的DataFrame ---")
print(merged_df)

代码输出示例

--- df1 原始数据 ---
             datetime  value             specifiers
0 2021-06-01 00:00:00  11.30  [P1, WEEKDAY, TUESDAY]
1 2021-06-01 00:30:00   9.00  [P2, WEEKDAY, TUESDAY]
2 2021-06-01 01:00:00  10.40  [P3, WEEKDAY, TUESDAY]
3 2021-06-01 01:30:00   8.50  [P4, WEEKDAY, TUESDAY]
4 2021-06-01 02:00:00   9.70  [P5, WEEKDAY, TUESDAY]
5 2021-06-01 02:30:00  12.00  [P6, WEEKEND, SATURDAY]

--- df2 原始数据 ---
        specifiers  values_from_df2
0             [P1]             0.43
1             [P2]             0.51
2             [P3]             0.62
3    [P4, WEEKDAY]             0.73
4    [P5, TUESDAY]             0.84
5  [WEEKEND, P6]             0.99

--- 合并后的DataFrame ---
        specifiers  values_from_df2            datetime  value             specifiers
0             [P1]             0.43 2021-06-01 00:00:00  11.30  [P1, WEEKDAY, TUESDAY]
1             [P2]             0.51 2021-06-01 00:30:00   9.00  [P2, WEEKDAY, TUESDAY]
2             [P3]             0.62 2021-06-01 01:00:00  10.40  [P3, WEEKDAY, TUESDAY]
3    [P4, WEEKDAY]             0.73 2021-06-01 01:30:00   8.50  [P4, WEEKDAY, TUESDAY]
4    [P5, TUESDAY]             0.84 2021-06-01 02:00:00   9.70  [P5, WEEKDAY, TUESDAY]
5  [WEEKEND, P6]             0.99 2021-06-01 02:30:00  12.00  [P6, WEEKEND, SATURDAY]

关键概念解析

  1. df2.itertuples(index=False):

    Insou AI
    Insou AI

    Insou AI 是一款强大的人工智能助手,旨在帮助你轻松创建引人入胜的内容和令人印象深刻的演示。

    下载
    • itertuples()是一种高效遍历DataFrame行的方法,它将每行转换为一个命名元组(namedtuple)。相比iterrows(),它通常具有更好的性能,尤其是在处理大量数据时。
    • index=False参数表示在生成的元组中不包含行索引,使数据访问更简洁。
  2. set(list_a).issubset(set(list_b)):

    • 这是实现子集判断的核心。将列表转换为集合(set)是进行集合操作(如子集、交集、并集等)的常用且高效的方法。
    • issubset()方法用于检查一个集合是否是另一个集合的子集。在这里,我们检查df2当前行的specifiers集合是否是df1某行specifiers集合的子集。
  3. df1['specifiers'].apply(lambda x: ...):

    • apply()方法用于对DataFrame或Series的每个元素或每行/列应用一个函数。
    • lambda x: ...是一个匿名函数,x代表df1['specifiers']列中的每一个列表元素。这个函数将每个列表转换为集合,然后执行子集判断。
    • apply()返回一个布尔型Series,用于筛选df1中满足条件的行。
  4. pd.concat([...], axis=1):

    • pd.concat()用于沿着特定轴(行或列)连接Pandas对象。
    • axis=1表示按列(横向)拼接。
    • 我们首先通过pd.DataFrame([row_df2] * len(matching_rows_df1))将df2的当前行数据重复matching_rows_df1的行数次,这样可以确保在横向拼接时,df2的单行数据能与df1的多个匹配行一一对应。
    • matching_rows_df1.reset_index(drop=True):在拼接前重置df1匹配行的索引,避免因索引不一致导致的问题。drop=True表示不将旧索引作为新列。
    • 最终,通过反复调用merged_df = pd.concat([merged_df, combined_row_data], ignore_index=True)将每次迭代的结果累积到merged_df中。ignore_index=True确保最终DataFrame的索引是连续的。

性能考量

虽然上述迭代方法能够准确解决问题,但其性能在大规模数据集上可能会成为瓶颈。具体来说:

  • df2.itertuples(): 遍历DataFrame行本身就比Pandas的矢量化操作慢。
  • df1['specifiers'].apply(lambda x: ...): apply方法虽然比纯Python循环快,但对于每一行都要执行集合转换和子集判断,这仍然是一个行级操作,效率低于完全矢量化的Pandas操作。
  • 重复pd.concat(): 在循环内部反复将结果拼接到一个不断增长的DataFrame上,会导致频繁的数据复制和内存重新分配,效率较低。更优的做法是先将所有中间结果存储在一个列表中,最后一次性concat。

对于原始问题中df1有623行,df2有95999行的数据规模,这种方法在合理的时间内完成计算是可接受的。然而,如果数据集规模更大(例如,df1和df2都有数百万行),则需要考虑更高级的优化技术,例如:

  • 将列表列转换为可哈希的字符串表示:如果列表元素顺序不重要且是固定的,可以将其排序后转换为字符串,然后使用字符串匹配或哈希表进行更快的查找。
  • 利用稀疏矩阵或倒排索引:如果specifiers中的元素种类很多,可以构建一个倒排索引,将每个specifier映射到包含它的df1行索引,从而加速查找过程。
  • 使用Cython或Numba:对于性能要求极高的场景,可以将核心的循环和判断逻辑用Cython或Numba进行JIT编译,以接近C语言的性能。

总结

当Pandas的传统merge功能无法满足基于列表(对象列)的复杂合并条件时,通过迭代、结合集合操作(issubset)和apply()进行筛选,再利用pd.concat()拼接结果,是一种灵活有效的解决方案。虽然这种方法在性能上可能不如完全矢量化的操作,但对于中等规模的数据集而言,其可读性和实现难度相对较低,能够满足业务需求。在处理大规模数据时,应额外关注性能瓶颈并考虑进一步的优化策略。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

410

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

638

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

362

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

263

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

632

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

564

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

671

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

618

2023.09.22

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号