
本教程探讨了在pandas中如何高效地实现基于数值范围的数据匹配与数据合并。针对传统`merge`函数无法直接处理区间匹配的场景,我们介绍了利用`pd.intervalindex`构建区间索引,并通过`get_indexer`方法将主表数据关联到对应的区间,最终实现精准的范围查找与值填充,有效解决了复杂的数据关联难题。
在数据分析和处理中,我们经常需要将两个数据集基于某个条件进行合并。当条件是精确值匹配时,Pandas的merge函数非常强大且高效。然而,当合并条件涉及到“一个值是否落入另一个数据框中定义的某个数值区间”时,传统的merge方法或直接的布尔索引往往会遇到挑战,因为它们无法直接处理这种非等值的范围匹配关系。
问题场景与挑战
假设我们有两个DataFrame:
df 包含一系列序列号(serial),我们需要为每个序列号查找其对应的作业(Job)。df2 包含作业信息,其中每个作业由一个起始序列号(StartSerial)和一个结束序列号(StopSerial)定义,并关联一个作业编号(Job)。
我们的目标是,如果df中的serial值落在df2中某个StartSerial和StopSerial定义的区间内,就将对应的Job值填充到df中。
以下是示例数据:
import pandas as pdimport numpy as npnum = {'serial':[10,20,30,50]}df = pd.DataFrame(num)print("DataFrame df:")print(df)cols = {'StartSerial':[9,19,29,39],'StopSerial':[15,25,35,45],'Job':[564,859,748,125]}df2 = pd.DataFrame(cols)print("nDataFrame df2:")print(df2)
输出:
硅基智能
基于Web3.0的元宇宙,去中心化的互联网,高质量、沉浸式元宇宙直播平台,用数字化重新定义直播
62 查看详情
DataFrame df: serial0 101 202 303 50DataFrame df2: StartSerial StopSerial Job0 9 15 5641 19 25 8592 29 35 7483 39 45 125
尝试使用直接的布尔索引或np.where通常会失败,因为这些操作是按行进行的,而我们期望的是跨行匹配:
# 错误尝试1:直接使用np.where# df['Job'] = np.where((df['serial'] >= df2['StartSerial']) & (df['serial'] = df2['StartSerial']) & (df['serial'] <= df2['StopSerial']), 'Job'] = df2['Job']# 同样,这种方式无法实现跨DataFrame的行级别区间匹配。
上述方法无法正确执行,因为它们试图在不同DataFrame的行之间直接进行元素级比较,而不是进行区间查找。
解决方案:利用 pd.IntervalIndex 进行区间匹配
Pandas提供了一个强大的数据结构 pd.IntervalIndex,专门用于表示和操作区间数据。它非常适合解决这类基于范围的查找问题。
pd.IntervalIndex 的核心思想是将一个DataFrame的列(例如StartSerial和StopSerial)转换为一个区间索引,然后可以使用这个索引来高效地查找另一个DataFrame中的值(例如serial)属于哪个区间。
步骤一:创建 pd.IntervalIndex
首先,我们从 df2 的 StartSerial 和 StopSerial 列创建一个 pd.IntervalIndex。closed 参数用于指定区间的闭合性,例如 ‘both’ 表示两端都包含,’left’ 表示左闭右开等。
# 从df2的起始和结束序列号创建IntervalIndex# closed="both" 表示区间 [StartSerial, StopSerial] 是闭合的idx = pd.IntervalIndex.from_arrays(df2.StartSerial, df2.StopSerial, closed="both")print("nCreated IntervalIndex:")print(idx)
输出:
Created IntervalIndex:IntervalIndex([[9, 15], [19, 25], [29, 35], [39, 45]], closed='both', dtype='interval[int64]')
步骤二:使用 get_indexer 查找匹配的区间索引
IntervalIndex 对象有一个 get_indexer 方法,它接受一个数组(例如 df 的 serial 列)作为输入,并返回一个整数数组,表示输入数组中每个元素在 IntervalIndex 中的位置。如果某个值没有落在任何区间内,则返回 -1。
# 使用IntervalIndex的get_indexer方法查找df.serial中每个值对应的区间索引# 这将返回df2中对应行的索引indexer = idx.get_indexer(df.serial)print("nIndexer array (df2 row indices):")print(indexer)
输出:
Indexer array (df2 row indices):[ 0 1 2 -1]
可以看到,serial 为 10 对应 df2 的第 0 行(区间 [9, 15]),20 对应第 1 行,30 对应第 2 行。而 50 没有落在任何区间内,因此返回 -1。
步骤三:根据索引填充 Job 值
现在我们有了 df 中每个 serial 值对应的 df2 行索引。我们可以利用这些索引来从 df2 中提取 Job 值并将其分配给 df。
由于 get_indexer 返回的索引可能包含 -1(表示无匹配),我们需要先处理这些无效索引。一种常见的方法是创建一个与 df 长度相同的空列,然后根据有效的 indexer 值进行填充。
# 初始化df中的'Job'列为NaNdf['Job'] = np.nan# 找到有效的索引(即不为-1的索引)valid_indices_in_df = (indexer != -1)valid_indices_in_df2 = indexer[valid_indices_in_df]# 将df2中对应Job值赋给df的Job列# 注意:这里直接使用df2.loc[valid_indices_in_df2, 'Job']来获取Job值# 然后赋值给df.loc[valid_indices_in_df, 'Job']df.loc[valid_indices_in_df, 'Job'] = df2.loc[valid_indices_in_df2, 'Job'].valuesprint("nFinal DataFrame with matched Jobs:")print(df)
输出:
Final DataFrame with matched Jobs: serial Job0 10 564.01 20 859.02 30 748.03 50 NaN
这正是我们期望的结果。serial 为 50 的行因为没有找到匹配的区间,所以 Job 列为 NaN。
完整代码示例
将上述步骤整合到一起,形成完整的解决方案:
import pandas as pdimport numpy as np# 原始数据num = {'serial':[10,20,30,50]}df = pd.DataFrame(num)cols = {'StartSerial':[9,19,29,39],'StopSerial':[15,25,35,45],'Job':[564,859,748,125]}df2 = pd.DataFrame(cols)# 1. 创建 pd.IntervalIndexidx = pd.IntervalIndex.from_arrays(df2.StartSerial, df2.StopSerial, closed="both")# 2. 使用 get_indexer 查找匹配的区间索引indexer = idx.get_indexer(df.serial)# 3. 初始化df中的'Job'列为NaN,并根据有效索引填充df['Job'] = np.nanvalid_indices_in_df = (indexer != -1)valid_indices_in_df2 = indexer[valid_indices_in_df]# 确保df2.loc[valid_indices_in_df2, 'Job']的索引与df.loc[valid_indices_in_df, 'Job']的索引对齐# 最简单的方式是获取其values进行赋值,避免索引不对齐的问题df.loc[valid_indices_in_df, 'Job'] = df2.loc[valid_indices_in_df2, 'Job'].valuesprint("n最终结果:")print(df)
注意事项与总结
闭合性 (closed 参数): pd.IntervalIndex.from_arrays 的 closed 参数非常重要,它决定了区间的包含关系。’left’:左闭右开 [a, b)’right’:左开右闭 (a, b]’both’:两端都闭合 [a, b] (默认值)’neither’:两端都开 (a, b)根据您的业务需求选择正确的闭合性。性能: pd.IntervalIndex 内部通常使用优化过的结构(如二叉搜索树),因此对于大规模数据集的区间查找,它的性能通常优于循环或复杂的条件判断。NaN 处理: 如果 serial 值没有落在任何区间内,get_indexer 返回 -1。在赋值时,这些未匹配的行将保留 NaN,这通常是期望的行为。多列匹配: 如果需要基于多个条件(例如,除了序列号范围,还要匹配一个类型字段),可能需要先对数据进行分组,然后在每个组内应用 IntervalIndex 匹配,或者考虑更复杂的索引结构。
通过 pd.IntervalIndex,Pandas提供了一种优雅且高效的方式来解决复杂的区间匹配问题,极大地简化了这类数据关联任务的实现。
以上就是Pandas中基于IntervalIndex实现高效区间匹配与数据关联的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/579916.html
微信扫一扫
支付宝扫一扫