Reputation: 405
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
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
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
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