使用numpy获取3D空间中所有物体的相对位置
我想要获取一个numpy数组中所有向量对的排列组合之间的差异。
在我的具体应用中,这些向量是一些物体在三维空间中的位置向量。
所以,如果我有一个数组 r = [r1, r2, r3]
,其中 r1
、r2
和 r3
是三维向量,我想要得到以下结果:
[[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]]]
(注意,我需要交换维度2
和3
,才能创建你的示例数组。)
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.tile
或np.repeat
有点相似,但它效率更高,因为它避免了创建原始数组的扩展副本。