I have some object y.
If directed, y a tuple (y0, y1) of shape (n, d) arrays.
If not directed, y is a single array of shape (n, d).
Furthermore, y/y0/y1 of type np.ndarray can be either 1d or 2d. If 1d, I want its length. If 2d, I want the second value of its shape.
I'm trying to think of the simplest code that accounts for all of this and gives me the value of d. So far, the simplest I have is
_, d = np.atleast_2d(y[0]).shape if directed else np.atleast_2d(y).shape
but this feels a little obtuse. Can anyone think of a better way of doing this?
Unless you have a budget for the number of lines you're allowed to use, then something like this is nice and simple (you could even add explanatory comments for bonus points):
if directed:
[y0, y1] = y
d = y0.shape[-1]
assert y1.shape[-1] == d
else:
d = y.shape[-1]
Related
I'm trying to vectorize a loop with NumPy but I'm stuck
I have a matrix A of shape (NN,NN) I define the A-dot product by
def scalA(u,v):
return v.T # A # u
Then I have two matrices B and C (B has a shape (N,NN) and C has a shape (K,NN) the loop I'm trying to vectorize is
res = np.zeros((N,K))
for n in range(N):
for k in range(K):
res[n,k] = scalA(B[n,:], C[k,:])
I found during my research functions like np.tensordot or np.einsum, but I haven't really understood how they work, and (if I have well understood) tensordot will compute the canonical dot product (that would correspond to A = np.eye(NN) in my case).
Thanks !
np.einsum('ni,ji,kj->nk', B,A,C)
I think this works. I wrote it 'by eye' without testing.
Probably you're looking for this:
def scalA(u,v):
return u # A # v.T
If shape of A is (NN,NN), shape of B is (N,NN), and shape of C is (K,NN), the result of scalA(B,C) has shape (N,K)
If shape of A is (NN,NN), shape of B is (NN,), and shape of C is (NN,), the result of scalA(B,C) is just a scalar.
However, if you're expecting B and C to have even higher dimensionality (greater than 2), this may need further tweaking. (I could not tell from your question whether that's the case)
Have been completely stuck on a rather silly issue: I'm trying to compute the dot product of some attributes between objects, but keep getting a Value Error - Shape Mismatch - but the shapes are identical (2,1) and (2,1), since the arrays are just attributes of different instances of the same class:
class MyClass(Object):
def __init__(self, a,b, x,y):
self.prop_1 = np.array((a,b))
self.prop_2 = np.array((x,y))
where all a, b, x, and y are scalars. then further down I'm trying
def MyFunction(Obj1, Obj2):
results = np.dot(Obj1.prop_1 - Obj2.prop_1, Obj2.prop_2 - Obj2.prop_3)
which keeps throwing the Value Error
ValueError: shapes (2,1) and (2,1) not aligned: 1 (dim 1) != 2 (dim 0)
Mathematically, this dot product should be fine - but the final bit of the error message kind of suggests I have to transpose one of the arrays. I'd be very thankful for a short explanation of the numpy shape interpretation to avoid this kind of error!
EDIT:
Think I misphrased this a bit. When I initiate my objects via (case a)
a,b = np.random.rand(2)
x,y = np.random.rand(2)
MyClass(a, b, x, y)
Everything works like a charm. If instead however I initiate as (case b)
a = np.random.rand(1)
b = np.random.rand(1)
x = np.random.rand(1)
y = np.random.rand(1)
MyClass(a, b, x, y)
the dot product later on fails to work because of the shape mismatch.
I have noticed that in case b, each individual value is of shape (1,) and it's clear to me that combining two of these will result in shape (2,1) instead of shape () in case a - but why do these two ways of declaring a variable result in different shapes?
As you can tell I'm relatively new to Python and thought this was just a neat way to perform multiple assignments - turns out there is some further reasoning behind it, and i'd be interested to hear about that.
Part 1
The issue is that your arrays are full-blown 2-D matrices, not 1D "vectors" in the sense that np.dot understands it. To get your multiplication working, you need to either (a) convert your vectors to vectors:
np.dot(a.reshape(-1), b.reshape(-1))
(b) set up the matrix multiplication so that the dimensions work. Remember that the dot product of two Nx1 matrices is ATB:
np.dot(a.T, b)
or (c), use np.einsum to explicitly set the dimension of the sum:
np.einsum('ij,ij->j', a, b).item()
For all of the examples using dot, you can use np.matmul (or equivalently the # operator), or np.tensordot, because you have 2D arrays.
In general, keep the following rules in mind when working with dot. Table cells are einsum subscripts
A
| 1D | 2D | ND |
---+-------------------+---------------------+-------------------------------+
1D | i,i-> | ij,j->i | a...yz,z->a...y |
---+-------------------+---------------------+-------------------------------+
B 2D | i,ij->j | ij,jk->ik | a...xy,yz->a...xz |
---+-------------------+---------------------+-------------------------------+
ND | y,a...xyz->a...xz | ay,b...xyz->ab...xz | a...mxy,n...wyz->a...mxn...wz |
---+-------------------+---------------------+-------------------------------+
Basically, dot follows normal rules for matrix multiplication along the last two dimensions, but the leading dimensions are always combined. If you want the leading dimensions to be broadcast together for arrays > 2D (i.e., multiplying corresponding elements in a stack of matrices, rather all possible combinations), use matmul or # instead.
Part 2
When you initialize the inputs as a, b = np.random.rand(2), you are unpacking the two elements of the array into scalars:
>>> a, b = np.random.rand(2)
>>> a
0.595823752387523
>>> type(a)
numpy.float64
>>> a.shape
()
Note that the type is not numpy.ndarray in this case. However, when you do a = np.random.rand(1), the result is a 1D array of one element:
>>> a = np.random.rand(1)a
>>> a
array([0.21983553])
>>> type(a)
numpy.ndarray
>>> a.shape
(1,)
When you create a numpy array from numpy arrays, the result is a 2D array:
>>> np.array([1, 2]).shape
(2,)
>>> np.array([np.array([1]), np.array([2])]).shape
(2, 1)
Going forward, you have two options. You can either be more careful with your inputs, or you can sanitize the array after you've created it.
You can expand the arrays that you feed in:
ab = np.random.rand(2)
xy = np.random.rand(2)
MyClass(*ab, *xy)
Or you can just flatten/ravel your arrays once you've created them:
def __init__(self, a, b, x, y):
self.prop_1 = np.array([a, b]).ravel()
self.prop_2 = np.array([x, y]).ravel()
You can use ....reshape(-1) instead of ...ravel().
I am not very familiar with tensor algebra and I am having trouble understanding how to make numpy.tensordot do what I want.
The example I am working with is simple: given a tensor a with shape (2,2,3) and another b with shape (2,1,3), I want a result tensor c with shape (2,1). This tensor would be the result of the following, equivalent python code:
n = a.shape[2]
c = np.zeros((2,n))
for k in range(n):
c += a[:,:,k]*b[:,:,k]
The documentation says that the optional parameter axes:
If an int N, sum over the last N axes of a and the first N axes of b in order. The sizes of the corresponding axes must match.
But I don't understand which "axes" are needed here (furthermore, when axes is a tuple or a tuple of tuples it gets even more confusing). Examples aren't very clear to me either.
The way tensordot works, it won't work here (not at least directly) because of the alignment requirement along the first axes. You can use np.einsum though to solve your case -
c = np.einsum('ijk,ilk->ij',a,b)
Alternatively, use np.matmul/#-operator (Python 3.x) -
np.matmul(a,b.swapaxes(1,2))[...,0] # or (a # b.swapaxes(1,2))[...,0]
I am trying to set up some code for doing some numerical calculations on 3D vector fields such as electric or magnetic fields. I am having trouble setting up my meshes in the way I would like.
Consider this program:
import numpy as np
X1, Y1, Z1 = np.meshgrid(np.linspace(-10,10,10),np.linspace(-10,10,10),np.linspace(-10,10,10))
def scalarf(x,y,z):
return x**2 + y**2 + z**2
def vectorf(x,y,z):
return np.array([y,x,z])
def afunc(p,v):
return np.cross(p,v)
V = scalarf(X1,Y1,Z1)
F = vectorf(X1,Y1,Z1)
# This line clearly not working
F2 = afunc(F,np.array([1,0,0]))
print (V.shape)
print (F.shape)
The output of this gives (10,10,10) for V and (3,10,10,10) for F. So V is a 10x10x10 array of scalar values as intended. But for F what I wanted was a 10x10x10 array of 3 element arrays representing mathematical 3D vectors. Instead I have a 3 element array containing a 10x10x10 array as each of its elements. So I'm guessing I want a (10,10,10,3) shape. Ultimately I want to be able to (for ex) run functions like afun in the above. Again here the intention is that F2 would now be a new 10x10x10 array of vectors. At the moment it just fails because I guess its trying to perform a cross product with the 10x10x10 array and the fixed 3d vector in the function call.
Am I going about this in remotely the right way? Is there another way of creating a 3D array of vectors? BTW I have also tried using mgrids with much the same result.
Any help pointing me in the right direction much appreciated.
Use np.stack with the axis keyword
def vectorf(x, y, z):
return np.stack((y, x, z), axis = -1)
The code below is meant to conduct a linear coordinate transformation on a set of 3d coordinates. The transformation matrix is A, and the array containing the coordinates is x. The zeroth axis of x runs over the dimensions x, y, z. It can have any arbitrary shape beyond that.
Here's my attempt:
A = np.random.random((3, 3))
x = np.random.random((3, 4, 2))
x_prime = np.einsum('ij,j...->i...', A, x)
The output is:
x_prime = np.einsum('ij,j...->i...', A, x)
ValueError: operand 0 did not have enough dimensions
to match the broadcasting, and couldn't be extended
because einstein sum subscripts were specified at both
the start and end
If I specify the additional subscripts in x explicitly, the error goes away. In other words, the following works:
x_prime = np.einsum('ij,jkl->ikl', A, x)
I'd like x to be able to have any arbitrary number of axes after the zeroth axis, so the workaround I give about is not optimal. I'm actually not sure why the first einsum example is not working. I'm using numpy 1.6.1. Is this a bug, or am I misunderstanding the documentation?
Yep, it's a bug. It was fixed in this pull request: https://github.com/numpy/numpy/pull/4099
This was only merged a month ago, so it'll be a while before it makes it to a stable release.
EDIT: As #hpaulj mentions in the comment, you can work around this limitation by adding an ellipsis even when all indices are specified:
np.einsum('...ij,j...->i...', A, x)