按行查找矩阵和向量之间的交点

时间:2019-03-14 19:55:13

标签: python numpy

请考虑以下内容:

tmp1 = ['a', 'b', 'c', 'd', 'e']
tmp2 = ['f', 'g', 'h', 'b', 'd']
tmp3 = ['b', 'i', 'j', 'k', 'l']
matr = np.array([tmp1, tmp2, tmp3])

matr

产生矩阵:

array([['a', 'b', 'c', 'd', 'e'],
   ['f', 'g', 'h', 'b', 'd'],
   ['b', 'i', 'j', 'k', 'l']], 
  dtype='|S1')

现在,我想知道与向量相交的每一行的值总和。说,

vec = ['a', 'c', 'f', 'b']
[sum([y in vec for y in row]) for row in matr]

返回

[3, 2, 1]

这是所需的输出。问题是我的“ matr”实际上是≈1000000 x 2200,我有6700个向量要比较。我在这里找到的解决方案太慢了,无法尝试。

我该如何改善自己的工作?

值得注意的是,矩阵内部的值来自一组〜30000个值,而我有完整的值。我已经考虑过解决方案,在这些解决方案中,我针对每个向量对这30000个值进行了运算,并使用该字典在整个矩阵中将其转换为True / False,然后才按行求和。我不确定这是否有帮助。

4 个答案:

答案 0 :(得分:2)

对于matrvec作为数组,这里是np.searchsorted-

def count_in_rowwise(matr,vec):
    sidx = vec.argsort()
    idx = np.searchsorted(vec,matr,sorter=sidx)
    idx[idx==len(vec)] = 0
    return (vec[sidx[idx]] == matr).sum(1)

vec相对较小,我们可以对其进行预排序并使用,从而为我们提供了一种替代方法来计算行数,就像这样-

def count_in_rowwise_v2(matr,vec,assume_sorted=False):
    if assume_sorted==1:
        sorted_vec = vec
    else:
        sorted_vec = np.sort(vec)
    idx = np.searchsorted(sorted_vec,matr)
    idx[idx==len(sorted_vec)] = 0
    return (sorted_vec[idx] == matr).sum(1)

上述解决方案适用于通用输入(数字或字符串相同)。为了解决特定的字符串情况,我们可以进一步优化它,方法是使用np.unique将字符串转换为数字,然后重新使用count_in_rowwise/count_in_rowwise_v2,这将为我们提供第二种方法-

u,ids = np.unique(matr, return_inverse=True)
out = count_in_rowwise(ids.reshape(matr.shape),ids[np.searchsorted(u,vec)])

答案 1 :(得分:2)

您可以使用设置相交来加快速度。比较一下:

您目前具有列表理解的解决方案:

%%timeit
print([sum([y in vec for y in row]) for row in matr])
#Output
[3,2,1]
20 µs ± 1.9 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

在列表理解中设置交集的建议解决方案:

%%timeit
print([len(set(row).intersection(vec)) for row in matr])
#Output:
[3,2,1]
17.8 µs ± 1.46 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

如果vec也是一个集合,我们将获得更高的效率:

%%timeit
vec = {'a', 'c', 'f', 'b'}
print([len(set(row).intersection(vec)) for row in matr])
#Output:
[3, 2, 1]
16.6 µs ± 1.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

答案 2 :(得分:1)

这是一个np.isin()docs)的简单可读解决方案:

np.sum(np.isin(matr, vec), axis=1)

作为奖励,如果要获取向量中矩阵的哪些元素,则可以不加和使用np.isin()

>>> np.isin(matr, vec)
array([[ True,  True,  True, False, False],
       [ True, False, False,  True, False],
       [ True, False, False, False, False]])

这说明了为什么沿行求和会产生所需的输出。

答案 3 :(得分:1)

让我们看一下当前算法的速度。根据python Wiki的说法,检查某项是否位于y in vec之类的数组中是否为O(n),这意味着最坏的情况是,它必须遍历vec中的每个元素。由于您要检查矩阵的每个元素,因此操作总数为numRows * numCols * vecLen,即O(n^3)

一种更快的方法是为vec构造一个字典来优化查找,因为字典是O(1)而不是O(n),这意味着它们可以在1个操作中完成检查,而无需不管vec多久:

vecDict = dict([(x, 1) for x in vec])

因此,您的新时间复杂度为(numRows * numCols) + vecLen,也就是O(n^2),我认为它的获取速度最快。

[sum([y in vecDict for y in row]) for row in matr]