计算周期性2D晶格中的成对空间距离

时间:2017-07-15 17:02:39

标签: python arrays numpy scipy euclidean-distance

我一直在寻找一种解决方案来计算周期性/包裹边缘晶格上离散的等距方形点之间的空间距离。例如,在9x9点阵上将相邻站点设置为2个单位:

m = 9
lattice = np.zeros((m,m))
for i in arange(0,6+1,3):
    for j in arange(0,6+1,3):
        lattice[i,j]=1

In  []: lattice
Out []: array([[ 1.,  0.,  0.,  1.,  0.,  0.,  1.,  0.,  0.],
               [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
               [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
               [ 1.,  0.,  0.,  1.,  0.,  0.,  1.,  0.,  0.],
               [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
               [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
               [ 1.,  0.,  0.,  1.,  0.,  0.,  1.,  0.,  0.],
               [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
               [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])

In  []: plt.imshow(lattice, origin='origin', cmap='binary', interpolation='none')

enter image description here 其中站点用索引注释,红色箭头表示从站点0(2个单位)的等距离,我如何创建一个数组sdist,它给出了站点之间最短的成对距离,即sdist[7,1]=2,{ {1}}。通常的工具sdist[2,6] = 2.8284似乎不适用于此。

2 个答案:

答案 0 :(得分:3)

由于distance.cdist接受任意度量(以可调用方式提供),因此问题只在于为度量标准编写函数。

如果它是点p和q之间的包裹距离,那将是

def wrapped_euclidean_points(p, q):
    diff = np.abs(p - q)
    return np.linalg.norm(np.minimum(diff, m - diff))

其中m是晶格的大小(遗憾的是cdist不支持将额外的参数传递给距离函数,因此必须从较大的范围中获取m。)< / p>

但是在你的情况下,你希望边长1的平方之间的距离最小。这意味着在计算包裹差矢量np.minimum(diff, m - diff)后,我们可以将每个分量减少1,因为,例如,两个正方形的点之间的最小x差异比这些正方形的中心之间的x差异小1。当然,这个减法不应该产生差异。 因此功能变为

def wrapped_euclidean_squares(p, q):
    diff = np.abs(p - q)
    return np.linalg.norm(np.clip(np.minimum(diff, m - diff) - 1, 0, None))

其中clip处理两个中心具有相同x坐标或相同y坐标的情况。

剩下的就是两行(不计算from scipy.spatial import distance):

coords = np.vstack(np.nonzero(lattice)).T
dist = distance.cdist(coords, coords, metric=wrapped_euclidean_squares)

您的示例的输出:

[[ 0.          2.          2.          2.          2.82842712  2.82842712       2.          2.82842712  2.82842712]
 [ 2.          0.          2.          2.82842712  2.          2.82842712       2.82842712  2.          2.82842712]
 [ 2.          2.          0.          2.82842712  2.82842712  2.       2.82842712  2.82842712  2.        ]
 [ 2.          2.82842712  2.82842712  0.          2.          2.          2.       2.82842712  2.82842712]
 [ 2.82842712  2.          2.82842712  2.          0.          2.       2.82842712  2.          2.82842712]
 [ 2.82842712  2.82842712  2.          2.          2.          0.       2.82842712  2.82842712  2.        ]
 [ 2.          2.82842712  2.82842712  2.          2.82842712  2.82842712       0.          2.          2.        ]
 [ 2.82842712  2.          2.82842712  2.82842712  2.          2.82842712       2.          0.          2.        ]
 [ 2.82842712  2.82842712  2.          2.82842712  2.82842712  2.          2.       2.          0.        ]]

答案 1 :(得分:2)

enter image description here

我试图理解这个请求,我的插图在这里说明了

而不是格子点的那些,我从1开始编号。

然后我增加了&#39; lattice中给出的单元格通过切片并使用np.concatenate在每个起始点阵的边界上添加半个单元格 图中的黄线表示细胞寄宿生

给定起始格点标签号,我在我的&#39; fullLaug&#39;中找到所有实例。数组和计算平方欧几里德的差异,并按它排序

根据这个例子,我采用最短的方法并绘制一条从起点1开始的线到其他每个点的最短距离实例&lt; fullLaug&#39;以及打印实际距离

import numpy as np
import matplotlib.pyplot as plt

" create lattice, lattice points nonzero, incremtning count "

m = 9
pcnt = 0
lattice = np.zeros((m, m))
for i in range(0, 6+1, 3):
    for j in range(0, 6+1, 3):
        pcnt += 1
        lattice[i, j] = pcnt # lable lattice points with count

#print(*lattice, sep='\n')

"augment lattice with duplicated halves, up/down, left/right"

def halves(a):
    n = len(a)
    return a[:(n+n%2)//2-n%2], a[(n-n%2)//2+n%2:]


rightL, leftL = halves(lattice.T)
n = len(rightL)
cornerL = np.zeros((n, n))
rightL, leftL = rightL.T, leftL.T

rightLaug = np.concatenate((cornerL, rightL, cornerL), axis=0)
leftLaug = np.concatenate((cornerL, leftL, cornerL), axis=0)

lowL, upL = halves(lattice)

centerL = np.concatenate((upL, lattice, lowL), axis=0)

fullLaug = np.concatenate((leftLaug, centerL, rightLaug), axis=1)

"plot fully agumented lattice, yellow lines are boarders of cells"

plt.imshow(fullLaug, origin='origin', cmap='jet', interpolation='none')

plt.plot((n, n), (0, 2*n+m-1), 'y', (n+m-1, n+m-1), (2*n+m-1, 0), 'y')
plt.plot((0, 2*n+m-1), (n, n), 'y', (2*n+m-1, 0), (n+m-1, n+m-1), 'y')

#print(*zip(*np.where(fullLaug == 3)))


def distsq(a, b, n):
    """takes lattice point pcnt labels a, b
    finds a indices in lattice, offests to indices in fullaug
    finds all instances of b indices in fullaug
    calcs all a-b distances
    returns sorted list of distances, indices, a-a appended to start
    """
    a = list(*zip(*np.where(lattice == a)))
    a[0] += n; a[1] += n

    bz = zip(*np.where(fullLaug == b))

    ds = [[(a[0] - b[0])**2 + (a[1] - b[1])**2, [*b]] for b in bz]

    return [[0, a]] + sorted(ds, key=lambda x: x[0])

""" plot shortest distance lines from point a to each point in lattice
    fill dist_lattice"""

dist_lattice = np.zeros((m, m))
for i in range(1, pcnt + 1):
    dps = distsq(1, i, n)[0:2]
    dist_lattice[np.where(lattice == i)] = np.sqrt(dps[1][0])
    plt.plot(*zip(dps[0][1], dps[1][1]))

np.set_printoptions(precision=4, threshold=1000)
print(dist_lattice)

[[ 0.      0.      0.      3.      0.      0.      3.      0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.      0.    ]
 [ 3.      0.      0.      4.2426  0.      0.      4.2426  0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.      0.    ]
 [ 3.      0.      0.      4.2426  0.      0.      6.7082  0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.      0.    ]]