最快的重合矩阵

时间:2017-11-24 14:48:45

标签: python numpy

我有两个数组,我想计算一个巧合的列表/数组。也就是说,所有索引i,j的列表使得a [i] == b [j]。这是我现在的代码:

b = np.array([3, 5, 6, 4])
a = np.array([1, 2, 3, 4])

np.array([[i, j] for i in range(a.size) for j in range(b.size) if a[i] == b[j]])

是否有更快(也许是以numpy为动力)的方式来做到这一点?

4 个答案:

答案 0 :(得分:2)

方法#1

一种方法是使用np.in1d -

m_a = np.in1d(a,b)
I = np.flatnonzero(m_a)
J = np.flatnonzero(np.in1d(b, a[m_a]))

示例输入,输出 -

In [367]: a
Out[367]: array([1, 2, 3, 4])

In [368]: b
Out[368]: array([3, 5, 6, 4])

In [370]: I
Out[370]: array([2, 3])

In [371]: J
Out[371]: array([0, 3])

方法#2

另一种直接但记忆重要的方式是使用broadcasting -

I,J = np.nonzero(a[:,None] == b)

方法#3

对于输入数组中没有重复项的情况,我们可以使用np.searchsorted。这里有两个变体 - 一个用于排序a,另一个用于通用a

变体#1:对于已排序的a -

idx = np.searchsorted(a, b)
idx[idx==a.size] = 0
mask = a[idx] == b
I = np.searchsorted(a,b[mask])
J = np.flatnonzero(mask)

变体#2:对于此通用变体案例,我们需要使用a的argsort索引 -

sidx = a.argsort()
a_sort = a[sidx]
idx = np.searchsorted(a_sort, b)
idx[idx==a.size] = 0
mask = a_sort[idx] == b
I = sidx[np.searchsorted(a_sort,b[mask])]
J = np.flatnonzero(mask)

答案 1 :(得分:1)

numpy解决方案可能正在使用函数numpy.argwhere(),该函数可用于查找符合给定条件的数组的索引。

ax = np.tensordot(a, np.ones(len(a)), axes = 0)
bx = np.tensordot(np.ones(len(b)), b, axes = 0)
np.argwhere(ax - bx == 0)

ax - bx的零元素索引只是那些与ab的相等元素对应的索引,因为有常量行rsp。张量产品的列相交'。不确定,这个解决方案是否更快。

答案 2 :(得分:1)

[已经解决了,但这里是时间]:

我已将您的解决方案与更多python列表方式以及建议的矩阵解决方案here进行了比较:(您需要从该矩阵中获取索引)。

import numpy as np
import random
import time

random.seed(12345)
b = [random.randint(0,100000) for i in range(10000)]
a = [random.randint(0,100000) for i in range(10000)]

为了获得更准确的计时(对于大型数据集),我创建了两个(伪)随机整数列表,其长度为1e5。

#List based approach
start_time = time.time()
c2 = [[i,j]  for i in range(len(a)) for j in range(len(b)) if a[i] == b[j]]
print time.time() - start_time # t = 29.2776758671

b = np.array(a)
a = np.array(b)

#NumPy Array based approach    
start_time = time.time()
c1 = np.array([[i, j] for i in range(a.size) for j in range(b.size) if a[i] == b[j]])
print time.time() - start_time # t = 46.374776125

虽然在不使用numpy数组的情况下需要稍微缩短,但它并不是一个巨大的时间差,但它仍然意味着大型矢量的计算时间相当长。

以巧合的形式创建中介解决方案

#Coincidence Matrix (NumPy) based approach    
start_time = time.time()
c3 = (a[None,:] == b[:,None]).astype(int)
c3s = np.where(c3 == 1)
print time.time() - start_time # t = 0.857568979263

我还计划了之前发布的另一个解决方案,似乎是解决此问题的最快方法:

c4 = np.nonzero(a[:,None] == b)
# t = 0.399062156677

答案 3 :(得分:1)

基于dicts集和列表的(非常)快速解决方案,它在时间和空间上是线性的,无论数据是什么,是否重复,是否是大键。

a,b = np.random.randint(0,10**8,size=(2,10**4))

import collections

def default(a):
    d=collections.defaultdict(list)
    for k,v in enumerate(a):
        d[v].append(k)
    return d

def coincidences(a,b):
    aa=default(a)
    bb=default(b)
    ab=set(aa.keys()) & set(bb.keys())
    l=[]
    for k in ab:
      for i in aa[k]:
          for j in bb[k]:
              l.append((i,j))
    return l

运行:

In [125]: %timeit coincidences(a,b)
10.6 ms ± 402 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy只有在能够实现线性解决方案时才会获胜。

修改

等效的Pandas解决方案(相同时间):

def coincidences_pd(a,b):        
    aa=pd.DataFrame(list(range(len(a))),a)
    bb=pd.DataFrame(list(range(len(b))),b)
    return pd.merge(aa,bb,left_index=True,right_index=True)

In [219]: coincidences_pd(a,b)
Out[219]: 
           0_x   0_y
54025822  1752  8046
84735197  7301  2956