Python

时间:2017-07-30 18:11:19

标签: python arrays numpy optimization correlation

给定一组离散位置(例如"网站")以某些分类方式成对相关(例如一般接近)并包含本地级别数据(例如人口规模),我希望有效地计算以相同关系为特征的成对位置上的本地级数据之间的平均相关系数。

例如,我假设了100个站点并使用值1到25随机化它们的成对关系,产生三角矩阵relations

import numpy as np

sites = 100
categ = 25

relations = np.random.randint(low=1, high=categ+1, size=(sites, sites))
relations = np.triu(relations) # set relation_ij = relation_ji
np.fill_diagonal(relations, 0) # ignore self-relation

我在每个站点上还有5000个模拟结果的重复:

sims = 5000
res = np.round(np.random.rand(sites, sims),1)

为了计算每个特定关系类别的平均成对相关性,我首先计算每个关系类别i每个唯一网站对的模拟结果rho[j]之间的相关系数res { {1}},然后使用关系j

获取所有可能对的平均值
i

尽管这个脚本有效,但是一旦我增加rho_list = np.ones(categ)*99 for i in range(1, categ+1): idr = np.transpose(np.where(relations == i)) # pairwise site indices of the same relation category comp = np.vstack([res[idr[:,0]].ravel(), res[idr[:,1]].ravel()]) # pairwise comparisons of simulation results from the same relation category comp_uniq = np.reshape(comp.T, (len(idr), res.shape[1], -1)) # reshape above into pairwise comparisons of simulation results between unique site pairs rho = np.ones(len(idr))*99 # correlation coefficients of all unique site pairs of current relation category for j in range(len(idr)): # loop through unique site pairs comp_uniq_s = comp_uniq[j][np.all(comp_uniq!=0, axis=2)[j]].T # shorten comparisons by removing pairs with zero-valued result rho[j] = np.corrcoef(comp_uniq_s[0], comp_uniq_s[1])[0,1] rho_list[i-1] = np.nanmean(rho) ,那么整个计算可能需要6个多小时才能完成,这让我质疑我对数组函数的使用。这种糟糕表现的原因是什么?我该如何优化算法?

1 个答案:

答案 0 :(得分:6)

我们可以使用j迭代器和一些masking向内部循环进行向量化,以处理在该循环的每次迭代中处理的数据的粗糙性。我们也可以放慢np.corrcoef(受this post启发)。此外,我们可以在外循环开始时优化几个步骤,特别是堆叠步骤,这可能是瓶颈。

因此,完整的代码将减少到这样的东西 -

for i in range(1, categ+1):
    r,c = np.where(relations==i)

    A = res[r]
    B = res[c]

    mask0 = ~((A!=0) & (B!=0))
    A[mask0] = 0
    B[mask0] = 0

    count = mask0.shape[-1] - mask0.sum(-1,keepdims=1)
    A_mA = A - A.sum(-1, keepdims=1)/count
    B_mB = B - B.sum(-1, keepdims=1)/count

    A_mA[mask0] = 0
    B_mB[mask0] = 0

    ssA = np.einsum('ij,ij->i',A_mA, A_mA)
    ssB = np.einsum('ij,ij->i',B_mB, B_mB)
    rho = np.einsum('ij,ij->i',A_mA, B_mB)/np.sqrt(ssA*ssB)

    rho_list[i-1] = np.nanmean(rho)

运行时测试

案例#1:对于给定的样本数据,其中sites = 100

In [381]: %timeit loopy_app()
1 loop, best of 3: 7.45 s per loop

In [382]: %timeit vectorized_app()
1 loop, best of 3: 479 ms per loop

15x+ 加速。

案例#2:使用sites = 200

In [387]: %timeit loopy_app()
1 loop, best of 3: 1min 56s per loop

In [388]: %timeit vectorized_app()
1 loop, best of 3: 1.86 s per loop

In [390]: 116/1.86
Out[390]: 62.36559139784946

62x+ 加速。

案例#3:最后使用sites = 400

In [392]: %timeit vectorized_app()
1 loop, best of 3: 7.64 s per loop

这在OP结束时用6hrs+进行循环播放。

从时间上看,很明显,对内部循环进行矢量化是获得大型sites显着加速的关键。