快速,优雅的方式来计算经验/样本协方差

时间:2014-05-29 21:28:03

标签: python numpy covariogram

有没有人知道计算经验/样本协方差图的好方法,如果可能的话在Python中?

这是book的屏幕截图,其中包含对covariagram的良好定义:

enter image description here

如果我理解正确,对于给定的滞后/宽度h,我应该得到所有由h(或小于h)分隔的点对,乘以其值并且对于每个这些值点,计算其平均值,在这种情况下,定义为m(x_i)。但是,根据m(x_ {i})的定义,如果我想计算m(x1),我需要获得距离x1的距离h内的值的平均值。这看起来像是一个非常密集的计算。

首先,我能正确理解这一点吗?如果是这样,假设二维空间,计算这个的好方法是什么?我试图用Python编写这个代码(使用numpy和pandas),但是它需要几秒钟而我甚至不确定它是否正确,这就是为什么我不会在这里发布代码。这是一个非常天真的实现的另一种尝试:

from scipy.spatial.distance import pdist, squareform
distances = squareform(pdist(np.array(coordinates))) # coordinates is a nx2 array
z = np.array(z) # z are the values
cutoff = np.max(distances)/3.0 # somewhat arbitrary cutoff
width = cutoff/15.0
widths = np.arange(0, cutoff + width, width)
Z = []
Cov = []

for w in np.arange(len(widths)-1): # for each width
    # for each pairwise distance
    for i in np.arange(distances.shape[0]): 
        for j in np.arange(distances.shape[1]): 
            if distances[i, j] <= widths[w+1] and distances[i, j] > widths[w]:
                m1 = []
                m2 = []
                # when a distance is within a given width, calculate the means of
                # the points involved
                for x in np.arange(distances.shape[1]):
                    if distances[i,x] <= widths[w+1] and distances[i, x] > widths[w]:
                        m1.append(z[x])

                for y in np.arange(distances.shape[1]):
                    if distances[j,y] <= widths[w+1] and distances[j, y] > widths[w]:
                        m2.append(z[y])

                mean_m1 = np.array(m1).mean() 
                mean_m2 = np.array(m2).mean()
                Z.append(z[i]*z[j] - mean_m1*mean_m2)
    Z_mean = np.array(Z).mean() # calculate covariogram for width w
    Cov.append(Z_mean) # collect covariances for all widths

但是,现在我已经确认我的代码中存在错误。我知道因为我使用变差函数来计算协方差图(协方差(h)=协方差(0) - 变异函数(h)),我得到了不同的情节:

enter image description here

它应该看起来像这样:

enter image description here

最后,如果你知道一个Python / R / MATLAB库来计算经验协方差图,请告诉我。至少,这样我可以验证我做了什么。

1 个答案:

答案 0 :(得分:5)

可以使用scipy.cov,但如果直接进行计算(这很容易),有更多方法可以加快速度。

首先,制作一些具有一定空间相关性的假数据。我将首先进行空间相关,然后使用使用此生成的随机数据点,根据底层地图定位数据,并采用底层地图的值。

编辑1:
我更改了数据点生成器,因此位置纯粹是随机的,但z值与空间映射成比例。并且,我更改了地图,以便左右两侧相对于彼此移动以在大h处创建负相关。

from numpy import *
import random
import matplotlib.pyplot as plt

S = 1000
N = 900
# first, make some fake data, with correlations on two spatial scales
#     density map
x = linspace(0, 2*pi, S)
sx = sin(3*x)*sin(10*x)
density = .8* abs(outer(sx, sx))
density[:,:S//2] += .2
#     make a point cloud motivated by this density
random.seed(10)  # so this can be repeated
points = []
while len(points)<N:
    v, ix, iy = random.random(), random.randint(0,S-1), random.randint(0,S-1)
    if True: #v<density[ix,iy]:
        points.append([ix, iy, density[ix,iy]])
locations = array(points).transpose()
print locations.shape
plt.imshow(density, alpha=.3, origin='lower')
plt.plot(locations[1,:], locations[0,:], '.k')
plt.xlim((0,S))
plt.ylim((0,S))
plt.show()
#     build these into the main data: all pairs into distances and z0 z1 values
L = locations
m = array([[math.sqrt((L[0,i]-L[0,j])**2+(L[1,i]-L[1,j])**2), L[2,i], L[2,j]] 
                         for i in range(N) for j in range(N) if i>j])

给出了:

enter image description here

以上只是模拟数据,我没有尝试优化它的生产等。我认为这是OP启动的地方,下面的任务,因为数据已经存在于实际情况中


现在计算&#34;协方差&#34; (这比生成假数据容易得多,顺便说一句)。这里的想法是按h对所有对和关联值进行排序,然后使用ihvals对这些值进行索引。也就是说,总结到索引ihval是等式中N(h)的总和,因为这包括h s低于期望值的所有对。

编辑2:
正如下面的评论中所建议的,N(h)现在只是h-dhh之间的对,而不是0h之间的所有对(其中) dhh的间距 - ihvals中的值 - 即,下面使用了S / 1000。

# now do the real calculations for the covariogram
#    sort by h and give clear names
i = argsort(m[:,0])  # h sorting
h = m[i,0]
zh = m[i,1]
zsh = m[i,2]
zz = zh*zsh

hvals = linspace(0,S,1000)  # the values of h to use (S should be in the units of distance, here I just used ints)
ihvals = searchsorted(h, hvals)
result = []
for i, ihval in enumerate(ihvals[1:]):
    start, stop = ihvals[i-1], ihval
    N = stop-start
    if N>0:
        mnh = sum(zh[start:stop])/N
        mph = sum(zsh[start:stop])/N
        szz = sum(zz[start:stop])/N
        C = szz-mnh*mph
        result.append([h[ihval], C])
result = array(result)
plt.plot(result[:,0], result[:,1])
plt.grid()
plt.show()

enter image description here

这看起来对我来说是合理的,因为人们可以看到h值预期的颠簸或低谷,但我还没有仔细检查。

此处scipy.cov的主要加速是,可以预先计算所有产品zz。否则,对于每个新zh,我们会将zshcov投放到h,并重新计算所有产品。通过在每个时间步ihvals[n-1]ihvals[n]n进行部分求和,可以加快计算速度,但我怀疑这是必要的。