方法#1
corr2_coeff_rowwise
介绍了如何在行之间进行逐元素相关性。我们可以将其简化为两列之间的逐元素相关操作。因此,我们得到一个使用corr2_coeff_rowwise
的循环。接下来,我们会尝试将其向量化,并发现其中有一些可以向量化的部分:
- 使用
mean
获取平均值。这可以通过使用统一过滤器进行向量化。
- 接下来是获取这些平均值与输入数组的滑动元素之间的差异。为了将其转换为向量化版本,我们将利用
broadcasting
。
剩下的步骤与获取pearsonr
的第一个输出相同。
要获得第二个输出,我们需要回到源代码
。这应该很简单,考虑到第一个系数输出。
因此,考虑到这些,我们最终会得到类似于以下内容的代码 -
import scipy.special as special
from scipy.ndimage import uniform_filter
def sliding_corr1(a,b,W):
am = uniform_filter(a.astype(float),W)
bm = uniform_filter(b.astype(float),W)
amc = am[W//2:-W//2+1]
bmc = bm[W//2:-W//2+1]
da = a[:,None]-amc
db = b[:,None]-bmc
m,n = da.shape
mask1 = np.arange(m)[:,None] >= np.arange(n)
mask2 = np.arange(m)[:,None] < np.arange(n)+W
mask = mask1 & mask2
dam = (da*mask)
dbm = (db*mask)
ssAs = np.einsum('ij,ij->j',dam,dam)
ssBs = np.einsum('ij,ij->j',dbm,dbm)
D = np.einsum('ij,ij->j',dam,dbm)
coeff = D/np.sqrt(ssAs*ssBs)
n = W
ab = n/2 - 1
pval = 2*special.btdtr(ab, ab, 0.5*(1 - abs(np.float64(coeff))))
return coeff,pval
因此,为了从Pandas系列的输入中获取最终输出 -
out = sliding_corr1(df['x'].to_numpy(copy=False),df['y'].to_numpy(copy=False),50)
第二种方法
与"第一种方法"非常相似,但我们将使用numba
提高内存效率,以替换前一个方法中的步骤#2。
from numba import njit
import math
@njit(parallel=True)
def sliding_corr2_coeff(a,b,amc,bmc):
L = len(a)-W+1
out00 = np.empty(L)
for i in range(L):
out_a = 0
out_b = 0
out_D = 0
for j in range(W):
d_a = a[i+j]-amc[i]
d_b = b[i+j]-bmc[i]
out_D += d_a*d_b
out_a += d_a**2
out_b += d_b**2
out00[i] = out_D/math.sqrt(out_a*out_b)
return out00
def sliding_corr2(a,b,W):
am = uniform_filter(a.astype(float),W)
bm = uniform_filter(b.astype(float),W)
amc = am[W//2:-W//2+1]
bmc = bm[W//2:-W//2+1]
coeff = sliding_corr2_coeff(a,b,amc,bmc)
ab = W/2 - 1
pval = 2*special.btdtr(ab, ab, 0.5*(1 - abs(np.float64(coeff))))
return coeff,pval
方法三
与前一个方法非常相似,只是我们将所有系数工作都推到了numba
中-
@njit(parallel=True)
def sliding_corr3_coeff(a,b,W):
L = len(a)-W+1
out00 = np.empty(L)
for i in range(L):
a_mean = 0.0
b_mean = 0.0
for j in range(W):
a_mean += a[i+j]
b_mean += b[i+j]
a_mean /= W
b_mean /= W
out_a = 0
out_b = 0
out_D = 0
for j in range(W):
d_a = a[i+j]-a_mean
d_b = b[i+j]-b_mean
out_D += d_a*d_b
out_a += d_a*d_a
out_b += d_b*d_b
out00[i] = out_D/math.sqrt(out_a*out_b)
return out00
def sliding_corr3(a,b,W):
coeff = sliding_corr3_coeff(a,b,W)
ab = W/2 - 1
pval = 2*special.btdtr(ab, ab, 0.5*(1 - np.abs(coeff)))
return coeff,pval
时间 -
In [181]: df = pd.DataFrame({'x': np.random.rand(10000), 'y': np.random.rand(10000)})
In [182]: %timeit sliding_corr2(df['x'].to_numpy(copy=False),df['y'].to_numpy(copy=False),50)
100 loops, best of 3: 5.05 ms per loop
In [183]: %timeit sliding_corr3(df['x'].to_numpy(copy=False),df['y'].to_numpy(copy=False),50)
100 loops, best of 3: 5.51 ms per loop
注意:
apply
在底层只是一个循环,因此在DataFrame
变得很大时会遇到性能问题。我希望能够找到一个numpy
解决方案,或者可能对.corr
本身进行一些修改。 - mch56