Performance 高效的柱状相关系数计算

Performance 高效的柱状相关系数计算,performance,numpy,correlation,pearson-correlation,Performance,Numpy,Correlation,Pearson Correlation,原始问题 我将大小为n的p行与大小为n×m的矩阵O的每一列相关联。我编写了以下代码: 将numpy导入为np def ColumnWiseCorrcoef(O,P): n=P.size DO=O-(np.和(O,0)/np.双(n)) DP=P-(np.和(P)/np.双(n)) 返回np.dot(DP,DO)/np.sqrt(np.sum(DO**2,0)*np.sum(DP**2)) 它比单纯的方法更有效: def columnwiseCorcoefnaive(O,P): 返回np.cor

原始问题

我将大小为n的p行与大小为n×m的矩阵O的每一列相关联。我编写了以下代码:

将numpy导入为np
def ColumnWiseCorrcoef(O,P):
n=P.size
DO=O-(np.和(O,0)/np.双(n))
DP=P-(np.和(P)/np.双(n))
返回np.dot(DP,DO)/np.sqrt(np.sum(DO**2,0)*np.sum(DP**2))
它比单纯的方法更有效:

def columnwiseCorcoefnaive(O,P):
返回np.corrcof(P,O.T)[0,1:O[0].size+1]
以下是我在Intel core上使用numpy-1.7.1-MKL的计时:

O=np.重塑(np.随机.兰德(100000),(1000100))
P=np.rand.rand(1000)
%timeit-n 1000 A=列式相关系数(O,P)
1000圈,最好3圈:每圈787美元
%timeit-n 1000 B=列型相关系数(O,P)
1000个回路,最佳3个:每个回路2.96毫秒
现在的问题是:你能为这个问题推荐一个更快的代码版本吗?再挤出20%就好了

2017年5月更新

过了一段时间,我回到这个问题上来,重新运行并扩展了任务和测试

  • 使用einsum,我将代码扩展到p不是一行而是一个矩阵的情况。因此,任务是将O的所有列与P的所有列关联起来

  • 出于对如何用科学计算中常用的不同语言解决同一问题的好奇,我(在其他人的帮助下)在MATLAB、Julia和R中实现了它。MATLAB和Julia是最快的,他们有一个专用的例程来计算列相关。R也有专门的程序,但速度最慢

  • 在当前版本的numpy(Anaconda的1.12.1)中,einsum仍然胜过我使用的专用功能


  • 所有的脚本和计时都可以在上找到。

    我们可以介绍
    np.einsum
    ;但是,您的差异可能取决于您的编译以及它是否使用SSE2。替换求和操作的额外
    einsum
    调用可能看起来无关,但是numpy ufuncs直到numpy 1.8才使用SSE2,而
    einsum
    使用SSE2,我们可以避免一些
    if
    语句

    在带有intel mkl blas的opteron内核上运行此命令时,我会得到一个奇怪的结果,因为我预计
    dot
    调用会占用大部分时间

    def newColumnWiseCorrcoef(O,P):
    n=P.size
    DO=O-(名词性宾语('ij->j',O)/名词性双宾语(n))
    P-=(np.einsum('i->',P)/np.double(n))
    tmp=np.einsum('ij,ij->j',DO,DO)
    tmp*=np.einsum('i,i->',P,P)#Dot或vdot实际上变化不大。
    返回np.dot(P,DO)/np.sqrt(tmp)
    O=np.重塑(np.随机.兰德(100000),(1000100))
    P=np.rand.rand(1000)
    old=列式相关系数(O,P)
    new=newColumnWiseCorrcoef(O,P)
    np.allclose(旧、新)
    真的
    %timeit ColumnWiseCorrcoef(O,P)
    100圈,最佳3圈:每圈1.52ms
    %timeit newColumnWiseCorrcoef(O,P)
    1000个回路,最好3个:每个回路518us
    
    再次在带有“英特尔mkl”的英特尔系统上运行此功能,我得到了更合理/更期望的结果:

    %timeit ColumnWiseCorrcoef(O,P)
    1000个回路,最好为3个:每个回路524µs
    %timeit newColumnWiseCorrcoef(O,P)
    1000个回路,最好为3个:每个回路354µs
    
    再次在intel机器上使用更大的功能:

    O=np.random.rand(1E5,1E3)
    P=np.random.rand(1E5)
    %timeit ColumnWiseCorrcoef(O,P)
    1圈,最好3圈:每圈1.33秒
    %timeit newColumnWiseCorrcoef(O,P)
    1个回路,最佳3个:每个回路791毫秒
    
    可以更改
    O
    P
    吗?此外,这些方法严重依赖于链接到numpy的BLAS库,如果您还没有,我建议您使用某种优化的BLAS。@Ophion,
    P
    可以更改,
    O
    不能更改。我使用的是从中链接到英特尔MKL的numpy。现在无法测试它,但如果您尝试
    np.einsum('ij,ij->j',DO,DO)
    而不是
    np.sum(DO**2,0)
    ,并且
    np.dot(DP,DP)
    而不是
    np.sum(DP****.2)
    ,则是
    tmp=。。。;tmp*=…
    应该节省时间吗?我见过其他人也这么做,我不认为这比
    tmp=…*。我发现这个解决方案非常有用,我想知道是否有一种方法可以扩展它来计算回归残差,而不会在速度方面牺牲太多。