加快嵌套循环比较

时间:2020-07-13 20:26:34

标签: python numpy loops

我有两个数组arr1arr2,大小分别为(90000,1)(120000,1)。我想了解axis=0上是否存在arr1的{​​{1}}的任何元素。然后将它们的位置写到列表中,然后将其删除。这将确保两个列表中的任何一个都找不到。现在,我正在使用arr2循环:

for

它在外部循环上使用list_conflict=[] for i in range (len(arr1)): for j in range (len(arr2)): if (arr1[i]==arr2[j]): list_conflict.append([i,j]) fault_index_pos = np.unique([x[0] for x in list_conflict]) fault_index_neg = np.unique([x[1] for x in list_conflict]) X_neg = np.delete(X_neg,fault_index_neg,axis=0) X_pos = np.delete(X_pos,fault_index_pos,axis=0) 的元素,并将其与arr1的每个元素进行详尽比较。如果找到匹配项,则将索引arr2附加在第一个元素list_conflict的位置和第二个arr1的位置。然后将arr2fault_index_pos压缩为唯一元素,因为fault_index_neg的元素可能位于arr1的多个位置,并且列表将具有重复位置。最后,通过将arr2列表作为要删除的索引,用np.delete删除匹配的元素。

我正在寻找一种更快的冲突比较方法,称为fault_indexmultiprocessing或其他任何方法。您可以说不会花很多时间,但是实际上数组是在vectorization维度中,但是为了清楚起见,我将它们缩短了。

4 个答案:

答案 0 :(得分:3)

忽略numpy部分,找到冲突的索引对可以在纯Python中更快地完成,花费的时间与len(a)len(b)和冲突数量成正比,而不是嵌套循环与向量长度的乘积成正比的时间:

def conflicts(a, b):
    from collections import defaultdict
    elt2ix = defaultdict(list)
    for i, elt in enumerate(a):
        elt2ix[elt].append(i)
    for j, elt in enumerate(b):
        if elt in elt2ix:
            for i in elt2ix[elt]:
                yield i, j

然后,例如

for pair in conflicts([1, 2, 4, 5, 2], [2, 3, 8, 4]):
    print(pair)

显示

(1, 0)
(4, 0)
(2, 3)

是2和4匹配项的索引。

答案 1 :(得分:1)

  • 我会使用numpy上的pandas创建一个布尔蒙版
  • 采用4行代码
import numpy as np
import pandas as pd

# create test data
np.random.seed(1)
a = np.random.randint(10, size=(10, 1))
np.random.seed(1)
b = np.random.randint(8, 15, size=(10, 1))

# create dataframe
df_a = pd.DataFrame(a)
df_b = pd.DataFrame(b)

# find unique values in df_a
unique_a = df_a[0].unique().tolist()

# create a Boolean mask and return only values of df_b not found in df_a
values_not_in_a = df_b[~df_b[0].isin(unique_a)].to_numpy()

a = array([[5],
           [8],
           [9],
           [5],
           [0],
           [0],
           [1],
           [7],
           [6],
           [9]])

b = array([[13],
           [11],
           [12],
           [ 8],
           [ 9],
           [11],
           [13],
           [ 8],
           [ 8],
           [ 9]])

# final output array
values_not_in_a = array([[13],
                         [11],
                         [12],
                         [11],
                         [13]])

仅使用numpy

import numpy

# create test data
np.random.seed(1)
a = np.random.randint(10, size=(10, 1))
np.random.seed(1)
b = np.random.randint(8, 15, size=(10, 1))

ua = np.unique(a)  # unique values of a
ub = np.unique(b)  # unique values of b

mask_b = np.isin(b, ua, invert=True)
mask_a = np.isin(a, ub, invert=True)

b_values_not_in_a = b[mask_b]
a_values_not_in_b = a[mask_a]

# b_values_not_in_a
array([13, 11, 12, 11, 13])

# a_values_not_in_b
array([5, 5, 0, 0, 1, 7, 6])

timeit

# using the following arrays
np.random.seed(1)
a = np.random.randint(10, size=(90000, 1))
np.random.seed(1)
b = np.random.randint(8, 15, size=(120000, 1))

%%timeit
5.6 ms ± 151 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

答案 2 :(得分:0)

请完成一些有关NumPy的向量功能以及Python的序列包含运算符的教程。您正在尝试编写一个大型应用程序,该应用程序非常需要您尚未学习的语言功能。

也就是说,也许最快的方法是将每个转换为set并采用设置的交集。涉及的操作为 N 个元素的序列/集合的 O(n);您的嵌套循环为 O(N * M)(两个序列大小)。

任何有关Python集的教程都将引导您完成这一过程。

答案 3 :(得分:0)

如@Prune所建议,这是一个使用set s的解决方案:

overlap = np.array(list(set(arr1) & set(arr2)))  # Depending on array shapes you may need to flatten or slice first
arr1 = arr1[~np.isin(arr1, overlap)]
arr2 = arr2[~np.isin(arr2, overlap)]