【问题标题】:Optimized computation of pairwise correlations in PythonPython中成对相关性的优化计算
【发布时间】:2017-07-30 18:11:19
【问题描述】:

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

例如,我假设有 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 计算每个唯一站点对j 的模拟结果res 之间的相关系数rho[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)

虽然这个脚本有效,但是一旦我增加sites = 400,那么整个计算可能需要6个多小时才能完成,这让我质疑我对数组函数的使用。表现不佳的原因是什么?以及如何优化算法?

【问题讨论】:

  • @Divakar 谢谢。我花了几天时间研究这些链接,这教会了我很多关于使用einsum 对过程进行矢量化的知识。但是,我仍然看不到如何将它们应用于我的问题,因为我需要在从中删除零值结果后计算数组对的相关系数(参见上面的脚本comp_uniq_s = comp_uniq[j][np.all(comp_uniq!=0, axis=2)[j]].T)。
  • np.corrcoef(comp_uniq_s[0], comp_uniq_s[1])[0,1]np.corrcoef(comp_uniq[j][0], comp_uniq[j][1])[0,1] 的值不同。这是故意的吗?您的 cmets 看起来好像删除零值只是为了减少计算,而不是改变结果。
  • 我能找到的最大的节省时间是将np.all(comp_uniq!=0, axis=2)[j]更改为np.all(comp_uniq[j].astype(bool), axis=-1),所以你1)不要在每次迭代时创建大布尔矩阵,2)在无用的情况下节省一些时间([0]).asytpe(bool) = ([False])时的比较。

标签: python arrays numpy optimization correlation


【解决方案1】:

我们可以使用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:在给定的样本数据上,站点 = 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

6hrs+ 在 OP 结束时使用了他们的循环方法。

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

【讨论】:

  • 请不要对我的(现在已删除)答案进行基准测试。它是 。 . .一旦我有机会检查时间,真的很糟糕。
  • @Divakar 我研究了您的代码,老实说,仍然没有完全理解它,但对它的运行效果/准确性感到惊讶。例如:您使用的 masking 似乎只是将某些对替换为 0 值,然后从这些 0 膨胀数组中计算相关性。然而,给定a=[1,2,3,0,4,0]b=[0,4,0,3,2,1]np.corrcoef(a,b)[0,1] != 零膨胀np.corrcoef(a0,b0)[0,1],其中a0 = [0,2,0,0,4,0]b0 = [0,4,0,0,2,0]。我在这里误会了什么?
  • @Divakar 我还注意到,在用于计算 Pearson 相关系数的脚本的 older version 中,它使用的公式的 4 部分分解与此处和 here 略有不同。您对如何以数字方式解构计算有偏好吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-14
  • 2014-04-29
  • 2018-06-11
  • 2023-03-27
  • 1970-01-01
相关资源
最近更新 更多