使用numpy获取3D空间中所有物体的相对位置

3 投票
3 回答
865 浏览
提问于 2025-04-18 18:16

我想要获取一个numpy数组中所有向量对的排列组合之间的差异。

在我的具体应用中,这些向量是一些物体在三维空间中的位置向量。

所以,如果我有一个数组 r = [r1, r2, r3],其中 r1r2r3 是三维向量,我想要得到以下结果:

[[r1-r1 r1-r2 r1-r3]
 [r2-r1 r2-r2 r2-r3]
 [r3-r1 r3-r2 r3-r3]]

这里的 - 操作是逐个元素进行的。

基本上,这就像是向量的这个操作:

>>> scalars = np.arange(3)
>>> print(scalars)
[0 1 2]

>>> result = np.subtract.outer(scalars, scalars)
>>> print(result)
[[ 0 -1 -2]
 [ 1  0 -1]
 [ 2  1  0]]

不过,outer 函数似乎在减法之前会把我的向量数组压平,然后再重新调整形状。例如:

>>> vectors = np.arange(6).reshape(2, 3) # Two 3-dimensional vectors
>>> print(vectors)
[[0 1 2]
 [3 4 5]]

>>> results = np.subtract.outer(vectors, vectors)
>>> print(results.shape)
(2, 3, 2, 3)

我期待的结果是:

>>> print(result)
[[[ 0  0  0]
  [-3 -3 -3]]
 [[ 3  3  3]
  [ 0  0  0]]]

>>> print(result.shape)
(2, 2, 3)

我能在不遍历数组的情况下实现上述操作吗?

3 个回答

3

(我在这里回答我自己的问题)

这里有一种使用Numpy的方法:

import numpy as np

N = 2
r = np.arange(N * 3).reshape(N, 3)

left = np.tile(r, N).reshape(N, N, 3)
right = np.transpose(left, axes=[1, 0, 2])

result = left - right
print result

这个方法似乎适用于任何内部维度大小为3的二维数组,不过我主要是通过反复尝试得出的,所以不能百分之百确定它总是有效。

3

简短回答:

用(几乎)纯Python的方法来做向量r的“成对外减法”可以这样实现:

np.array(map(operator.sub, *zip(*product(r, r)))).reshape((2, 2, -1))

基本上,你可以使用product函数来获取列表中所有可能的项对,然后用unzip把它们分成两个单独的列表,再用map来进行减法操作operator。最后,你可以像往常一样reshape它。

逐步说明:

下面是一个逐步的示例,包括所有需要的库和中间结果的输出:

import numpy as np
from itertools import product
import operator

r = np.arange(6).reshape(2, 3)

print "Vectors:\n", r
print "Product:\n", list(product(r, r))
print "Zipped:\n", zip(*product(r, r))
print "Mapped:\n", map(operator.sub, *zip(*product(r, r)))
print "Reshaped:\n", np.array(map(operator.sub, *zip(*product(r, r)))).reshape((2, 2, -1))

输出:

Vectors:
[[0 1 2]
 [3 4 5]]
Product:
[(array([0, 1, 2]), array([0, 1, 2])), (array([0, 1, 2]), array([3, 4, 5])), (array([3, 4, 5]), array([0, 1, 2])), (array([3, 4, 5]), array([3, 4, 5]))]
Zipped:
[(array([0, 1, 2]), array([0, 1, 2]), array([3, 4, 5]), array([3, 4, 5])), (array([0, 1, 2]), array([3, 4, 5]), array([0, 1, 2]), array([3, 4, 5]))]
Mapped:
[array([0, 0, 0]), array([-3, -3, -3]), array([3, 3, 3]), array([0, 0, 0])]
Reshaped:
[[[ 0  0  0]
  [-3 -3 -3]]

 [[ 3  3  3]
  [ 0  0  0]]]

(注意,我需要交换维度23,才能创建你的示例数组。)

3

这个问题的答案几乎总是和广播机制有关:

>>> r = np.arange(6).reshape(2, 3)
>>> r[:, None] - r
array([[[ 0,  0,  0],
        [-3, -3, -3]],

       [[ 3,  3,  3],
        [ 0,  0,  0]]])

在索引中出现的那个None其实和np.newaxis是一样的,它的作用是给数组增加一个大小为1的维度。所以你实际上是在从一个形状为(2, 1, 3)的数组中减去一个形状为(2, 3)的数组。通过广播机制,这个(2, 3)的数组会被转换成(1, 2, 3)的形状,最终得到你想要的(2, 2, 3)的数组。虽然广播的概念和使用np.tilenp.repeat有点相似,但它效率更高,因为它避免了创建原始数组的扩展副本。

撰写回答