【问题标题】:Merge/Join 2 DataFrames by complex criteria按复杂标准合并/加入 2 个数据帧
【发布时间】: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

只有当 df1df2 中的 d 列完全相等时才会合并。 我更喜欢的是能够定义让我们说一个范围,也就是说,如果df2['d']-5 &lt;= df1['d'] &lt;= df2['d']+5 它仍然可以,这意味着,两个数据帧中的这些行都是要合并的候选者,只有当测试失败df1 列被填充NaN(如上例)。

这样,我可以通过几个步骤来模仿我的嵌套 for-for 循环,希望这样会更快吗?

任何建议/提示/示例将不胜感激。

谢谢

【问题讨论】:

    标签: python pandas scipy scikit-learn dataframe


    【解决方案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']即可。

    【讨论】:

      猜你喜欢
      • 2012-07-23
      • 1970-01-01
      • 1970-01-01
      • 2019-02-26
      • 2018-06-29
      • 1970-01-01
      • 2016-07-29
      • 2014-05-15
      • 2013-09-21
      相关资源
      最近更新 更多