【问题标题】:Efficient way to get rows of two arrays that have matching values in fraction of their colums获取在其列中具有匹配值的两个数组的行的有效方法
【发布时间】:2020-12-16 22:04:21
【问题描述】:

假设我们有两个二维数组,array1 和 array2,或者它们的 panda.DataFrame 等价物。对于array1 中的某些行,array2 中可能只有一行或没有行在我想要提取的第 2,3 和 4 列中具有相等的值。那就是:

array1 = array([[1,4,      5, 656,  1385,   434],
                [11,46,    3, 7356, 585,    74],
                [12,45,    5, 656,  135,    134],
                [112,475,  3, 356,  285,    134],
               ])


array2 = array([[15,44,     5, 656, 1385,   434],
                [151,436,   3, 356, 285,     74],
                [521,406,   5, 656, 135,     24],
                [152,445,   54, 56, 635,     134],
                [72,727,    12, 16, 55,      634]])

而我正在寻找的是:

array1_filtered = array([[1,4,       5, 656,  1385,     434],
                         [112,475,   3, 356,  285,      134],
                         [12,45,     5, 656,  135,      134],
                                               ])
array2_filtered = array([[15,   44,      5, 656, 1385,     434],
                         [151, 436,      3, 356, 285,      74],
                         [521, 406,      5, 656, 135,      24]
                                                ])

两个过滤后的数组在其第 2,3 和 4 列方面匹配。更好的是,在过滤结果中具有相同的匹配顺序,如上所示。

我可以想到以下解决方案,但结果很慢:

array1_filtered = []
array2_filtered = []

for row in array1:
    idx = np.where((array2[:, target_columns] == np.array([row[target_columns]])).all(axis=1))[0]
    if idx.size != 0:
        array1_filtered.append(row.tolist())
        array2_filtered.append(array2[idx,:][0].tolist())
array1_filtered  = np.asarray(array1_filtered)
array2_filtered  = np.asarray(array2_filtered)

有人知道使用 numpy 或 pandas.DataFrame 的更快方法吗?

【问题讨论】:

    标签: python pandas dataframe numpy


    【解决方案1】:

    让我们试试熊猫:

    cols = [2,3,4]
    
    s = pd.DataFrame(np.vstack([array1,array2])).duplicated(cols, keep=False)
    
    array1_filtered = array1[s.iloc[:len(array1)]]
    array2_filtered = array2[s.iloc[-len(array2):]]
    

    更新:另一个稍微好一点的解决方案,它不计算每个数组内部的重复:

    cols = [2,3,4]
    
    s = (pd.DataFrame(np.vstack([array1,array2]))
           .assign(arr_no=[0]*len(array1) + [1]*len(array2))
        )
    
    duplicates = s.groupby(cols).arr_no.transform('nunique')==2
    
    array1_filtered = array1[duplicates[s.arr_no==0]]
    array2_filtered = array2[duplicates[s.arr_no==1]]
    

    更新:对于相同的匹配顺序:

    cols = [2,3,4]
    
    s = (pd.DataFrame(np.vstack([array1,array2]))
           .assign(arr_no=[0]*len(array1) + [1]*len(array2))
           .sort_values(cols, kind='mergesort')              # here, sort by `cols`
        )
    
    duplicates = s.groupby(cols).arr_no.transform('nunique')==2
    
    # slicing from s instead of from the original arrays
    array1_filtered = s.to_numpy()[duplicates&(s.arr_no==0),:-1]
    array2_filtered = s.to_numpy()[duplicates&(s.arr_no==1),:-1]
    

    输出:

    # array1_filtered
    array([[ 112,  475,    3,  356,  285,  134],
           [  12,   45,    5,  656,  135,  134],
           [   1,    4,    5,  656, 1385,  434]])
    
    # array2_filtered
    array([[ 151,  436,    3,  356,  285,   74],
           [ 521,  406,    5,  656,  135,   24],
           [  15,   44,    5,  656, 1385,  434]])
    

    【讨论】:

    • 非常有效的解决方案
    • 我喜欢您的方法,但我认为您应该对 2 个数组进行排序以尊重“过滤结果中具有相同匹配顺序”的条件。此外,您的最后一行有一个小错字,应该是 array2_filtered。
    • @QuangHoang 第一个解决方案似乎更快不是吗?您能否推荐一种修改,以使匹配列的行顺序相同?
    • @Azerila 第一个选项无法从数组中的重复项中识别 数组内 中的重复项。后一种解决方案只选择两个数组中都出现的那些。将更新解决方案以产生相同的匹配顺序。
    • @Azerila 查看按顺序提供匹配的解决方案的更新答案。
    【解决方案2】:

    这可以通过合并第 2、3 和 4 列来完成:

    cols = [2,3,4]
    df1 = pd.DataFrame(array1)
    df2 = pd.DataFrame(array2)
    
    df_filtered1 = df1.merge(df2.iloc[:,cols], how = 'inner', on = cols).sort_values(cols)
    df_filtered2 = df2.merge(df1.iloc[:,cols], how = 'inner', on = cols).sort_values(cols)
    
    # Optional : if you want arrays
    array1_filtered = df_filtered1.values
    array2_filtered = df_filtered2.values
    

    【讨论】:

      猜你喜欢
      • 2016-12-02
      • 1970-01-01
      • 1970-01-01
      • 2021-05-23
      • 2020-03-20
      • 1970-01-01
      • 1970-01-01
      • 2019-09-02
      • 2021-11-22
      相关资源
      最近更新 更多