按复杂标准合并/加入 2 个数据帧
Posted
技术标签:
【中文标题】按复杂标准合并/加入 2 个数据帧【英文标题】:Merge/Join 2 DataFrames by complex criteria 【发布时间】:2015-03-26 11:01:29 【问题描述】:我有 2 个大型数据集(每个数据集从 70K 到 110K 很大)。我想关联/比较两者,并根据某些条件/标准找到 set2 中的哪些项目可以在 set1 中找到。
我目前的策略是按公共字段对两个列表进行排序,然后运行嵌套的 for
循环,执行条件 if
测试,将预定义的 dict 与找到的项目和不匹配的项目聚合。
例子:
import pandas as pd
list1 = ['a': 56, 'b': '38', 'c': '11', 'd': '10', 'e': 65,
'a': 31, 'b': '12', 'c': '26', 'd': '99', 'e': 71,
'a': 70, 'b': '49', 'c': '40', 'd': '227', 'e': 1,
'a': 3, 'b': '85', 'c': '32', 'd': '46', 'e': 70,]
list2 = ['a': 56, 'b': '38', 'c': '11', 'd': '10', 'e': 65,
'a': 145, 'b': '108', 'c': '123', 'd': '84', 'e': 3,
'a': 113, 'b': '144', 'c': '183', 'd': '7', 'e': 12,
'a': 144, 'b': '60', 'c': '46', 'd': '106', 'e': 148,
'a': 57, 'b': '87', 'c': '51', 'd': '95', 'e': 187,
'a': 41, 'b': '12', 'c': '26', 'd': '99', 'e': 71,
'a': 80, 'b': '49', 'c': '40', 'd': '227', 'e': 1,
'a': 3, 'b': '85', 'c': '32', 'd': '46', 'e': 70,
'a': 107, 'b': '95', 'c': '81', 'd': '15', 'e': 25,
'a': 138, 'b': '97', 'c': '38', 'd': '28', 'e': 171]
re_dict = dict([('found', []), ('alien', [])])
for L2 in list2:
for L1 in list1:
if (L1['a']-5 <= L2['a'] <= L2['a']+10) and L2['c'][-1:] in L1['c'][-1:]:
if (65 <= L2['e'] <= 75):
L2.update('e': 'some value')
re_dict['found'].append(L2)
list1.remove(L1)
break # break out from the inner loop
else: # if the inner loop traversed entire list, there were no matches
re_dict['alien'].append(L2)
以上产生期望的结果:
re_dict
'alien': ['a': 145, 'b': '108', 'c': '123', 'd': '84', 'e': 3,
'a': 113, 'b': '144', 'c': '183', 'd': '7', 'e': 12,
'a': 57, 'b': '87', 'c': '51', 'd': '95', 'e': 187,
'a': 41, 'b': '12', 'c': '26', 'd': '99', 'e': 71,
'a': 107, 'b': '95', 'c': '81', 'd': '15', 'e': 25,
'a': 138, 'b': '97', 'c': '38', 'd': '28', 'e': 171],
'found': ['a': 56, 'b': '38', 'c': '11', 'd': '10', 'e': 'some value',
'a': 144, 'b': '60', 'c': '46', 'd': '106', 'e': 148,
'a': 80, 'b': '49', 'c': '40', 'd': '227', 'e': 1,
'a': 3, 'b': '85', 'c': '32', 'd': '46', 'e': 'some value']
所以它完成了这项工作,但显然效率不高,似乎是pandas
的理想工作。
我认为如果我可以合并/加入两个DataFrames
,那将是理想的,但我无法弄清楚如何根据复杂标准进行合并。另外我的数据集大小不相等。
例子:
df1 = pd.DataFrame(list1)
df2 = pd.DataFrame(list2)
pd.merge(df1,df2,on='d',how='outer')
a_x b_x c_x d e_x a_y b_y c_y e_y
0 56 38 11 10 65 56 38 11 65
1 31 12 26 99 71 41 12 26 71
2 70 49 40 227 1 80 49 40 1
3 3 85 32 46 70 3 85 32 70
4 NaN NaN NaN 84 NaN 145 108 123 3
5 NaN NaN NaN 7 NaN 113 144 183 12
6 NaN NaN NaN 106 NaN 144 60 46 148
7 NaN NaN NaN 95 NaN 57 87 51 187
8 NaN NaN NaN 15 NaN 107 95 81 25
9 NaN NaN NaN 28 NaN 138 97 38 171
只有当 df1
和 df2
中的 d 列完全相等时才会合并。
我更喜欢的是能够定义让我们说一个范围,也就是说,如果df2['d']-5 <= df1['d'] <= df2['d']+5
它仍然可以,这意味着,两个数据帧中的这些行都是要合并的候选者,只有当测试失败df1
列被填充NaN(如上例)。
这样,我可以通过几个步骤来模仿我的嵌套 for-for 循环,希望这样会更快吗?
任何建议/提示/示例将不胜感激。
谢谢
【问题讨论】:
【参考方案1】:pandas 目前缺乏对“附近”查询的直接支持,尽管我有一个 pull request 来添加一些基本功能(对于您的用例来说还不够)。
幸运的是,科学的 Python 生态系统为您提供了自己完成这项工作所需的工具。
加入附近位置的有效方法是使用树数据结构,如scikit-learn documentation 中所述。 SciPy 和 scikit-learn 都有合适的 KDTree 实现。
完全使用临时规则并不容易(或高效),但只要您有明确定义的距离度量,您就可以有效地进行最近邻查找。我相信 scikit-learn 的 KDTree 甚至可以让您定义自己的距离度量,但我们将坚持使用正常的欧几里德距离来继续您的示例:
from scipy.spatial import cKDTree as KDTree
import pandas as pd
# for each row in df2, we want to join the nearest row in df1
# based on the column "d"
join_cols = ['d']
tree = KDTree(df1[join_cols])
distance, indices = tree.query(df2[join_cols])
df1_near_2 = df1.take(indices).reset_index(drop=True)
left = df1_near_2.rename(columns=lambda l: 'x_' + l)
right = df2.rename(columns=lambda l: 'y_' + l)
merged = pd.concat([left, right], axis=1)
这会导致:
x_a x_b x_c x_d x_e y_a y_b y_c y_d y_e
0 56 38 11 10 65 56 38 11 10 65
1 31 12 26 99 71 145 108 123 84 3
2 56 38 11 10 65 113 144 183 7 12
3 31 12 26 99 71 144 60 46 106 148
4 31 12 26 99 71 57 87 51 95 187
5 31 12 26 99 71 41 12 26 99 71
6 70 49 40 227 1 80 49 40 227 1
7 3 85 32 46 70 3 85 32 46 70
8 56 38 11 10 65 107 95 81 15 25
9 56 38 11 10 65 138 97 38 28 171
如果要根据多列的接近度进行合并,只需设置join_cols = ['d', 'e', 'f']
即可。
【讨论】:
以上是关于按复杂标准合并/加入 2 个数据帧的主要内容,如果未能解决你的问题,请参考以下文章