Pandas DataFrame区间匹配:两个含深度区间的DataFrame匹配需求
Pandas深度区间匹配解决方案
我经常处理这类地质/测井数据的区间匹配需求,刚好能给你一些实用的解决方案。核心目标就是把两个DataFrame中同ID下存在重叠(或包含关系)的深度区间关联起来,将file_2的参数匹配到对应的file_1区间中。下面分几种场景给出实现方法:
先准备示例数据(方便你测试)
import pandas as pd # 模拟你的file_1数据 file_1 = pd.DataFrame({ 'ID': ['HLD032', 'HLD032', 'HLD650'], 'Depth_From': [611.10, 612.00, 597.40], 'Depth_To': [612.00, 612.20, 598.10] }) # 模拟你的file_2数据 file_2 = pd.DataFrame({ 'ID': ['HLD032', 'HLD032', 'HLD650'], 'Depth_From': [611.00, 612.10, 597.00], 'Depth_To': [612.00, 612.30, 598.50], 'Infill': [0.2, 0.5, 0.1], 'Roughness': [1.2, 2.1, 0.8], 'Thickness': [0.9, 0.2, 0.7] })
方法一:基础Merge+条件筛选(适合小数据量)
这种方法逻辑简单,先按ID合并两个DataFrame,再筛选出区间重叠的行:
# 按ID合并,生成同ID的所有区间组合 merged = pd.merge(file_1, file_2, on='ID', suffixes=('_file1', '_file2')) # 筛选区间重叠的行:两个区间存在交集的条件 # 即file1的起始深度 ≤ file2的结束深度,且file1的结束深度 ≥ file2的起始深度 matched = merged[ (merged['Depth_From_file1'] <= merged['Depth_To_file2']) & (merged['Depth_To_file1'] >= merged['Depth_From_file2']) ] # 整理结果列名,保留需要的字段 result = matched.rename(columns={ 'Depth_From_file1': 'Depth_From', 'Depth_To_file1': 'Depth_To' })[['ID', 'Depth_From', 'Depth_To', 'Infill', 'Roughness', 'Thickness']] print(result)
方法二:Groupby+Apply(适合大数据量)
如果你的数据量很大(比如像你说的1万+行),直接Merge会生成大量冗余行,效率很低。这种情况下,按ID分组后再处理每个组的区间匹配会更高效:
def match_single_id_intervals(group_file1, group_file2): """处理单个ID下的区间匹配""" matches = [] # 遍历file1当前ID的每一行区间 for _, row_file1 in group_file1.iterrows(): # 筛选file2中与当前区间重叠的行 overlap_mask = ( group_file2['Depth_From'] <= row_file1['Depth_To'] ) & ( group_file2['Depth_To'] >= row_file1['Depth_From'] ) matched_rows = group_file2[overlap_mask].copy() # 带上file1的区间信息 matched_rows['File1_Depth_From'] = row_file1['Depth_From'] matched_rows['File1_Depth_To'] = row_file1['Depth_To'] matches.append(matched_rows) return pd.concat(matches) if matches else pd.DataFrame() # 按ID分组 grouped_file1 = file_1.groupby('ID') grouped_file2 = file_2.groupby('ID') # 只处理两个DataFrame都存在的ID common_ids = set(grouped_file1.groups.keys()) & set(grouped_file2.groups.keys()) # 逐个ID处理并合并结果 final_result = pd.concat([ match_single_id_intervals(grouped_file1.get_group(id), grouped_file2.get_group(id)) for id in common_ids ]).reset_index(drop=True) # 调整列顺序,让结果更清晰 final_result = final_result[ ['ID', 'File1_Depth_From', 'File1_Depth_To', 'Depth_From', 'Depth_To', 'Infill', 'Roughness', 'Thickness'] ] print(final_result)
进阶:用IntervalTree加速超大数据集
如果你的数据量达到几十万甚至上百万行,上面的循环方法还是会慢。可以用专门处理区间查询的intervaltree库来大幅提升效率:
首先安装库:
pip install intervaltree
然后实现代码:
from intervaltree import IntervalTree def match_with_intervaltree(group_file1, group_file2): """用IntervalTree加速区间匹配""" # 构建file2的区间树,每个区间对应一行数据 interval_tree = IntervalTree() for _, row in group_file2.iterrows(): interval_tree.addi(row['Depth_From'], row['Depth_To'], row) matches = [] for _, row_file1 in group_file1.iterrows(): # 查询所有与当前区间重叠的file2区间 overlapping_intervals = interval_tree.search(row_file1['Depth_From'], row_file1['Depth_To']) for interval in overlapping_intervals: matched_row = interval.data.copy() matched_row['File1_Depth_From'] = row_file1['Depth_From'] matched_row['File1_Depth_To'] = row_file1['Depth_To'] matches.append(matched_row) return pd.DataFrame(matches) if matches else pd.DataFrame() # 用法和Groupby方法一致,替换match_single_id_intervals函数即可
关键注意事项
- 匹配条件调整:上面的代码用的是「区间有交集」的规则,如果你的需求是「file1的区间完全被file2的区间包含」,只需把筛选条件改成:
# 完全包含的条件:file2的起始深度 ≤ file1的起始深度,且file2的结束深度 ≥ file1的结束深度 mask = (group_file2['Depth_From'] <= row_file1['Depth_From']) & (group_file2['Depth_To'] >= row_file1['Depth_To']) - 一对多聚合:如果一个file1区间匹配到多个file2区间,可以对参数做聚合处理(比如取平均值、求和):
agg_result = final_result.groupby(['ID', 'File1_Depth_From', 'File1_Depth_To']).agg({ 'Infill': 'mean', 'Roughness': 'max', 'Thickness': 'sum' }).reset_index() - 数据类型检查:确保ID列和深度列的类型一致(比如ID是字符串,深度是float),避免匹配错误。
内容的提问来源于stack exchange,提问作者Tani84
相关产品推荐
相关产品推荐

