np.dot of two 2D arrays - python

I am new to using numpy so sorry if this sounds obvious, I did try to search through stackoverflow before I post this though..
I have two "list of lists" numpy arrays of length n (n = 3 in the example below)
a = np.array([[1, 2], [3, 4], [5, 6]])
b = np.array([[2, 2], [3, 3], [4, 4]])
I want to get a 1d array with the dot product of the lists at each corresponding index, i.e.
[(1*2 + 2*2), (3*3 + 4*3), (5*4 + 6*4)]
[6, 21, 44]
how should I go about doing it? thanks in advance!

You can do this
np.sum(a*b,axis=1)

The sum method in the other answer is the most straight forward method:
In [19]: a = np.array([[1, 2], [3, 4], [5, 6]])
...: b = np.array([[2, 2], [3, 3], [4, 4]])
In [20]: a*b
Out[20]:
array([[ 2, 4],
[ 9, 12],
[20, 24]])
In [21]: _.sum(1)
Out[21]: array([ 6, 21, 44])
With dot we have think a bit outside the box. einsum is easiest way of specifying a dot like action with less-than-obvious dimension combinations:
In [22]: np.einsum('ij,ij->i',a,b)
Out[22]: array([ 6, 21, 44])
Note that the i dimension is carried through. dot does ij,jk->ik, which would require extracting the diagonal (throwing away extra terms). In matmul/# terms, the i dimension is a 'batch' one, that doesn't actually participate in the sum-of-products. To use that:
In [23]: a[:,None,:]#b[:,:,None]
Out[23]:
array([[[ 6]],
[[21]],
[[44]]])
and then remove the extra size 1 dimensions:
In [24]: _.squeeze()
Out[24]: array([ 6, 21, 44])
In einsum terms this is i1j,ij1->i11

Related

Is there a reduced form of the dot product in numpy?

I am trying to find a numpy operation that gives me the scalar products between all vectors of a 2d array at index i of a 3d array and the vector at index i of a 2d array. Let me give you an example to explain what I am thinking of:
x = np.array([[[1,2,3],
[2,3,4]],
[[11,12,13],
[12,13,14]]])
y = np.array([[1,1,1],
[2,2,2]])
np.?operation?(x,y.T)
output:
[[[1 *1 + 1 *2 + 1 *3],
[1 *2 + 1 *3 + 1 *4]],
[[2 *11 + 2 *12 + 2 *13],
[2 *12 + 2* 13 + 2 *14]]]
= [[[6],
[9]],
[[72],
[78]]]
As you can see, I am basically looking for a reduced dot product operation. The dot product of x and y would yield the following:
np.dot(x, y.T)
output:
[[[ 6 12]
[ 9 18]]
[[36 72]
[39 78]]]
Or is there a way to extract the results I need from the dot product result?
I have also tried np.tensordot(x,y,axis) but I were not able to figure which tuples I should put for -axis-.
I have also come across the np.einsum() operation but couldn't work my head around how this could help me with my problem.
It should be doable with np.einsum or np.matmul/# which has a "batch" operation on the leading dimension. But sorting out your dimensions, and getting the (2,2,1) shape is a bit tricky.
Your np.dot(x, y.T) gives the numbers you want, but you have to extract a kind of diagonal on 2, while retaining a dimension.
Here's one way of doing this - it isn't the fastest or succinct, but should help me wrap my mind around the dimensions.
In [432]: y[:,None,:]
Out[432]:
array([[[1, 1, 1]],
[[2, 2, 2]]])
In [433]: y[:,None,:].repeat(2,1)
Out[433]:
array([[[1, 1, 1],
[1, 1, 1]],
[[2, 2, 2],
[2, 2, 2]]])
In [435]: x*y[:,None,:].repeat(2,1)
Out[435]:
array([[[ 1, 2, 3],
[ 2, 3, 4]],
[[22, 24, 26],
[24, 26, 28]]])
In [436]: (x*y[:,None,:].repeat(2,1)).sum(axis=-1, keepdims=True)
Out[436]:
array([[[ 6],
[ 9]],
[[72],
[78]]])
We don't need the repeat, broadcasting will take its place:
(x*y[:,None,:]).sum(axis=-1, keepdims=True)
This einsum does the same as the dot/#:
In [441]: np.einsum('ijk,lk->ijl',x,y)
Out[441]:
array([[[ 6, 12],
[ 9, 18]],
[[36, 72],
[39, 78]]])
Change the indices a bit to get the "diagonal" (i in all terms)
In [442]: np.einsum('ijk,ik->ij',x,y)
Out[442]:
array([[ 6, 9],
[72, 78]])
and add a trailing dimension:
In [443]: np.einsum('ijk,ik->ij',x,y)[:,:,None]
Out[443]:
array([[[ 6],
[ 9]],
[[72],
[78]]])
Now that I have the einsum I can visualize the matmul/# dimensions. I need to the treat the first dimension of both as the 'batch', and add a new trailing dimension to y, making it (2,3,1). (2,2,3) with (2,3,1) => (2,2,1) with sum-of-products on the 3.
In [445]: x#y[:,:,None]
Out[445]:
array([[[ 6],
[ 9]],
[[72],
[78]]])
If x and y were (4,2,3) and (4,3) shaped, this dimension matching would have been more obvious.
In [446]: X=x.repeat(2,0)
In [447]: Y=y.repeat(2,0)
In [448]: X.shape
Out[448]: (4, 2, 3)
In [449]: Y.shape
Out[449]: (4, 3)
In [450]: X#Y[:,:,None] # (4,2,1)
Out[450]:
array([[[ 6],
[ 9]],
[[ 6],
[ 9]],
[[72],
[78]],
[[72],
[78]]])
With these shapes it's more obvious that 4 is the batch, and 3 is the sum-of-products.

Get indices of element of one array using indices in another array

Suppose I have an array a of shape (2, 2, 2):
a = np.array([[[7, 9],
[19, 18]],
[[24, 5],
[18, 11]]])
and an array b that is the max of a: b=a.max(-1) (row-wise):
b = np.array([[9, 19],
[24, 18]])
I'd like to obtain the index of elements in b using index in flattened a, i.e. a.reshape(-1):
array([ 7, 9, 19, 18, 24, 5, 18, 11])
The result should be an array that is the same shape with b with indices of b in flattened a:
array([[1, 2],
[4, 6]])
Basically this is the result of maxpool2d when return_indices= True in pytorch, but I'm looking for an implementation in numpy. I've used where but it seems doesn't work, also is it possible to combine finding max and indices in one go, to be more efficient? Thanks for any help!
I have a solution similar to that of Andras based on np.argmax and np.arange. Instead of "indexing the index" I propose to add a piecewise offset to the result of np.argmax:
import numpy as np
a = np.array([[[7, 9],
[19, 18]],
[[24, 5],
[18, 11]]])
off = np.arange(0, a.size, a.shape[2]).reshape(a.shape[0], a.shape[1])
>>> off
array([[0, 2],
[4, 6]])
This results in:
>>> a.argmax(-1) + off
array([[1, 2],
[4, 6]])
Or as a one-liner:
>>> a.argmax(-1) + np.arange(0, a.size, a.shape[2]).reshape(a.shape[0], a.shape[1])
array([[1, 2],
[4, 6]])
The only solution I could think of right now is generating a 2d (or 3d, see below) range that indexes your flat array, and indexing into that with the maximum indices that define b (i.e. a.argmax(-1)):
import numpy as np
a = np.array([[[ 7, 9],
[19, 18]],
[[24, 5],
[18, 11]]])
multi_inds = a.argmax(-1)
b_shape = a.shape[:-1]
b_size = np.prod(b_shape)
flat_inds = np.arange(a.size).reshape(b_size, -1)
flat_max_inds = flat_inds[range(b_size), multi_inds.ravel()]
max_inds = flat_max_inds.reshape(b_shape)
I separated the steps with some meaningful variable names, which should hopefully explain what's going on.
multi_inds tells you which "column" to choose in each "row" in a to get the maximum:
>>> multi_inds
array([[1, 0],
[0, 0]])
flat_inds is a list of indices, from which one value is to be chosen in each row:
>>> flat_inds
array([[0, 1],
[2, 3],
[4, 5],
[6, 7]])
This is indexed into exactly according to the maximum indices in each row. flat_max_inds are the values you're looking for, but in a flat array:
>>> flat_max_inds
array([1, 2, 4, 6])
So we need to reshape that back to match b.shape:
>>> max_inds
array([[1, 2],
[4, 6]])
A slightly more obscure but also more elegant solution is to use a 3d index array and use broadcasted indexing into it:
import numpy as np
a = np.array([[[ 7, 9],
[19, 18]],
[[24, 5],
[18, 11]]])
multi_inds = a.argmax(-1)
i, j = np.indices(a.shape[:-1])
max_inds = np.arange(a.size).reshape(a.shape)[i, j, multi_inds]
This does the same thing without an intermediate flattening into 2d.
The last part is also how you can get b from multi_inds, i.e. without having to call a *max function a second time:
b = a[i, j, multi_inds]
This is a long one-liner
new = np.array([np.where(a.reshape(-1)==x)[0][0] for x in a.max(-1).reshape(-1)]).reshape(2,2)
print(new)
array([[1, 2],
[4, 3]])
However number = 18 is repeated twice; So which index is the target.

Override numpy.matmul function

I want to override numpy.matmul() function, such that instead of dot product of two matrix vectors, calculate how many elements are element-wise equal between those two vectors.
Example:
In [1]: import numpy as np
In [2]: m1 = np.matrix([[1, 2], [3, 4]])
In [3]: m2 = np.matrix([[1, 3], [2, 4]])
In [4]: np.matmul(m1, m2)
Out[4]:
matrix([[ 5, 11],
[11, 25]])
Instead of the result above, I want to have following:
In [4]: myfunction(m1, m2)
Out[4]:
matrix([[2, 0],
[0, 2]])
[1, 2] and [1, 2] dot product is 5, but I want np.sum([1,2] == [1,2]), which is 2.
My question is how can I change in np.matmul() function np.dot() product of two v1 and v2 vectors with np.sum(v1 == v2)
In [93]: m1,m2=np.array([[1,2],[3,4]]),np.array([[1,3],[2,4]])
In [94]: np.matmul(m1,m2)
Out[94]:
array([[ 5, 11],
[11, 25]])
The dot product can be calculated with:
In [95]: (m1[:,:,None]*m2[None,:,:]).sum(axis=1)
Out[95]:
array([[ 5, 11],
[11, 25]])
This doesn't have the speed of matmul because it doesn't pass anything to BLAS-like functions, but it is still reasonably fast, making full use of numpy array operations.
I think your desired version would be:
In [96]: (m1[:,:,None]==m2[None,:,:]).sum(axis=1)
Out[96]:
array([[2, 0],
[0, 2]])

Efficiently change order of numpy array

I have a 3 dimensional numpy array. The dimension can go up to 128 x 64 x 8192. What I want to do is to change the order in the first dimension by interchanging pairwise.
The only idea I had so far is to create a list of the indices in the correct order.
order = [1,0,3,2...127,126]
data_new = data[order]
I fear, that this is not very efficient but I have no better idea so far
You could reshape to split the first axis into two axes, such that latter of those axes is of length 2 and then flip the array along that axis with [::-1] and finally reshape back to original shape.
Thus, we would have an implementation like so -
a.reshape(-1,2,*a.shape[1:])[:,::-1].reshape(a.shape)
Sample run -
In [170]: a = np.random.randint(0,9,(6,3))
In [171]: order = [1,0,3,2,5,4]
In [172]: a[order]
Out[172]:
array([[0, 8, 5],
[4, 5, 6],
[0, 0, 2],
[7, 3, 8],
[1, 6, 3],
[2, 4, 4]])
In [173]: a.reshape(-1,2,*a.shape[1:])[:,::-1].reshape(a.shape)
Out[173]:
array([[0, 8, 5],
[4, 5, 6],
[0, 0, 2],
[7, 3, 8],
[1, 6, 3],
[2, 4, 4]])
Alternatively, if you are looking to efficiently create those constantly flipping indices order, we could do something like this -
order = np.arange(data.shape[0]).reshape(-1,2)[:,::-1].ravel()

Multiplying two 2D numpy arrays to a 3D array

I've got two 2D numpy arrays called A and B, where A is M x N and B is M x n. My problem is that I wish to multiply each element of each row of B with corresponding row of A and create a 3D matrix C which is of size M x n x N, without using for-loops.
As an example, if A is:
A = np.array([[1, 2, 3],
[4, 5, 6]])
and B is
B = np.array([[1, 2],
[3, 4]])
Then the resulting multiplication C = A x B would look something like
C = [
[[1, 2],
[12, 16]],
[[2, 4],
[15, 20]],
[[3, 6],
[18, 24]]
]
Is it clear what I'm trying to achieve, and is it possible doing without any for-loops? Best, tingis
C=np.einsum('ij,ik->jik',A,B)
It is possible by creating a new axis in each array and transposing the modified A:
A[np.newaxis,...].T * B[np.newaxis,...]
giving:
array([[[ 1, 2],
[12, 16]],
[[ 2, 4],
[15, 20]],
[[ 3, 6],
[18, 24]]])

Categories

Resources