加快嵌套循环比较
Posted
技术标签:
【中文标题】加快嵌套循环比较【英文标题】:Speeding up nested loop comparison 【发布时间】:2020-07-13 20:26:34 【问题描述】:我有两个数组arr1
和arr2
,大小为(90000,1)
和(120000,1)
。我想知道arr1
的axis=0
的任何元素是否存在于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
位置,第二个元素为arr2
。然后fault_index_pos
和fault_index_neg
被压缩到唯一的元素中,因为arr1
的元素可能在arr2
的多个位置,并且列表将具有循环位置。最后,通过将fault_index
列表作为要删除的索引,使用np.delete
删除匹配的元素。
我正在寻找一种更快的冲突比较方法,将其称为multiprocessing
、vectorization
或其他任何名称。您可以说这不会花费太多时间,但实际上数组的维度是 (x,8,10)
,但为了清楚起见,我将它们缩短了。
【问题讨论】:
【参考方案1】:忽略 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 匹配出现的索引。
【讨论】:
如何从 both 输入创建反向字典(可能称它们为elt2ix_a
和elt2ix_b
)然后执行:for elt in set(elt2ix_a) & set(elt2ix_b): yield from itertools.product(elt2ix_a[elt], elt2ix_b[elt])
会工作,占用更多内存,也没有计时。注意set(dict1) & set(dict2)
也可以写成dict1.keys() & dict2.keys()
。也没有计时;-)
谢谢,我不知道你可以用 dict_keys
做到这一点。
Python 3 的“dict 视图”在几个方面表现得像集合。这里非常简短的概述:***.com/questions/47273297/…
如果 a 和 b 是列表列表而不是整数列表怎么办?我得到不可散列的类型:elt2ix[elt].append(i)
的列表错误【参考方案2】:
我会使用 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)
【讨论】:
@0x5453 确实如此,正如我在帖子的第一个项目符号中所说的那样。但是,使用 pandas,我不需要转置数组的形状来创建遮罩。 @colt.exe 这改变了原始问题的范围。 (90000, 80) 是 90k 行和 80 列。现在尚不清楚您要完成什么。您现在是否尝试从 2d 数组的每一列中删除 1d 数组中的所有值? @colt.exe 但是,如果我理解正确,如果您有一个 1d 数组,您希望从 2d 数组的每一列中删除其值,您可以按照我的说明进行操作,但使用循环将掩码应用于二维数组的每一列。但是,我认为您不能将它们组合回一个数组,因为列的长度必须相同。【参考方案3】:请阅读一些关于 NumPy 的向量功能以及 Python 的序列包含运算符的教程。您正在尝试编写一个大型应用程序,该应用程序非常需要您尚未学习的语言工具。
也就是说,最快的方法可能是将每个转换为set
并采取设置的交叉点。对于 N 个元素的序列/集合,所涉及的操作是 O(n);您的嵌套循环是 O(N*M) (在两个序列大小上)。
任何有关 Python 集的教程都会引导您完成此操作。
【讨论】:
嗯,我当然想到了集合,但没有受过教育的猜测可能有更多更快的方法来实现这一点。但是我完全同意我必须学习一些教程,并且当我有空闲时间时肯定会这样做。欣赏您的建议。【参考方案4】:正如@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)]
【讨论】:
我没有以这种方式使用掩码,因为它确实适用于 OP 呈现的数据的形状。 arrarys 是 np 数组,在将它们重塑为(-1,80)
重叠后会抛出不可散列的类型:列表。以上是关于加快嵌套循环比较的主要内容,如果未能解决你的问题,请参考以下文章