Use numpy to get the positions of all objects in 3D space relative to each other
I want to get the differences between all permutations of vector pairs in a numpy array.
In my particular use case, these vectors are 3D positional vectors of a list of objects.
So, if I have an array r = [r1, r2, r3]
where r1
, r2
and r3
are 3-dimensional vectors, I want the following:
[[r1-r1 r1-r2 r1-r3] [r2-r1 r2-r2 r2-r3] [r3-r1 r3-r2 r3-r3]]
Where -
op is applied to vectors by element.
Basically, the vector equivalent of this:
>>> 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]]
However, the function outer
seems to flatten my vector array before subtracting and then reshape. For example:
>>> 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)
As a result, I expect:
>>> print(result)
[[[ 0 0 0]
[-3 -3 -3]]
[[ 3 3 3]
[ 0 0 0]]]
>>> print(result.shape)
(2, 2, 3)
Can I achieve the above without iterating over the array?
source to share
The answer is almost always broadcasting :
>>> r = np.arange(6).reshape(2, 3)
>>> r[:, None] - r
array([[[ 0, 0, 0],
[-3, -3, -3]],
[[ 3, 3, 3],
[ 0, 0, 0]]])
Which None
in indexing is the same as np.newaxis
and adds a size of size 1 to the shape of the array. So you subtract from the shape (2, 1, 3)
array an array with the shape (2, 3)
that is converted by casting to (1, 2, 3)
, and the end result is your desired array (2, 2, 3)
. While broadcasting is conceptually similar to using np.tile
or np.repeat
, it is much more efficient because it avoids making extended copies of the original arrays.
source to share
The short answer is:
A (almost) pure Python method for doing "para-hazy outer subtraction" of vectors r
would be:
np.array(map(operator.sub, *zip(*product(r, r)))).reshape((2, 2, -1))
So you can basically use a function product
to get all possible pairs of elements in a list, un zip
, to get two separate lists and map
subtract them operator
. Finally, you can reshape
as usual.
Step by step:
Below is a step-by-step example with all the required libraries and intermediate results:
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))
Output:
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]]]
(Note that I need to switch sizes 2
and 3
to create an array of examples.)
source to share
(Answering my own question here)
Here's an example using 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
This seems to work for any 2D array where the inner dimension has a size of 3, but I've done it mostly through trial and error, so I can't be 100% sure.
source to share