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?

+3


source to share


3 answers


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.

+3


source


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.)

+3


source


(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.

+3


source







All Articles