【问题标题】:Minimum distance between two points of multiple arrays多个阵列的两点之间的最小距离
【发布时间】:2021-10-09 16:05:18
【问题描述】:

我有多个形状数组 (12,3)。假设它们是 Q、V、R、S。这些数组实际上是 xyz 坐标(省略了原子符号)对于一个样本,我试图计算 Q 中的粒子与 V 数组中的粒子之间的最小距离。除此之外,我想要最小距离的 xyz 分量。 对于输出距离和 xyz 分量,我创建了两个数组并将距离和 xyz 分量值附加到它们。这是我的代码,

import numpy as np
xyz_QV=[]; r_QV=[]

def min_distance(A,B):
        diff=10**20
        for i in A:
            for j in B:
                k=np.linalg.norm(i-j)
                if  k < diff:
                    diff = k
                    #xyz_QV.append(i-j)
        xyz_QV.append(i-j)
        return diff
    print('r_min=',min_distance(V,Q))
    r_QV.append(min_distance(V,Q))
xyz_QV=np.array(xyz_QV)
print(xyz_QV)
print(r_QV)

我手动检查了距离数组是否正常,但我得到的 xyz 组件是错误的,我得到的不是一排 xyz,而是两行。 请帮忙。

另外,如果我想为QS和RS找到相同的,我需要在函数中修改什么?

提前致谢..

编辑 1:提供示例数组

Q = np.array([[ 0.71264532,  1.1209957 ,  0.06054078],
              [ 1.35784165,  1.98639917,  0.12773717],
              [ 1.25823573, -0.1592519 ,  0.12423352],
              [ 2.32495428, -0.28709988,  0.24674303],
              [ 0.42688496, -1.27452666,  0.04265043],
              [ 0.85044465, -2.26843268,  0.09474995]])

R = np.array([[ 0.42688496, -1.27452666,  0.04265043],
              [ 0.85044465, -2.26843268,  0.09474995],
              [-0.94957784, -1.11007406, -0.1003136 ],
              [-1.5944557 , -1.9762737 , -0.16371348],
              [-1.49552564,  0.17105056, -0.16154602],
              [-2.56378279,  0.29922115, -0.27370311]])

V = np.array([[ 1.82750755,  1.11126079,  3.25188149],
              [ 2.47235268,  1.97744454,  3.31563221],
              [ 2.37346068, -0.16989031,  3.31340122],
              [ 3.44166756, -0.29803736,  3.42614544],
              [ 1.5418112 , -1.28549041,  3.23475079],
              [ 1.9648929 , -2.27984284,  3.28440446]])

我得到的输出是

r_min= 3.069280599179459
[[ 1.11444826 -0.01141016  3.18965451]
 [ 1.11444826 -0.01141016  3.18965451]]
[3.069280599179459]

【问题讨论】:

  • 首先,不要在 Python 中使用分号。其次,请更正您的缩进,以便清楚函数定义的内部和外部。最后,请提供示例输入、预期输出、实际输出(如果有)以及任何错误或回溯的全文
  • 另外,请尽量使用比单个字母更具描述性的变量名称。
  • 你能澄清一下你想找到哪些对的距离吗?都是 Q、V、R、S 对吗?
  • @MattDMo 我没有收到错误。好的,让我分享两个示例输入和输出。
  • @ddejohn 我提供了 Q、R 和 V 数组。我可以得到 Q-V 、 V-R 和 Q-R 之间的最小距离吗?

标签: python python-3.x numpy


【解决方案1】:

使用 scipy 的“干净”解决方案

您可以使用 scipy 的 spatial.distance 模块获取两个数组之间的所有成对距离:

# Standard library
from typing import Tuple
from itertools import combinations as combs

# Third party
import numpy as np
from scipy.spatial.distance import cdist


def min_dist_and_xyz(A: np.ndarray, B: np.ndarray) -> dict:
    """
    Calculates the distance between all possible pairs of points
    from `A` and `B`, returning the smallest of them.
    """
    AB_dist = cdist(A, B)
    A_i, B_i = np.unravel_index(AB_dist.argmin(), AB_dist.shape)
    return {"min_dist": AB_dist[A_i, B_i], "xyz": np.abs(A[A_i] - B[B_i])}


def all_min_dists(arrs: Tuple[np.ndarray], labels: str) -> dict:
    """
    Calculates the minimum distance out of all pairwise combinations
    of arrays in `arrs`.
    """
    distances = {}
    for (x_lbl, x), (y_lbl, y) in combs(dict(zip(labels, arrs)).items(), 2):
        distances[f"{x_lbl}{y_lbl}"] = min_dist_and_xyz(x, y)
    return distances

用法(为了显示数据,我分别对输出进行了四舍五入):

>>> Q, V, R, S = np.random.random((4, 12, 3))
>>> all_min_dists(arrs=(Q, V, R, S), labels="QVRS")
{'QV': {'min_dist': 0.14817, 'xyz': array([0.13026, 0.04566, 0.05387])},
 'QR': {'min_dist': 0.06003, 'xyz': array([0.01459, 0.05818, 0.00255])},
 'QS': {'min_dist': 0.04912, 'xyz': array([0.03048, 0.02257, 0.03121])},
 'VR': {'min_dist': 0.18245, 'xyz': array([0.12439, 0.06326, 0.11754])},
 'VS': {'min_dist': 0.14755, 'xyz': array([0.02883, 0.12283, 0.0765 ])},
 'RS': {'min_dist': 0.13614, 'xyz': array([0.12737, 0.04761, 0.00664])}}

仅使用 numpy 的“纯”解决方案

通常一个公平的假设是,那些使用 numpy 的人也安装了 scipy,但如果由于某种原因您没有安装 scipy,和/或不想使用 scipy,这里有一个仅使用 numpy 的解决方案并需要np.einsum()的优势。

这通过获取任何两个数组中所有可能的点对之间的平方距离之和,然后只对这些值中的最小值计算一次平方根,以返回最小距离(Alain 提供的见解) T)。

def min_dist_and_xyz(A, B):
    diff = A - B[:, None]
    sq_dist = np.einsum("ijk,ijk->ji", diff, diff)
    ai, bi = np.unravel_index(sq_dist.argmin(), sq_dist.shape)
    return {"min_dist": np.sqrt(sq_dist[ai, bi]), "xyz": np.abs(A[ai] - B[bi])}

令人惊讶的是,在大型数组的情况下,这种“优化”实际上并没有产生比 scipy 的 cdist 更快的解决方案:

In [5]: Q, V = np.random.random((2, 100, 3))  # Q and V are (100, 3)

In [6]: %timeit with_einsum(Q, V)
136 µs ± 1.32 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [7]: %timeit with_cdist(Q, V)
58 µs ± 646 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

【讨论】:

  • @PrasantaBandyopadhyay 我的回答有什么不足之处?
【解决方案2】:

IIUC,您正在尝试找到 Q 中的每个粒子与 V 中的每个粒子之间的最小距离。除此之外,您还想获得这些粒子。

这是您尝试做的完全矢量化的方式。

  1. 你可以用同样的方法np.linalg.norm(particle1-particle2)
  2. 您可以在 Q 和 V 矩阵中创建 2 个加法轴,这样您就可以广播您想要在 Q 和 V 中的粒子之间获得叉积的操作。
Q    -> 12  , None , 3
V    -> None, 12   , 3
---------------------
Q-V  -> 12  , 12   , 3
---------------------
norm -> 12  , 12
---------------------
min  -> 1

  1. 这导致 12、12、3 矩阵可以用np.linalg.normaxis=-1 减少为 12、12 矩阵。这是 Q 中每 12 个粒子与 V 中每 12 个粒子之间的距离。

  2. 现在,只需在这个矩阵中找到最小值

  3. 要获取导致此最小距离的粒子在 Q 中的索引和在 V 中的索引,您可以使用 np.unravel_index 获取它们,然后在发布后索引 Q 和 V。

#Creating 2 dummy lists of particles
Q = np.random.random((12,3))
V = np.random.random((12,3))

#distance between every particle in Q vs V (resuting in 12,12 matrix)
distances = np.linalg.norm(Q[:,None,:]-V[None,:,:], axis=-1) 

#Get index position for Q and V particles with min dist
idx = np.unravel_index(distances.argmin(), distances.shape)

#Find particle Q and particle V which result in min distance
particleQ, particleV = Q[idx[0]], V[idx[1]]

print(particleQ)
print(particleV)
[0.60751186 0.93177959 0.23249369]
[0.64406579 0.91601754 0.27724177]

证明这些粒子之间的距离最小。

print('Minimum distance between Q and V particles:', distances.min())
print('Distance between the above calculated particleQ & particleV', np.linalg.norm(particleQ-particleV))
Minimum distance between Q and V particles: 0.11540521863305497
Distance between the above calculated particleQ & particleV 0.11540521863305497

作为一个函数

def find_dist(Q, V):
    #distance between every particle in Q vs V (resuting in 12,12 matrix)
    distances = np.linalg.norm(Q[:,None,:]-V[None,:,:], axis=-1) 
    idx = np.unravel_index(distances.argmin(), distances.shape)
    particleQ, particleV = Q[idx[0]], V[idx[1]]
    
    return distances.min(), (particleQ, particleV), distances

QR_min, QR_particles, _ = find_dist(Q,R)
QV_min, QV_particles, _ = find_dist(Q,V)
RV_min, RV_particles, _ = find_dist(R,V)

print('Arrays Q, R')
print('Min distance:', QR_min)
print('Particle with min distance Q', QR_particles[0])
print('Particle with min distance R', QR_particles[1])
print('')
print('Arrays Q, V')
print('Min distance:', QV_min)
print('Particle with min distance Q', QV_particles[0])
print('Particle with min distance V', QV_particles[1])
print('')
print('Arrays R, V')
print('Min distance:', RV_min)
print('Particle with min distance R', RV_particles[0])
print('Particle with min distance V', RV_particles[1])
print('')
Arrays Q, R
Min distance: 0.0
Particle with min distance Q [ 0.42688496 -1.27452666  0.04265043]
Particle with min distance R [ 0.42688496 -1.27452666  0.04265043]

Arrays Q, V
Min distance: 3.0692806011237583
Particle with min distance Q [ 2.32495428 -0.28709988  0.24674303]
Particle with min distance V [ 2.37346068 -0.16989031  3.31340122]

Arrays R, V
Min distance: 3.3621077448250167
Particle with min distance R [ 0.85044465 -2.26843268  0.09474995]
Particle with min distance V [ 1.5418112  -1.28549041  3.23475079]

【讨论】:

  • @Akshay Sehgal,感谢您的回答。是的,您所说的关于查找距离和创建矩阵的内容将起作用。但是,我认为与其创建一个新矩阵,不如在一个数组中找到距离和保存最短,我将需要更少的输入。
  • 当然,这将是一个好方法,但是你必须确保你有多个嵌套循环。这可以通过使解决方案完全矢量化来避免。我添加了一个可以与其他数组一起使用的函数。看看这个。
  • @PrasantaBandyopadhyay,您并没有在这种方法中创建新数组,您只是对齐数组,以便您可以广播并执行此操作,而无需 numpy 之外的任何其他库。
  • 我已经尝试过你的工作作为一个函数。它有效@Akshay。非常感谢。但是,由于我是新手,我无法理解您的某些代码。谢谢。
  • 这能回答你的问题吗?
猜你喜欢
  • 2021-10-24
  • 2021-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
  • 2020-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多