Andreas Schuldei
Andreas Schuldei

Reputation: 405

How to calculate the orthogonal vector of a unit vector with numpy?

I have a set of unit vectors in a numpy array u:

import numpy as np
a = np.arange(12).reshape(2,6)     # generate some vectors
u = a/np.linalg.norm(a, axis=0)    # turn them into unit vectors
print(u)
[[0.         0.5547002  0.62469505 0.65079137 0.66436384 0.67267279]
 [1.         0.83205029 0.78086881 0.7592566  0.74740932 0.73994007]]

Now I want to generate the vectors orthogonal to each vector (just by flipping the components of the vectors like (x,y) -> (-y,x) ):

ortogonal_u = np.array(-u[1,:], u[0,:])

and get the error

TypeError: data type not understood

What am i doing wrong? How to fix it?

Is there a better way to find the orthogonal vectors of such a set of vectors? I would like it to be performant.

Upvotes: 1

Views: 3423

Answers (3)

tom10
tom10

Reputation: 69182

If you want this to be fast for large arrays, it helps to do things in place. The following will do that:

a = np.arange(12).reshape(2,6)

a = a[::-1, :]   # change the indexing to reverse the vector to swap x and y (note that this doesn't do any copying)
np.negative(a[0,:], out=a[0, :])  # negate one axis

#  [[ -6  -7  -8  -9 -10 -11]
#   [  0   1   2   3   4   5]]

Speed testing this and some of the other posted answers:

N = 10000000
a0 = np.arange(2*N).reshape(2,N)

def f0(a):
    x = a[::-1, :]
    np.negative(x[0,:], out=x[10, :])
    return x

def f1(a):
    x = np.array([-a[1,:], a[0,:]])
    return x

def f2(a):
    x = np.flip(a, axis=0) * np.array([[1], [-1]])
    return x

%timeit f0(a0)
#   6.69 ms ± 81.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit f1(a0)
# 103 ms ± 1.41 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit f2(a0)
#  81.6 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

So the in-place operation is more than 10x faster for a very large array. (This is particularly fast since all it does is change the indexing direction (a single operation on the array header and independent of array size), and then change the sign of one row, so it's an unusual speed gain. Currently, I suspect the sign change requires a copy, but there may be a way to do this without a copy, but I don't know it. Also, note that if you do the operation in place, the original array is over-written, so this may not work for your use case.

Upvotes: 2

dmontaner
dmontaner

Reputation: 2165

You can use flip and broadcast opperations:

import numpy as np
a = np.arange(12).reshape(2,6)     # generate some vectors
u = a/np.linalg.norm(a, axis=0)    # turn them into unit vectors
print(u)

print(np.flip(u, axis=0) * np.array([[1], [-1]]))  # NEW LINE HERE

[[0.         0.14142136 0.24253563 0.31622777 0.37139068 0.41380294]
 [1.         0.98994949 0.9701425  0.9486833  0.92847669 0.91036648]]

[[ 1.          0.98994949  0.9701425   0.9486833   0.92847669  0.91036648]
 [-0.         -0.14142136 -0.24253563 -0.31622777 -0.37139068 -0.41380294]]

Upvotes: 1

senderle
senderle

Reputation: 150977

You're passing two data arguments to the array constructor, but it only expects one. When a second argument is passed, array expects it to be a description of the datatype of the array, and u[0, :] is not a valid type descriptor.

The minimal change needed to get the expected result is to place the two slices in a list.

np.array([-u[1,:], u[0,:]])

Upvotes: 1

Related Questions