Python 如何使用numpy查找两个非常大的矩阵行之间的成对差异?

Python 如何使用numpy查找两个非常大的矩阵行之间的成对差异?,python,numpy,matrix,array-broadcasting,Python,Numpy,Matrix,Array Broadcasting,给定两个矩阵,我想计算所有行之间的成对差异。每个矩阵有1000行和100列,因此它们相当大。我尝试使用for循环和纯广播,但for循环似乎工作得更快。我做错什么了吗?代码如下: from numpy import * A = random.randn(1000,100) B = random.randn(1000,100) start = time.time() for a in A: sum((a - B)**2,1) print time.time() - start # pure

给定两个矩阵,我想计算所有行之间的成对差异。每个矩阵有1000行和100列,因此它们相当大。我尝试使用for循环和纯广播,但for循环似乎工作得更快。我做错什么了吗?代码如下:

from numpy import *
A = random.randn(1000,100)
B = random.randn(1000,100)

start = time.time()
for a in A:
   sum((a - B)**2,1)
print time.time() - start

# pure broadcasting
start = time.time()
((A[:,newaxis,:] - B)**2).sum(-1)
print time.time() - start

广播方法需要大约1秒的时间,对于大型矩阵甚至更长。你知道如何纯粹使用numpy来加快速度吗?

这里有一个解决方案,可以避免循环和大型中间产物:

from numpy import *
import time

A = random.randn(1000,100)
B = random.randn(1000,100)

start = time.time()
for a in A:
   sum((a - B)**2,1)
print time.time() - start

# pure broadcasting
start = time.time()
((A[:,newaxis,:] - B)**2).sum(-1)
print time.time() - start

#matmul
start = time.time()
add.outer((A*A).sum(axis=-1), (B*B).sum(axis=-1)) - 2*dot(A,B.T)
print time.time() - start
印刷品:

0.546781778336
0.674743175507
0.10723400116

np.einsum的另一份工作

np.einsum('ijk,ijk->ij', A[:,None,:] - B[None,:,:], A[:,None,:] - B[None,:,:])

下面是另一种执行方法:

(a-b)^2=a^2+b^2-2ab

前两个术语为,第三个术语为
dot product
-

import numpy as np

np.einsum('ij,ij->i',A,A)[:,None] + np.einsum('ij,ij->i',B,B) - 2*np.dot(A,B.T)
运行时测试

接近-

def loopy_app(A,B):
    m,n = A.shape[0], B.shape[0]
    out = np.empty((m,n))
    for i,a in enumerate(A):
       out[i] = np.sum((a - B)**2,1)
    return out

def broadcasting_app(A,B):
    return ((A[:,np.newaxis,:] - B)**2).sum(-1)

# @Paul Panzer's soln
def outer_sum_dot_app(A,B):
    return np.add.outer((A*A).sum(axis=-1), (B*B).sum(axis=-1)) - 2*np.dot(A,B.T)

# @Daniel Forsman's soln
def einsum_all_app(A,B):
    return np.einsum('ijk,ijk->ij', A[:,None,:] - B[None,:,:], \
                                        A[:,None,:] - B[None,:,:])

# Proposed in this post
def outer_einsum_dot_app(A,B):
    return np.einsum('ij,ij->i',A,A)[:,None] + np.einsum('ij,ij->i',B,B) - \
                                                            2*np.dot(A,B.T)
时间安排-

In [51]: A = np.random.randn(1000,100)
    ...: B = np.random.randn(1000,100)
    ...: 

In [52]: %timeit loopy_app(A,B)
    ...: %timeit broadcasting_app(A,B)
    ...: %timeit outer_sum_dot_app(A,B)
    ...: %timeit einsum_all_app(A,B)
    ...: %timeit outer_einsum_dot_app(A,B)
    ...: 
10 loops, best of 3: 136 ms per loop
1 loops, best of 3: 302 ms per loop
100 loops, best of 3: 8.51 ms per loop
1 loops, best of 3: 341 ms per loop
100 loops, best of 3: 8.38 ms per loop

与@paul panzer类似,计算任意维数组两两差异的一般方法如下:

设v是大小为(n,d)的NumPy数组:

为了更好地了解正在发生的事情,想象一下每一个d维的v行都像一根旗杆一样被支撑起来,并被来回复制。现在,当你进行分量相减时,你得到的是每一个成对的组合

__

还有,它以成对方式计算度量

from scipy.spatial.distance import pdist, squareform
pairwise_L2_norms = squareform(pdist(v))

哈我击败了迪瓦卡,得到了
einsum
答案!当然,如果你想要更快的解决方案,我的方法似乎是错误的@丹尼尔弗斯曼:你只需要更多的练习,你最终会达到目的的!:)考虑到我目前的代码堆有多少依赖于快速计算笛卡尔距离,这个代数技巧非常有用,我不太介意:)
from scipy.spatial.distance import pdist, squareform
pairwise_L2_norms = squareform(pdist(v))