Is layered broadcasting available in NumPy? - python

I wonder if there is a built-in operation which would free my code from Python-loops.
The problem is this: I have two matrices A and B. A has N rows and B has N columns. I would like to multiply every i row from A with corresponding i column from B (using NumPy broadcasting). The resulting matrix would form i layer in the output. So my result would be 3-dimensional array.
Is such operation available in NumPy?

One way to express your requirement directly is by using np.einsum():
>>> A = np.arange(12).reshape(3, 4)
>>> B = np.arange(15).reshape(5, 3)
>>> np.einsum('...i,j...->...ij', A, B)
array([[[ 0, 0, 0, 0, 0],
[ 0, 3, 6, 9, 12],
[ 0, 6, 12, 18, 24],
[ 0, 9, 18, 27, 36]],
[[ 4, 16, 28, 40, 52],
[ 5, 20, 35, 50, 65],
[ 6, 24, 42, 60, 78],
[ 7, 28, 49, 70, 91]],
[[ 16, 40, 64, 88, 112],
[ 18, 45, 72, 99, 126],
[ 20, 50, 80, 110, 140],
[ 22, 55, 88, 121, 154]]])
This uses the Einstein summation convention.
For further discussion, see chapter 3 of Vectors, Pure and Applied: A General Introduction to Linear Algebra by T. W. Körner. In it, the author cites an amusing passage from Einstein's letter to a friend:
"I have made a great discovery in mathematics; I have suppressed the summation sign every time that the summation must be made over an index which occurs twice..."

Yes, in it's simplest form you just add "zero" dimensions so the NumPy broadcasts along the rows of A and columns of B:
>>> import numpy as np
>>> A = np.arange(12).reshape(3, 4) # 3 row, 4 colums
>>> B = np.arange(15).reshape(5, 3) # 5 rows, 3 columns
>>> res = A[None, ...] * B[..., None]
>>> res
array([[[ 0, 0, 0, 0],
[ 4, 5, 6, 7],
[ 16, 18, 20, 22]],
[[ 0, 3, 6, 9],
[ 16, 20, 24, 28],
[ 40, 45, 50, 55]],
[[ 0, 6, 12, 18],
[ 28, 35, 42, 49],
[ 64, 72, 80, 88]],
[[ 0, 9, 18, 27],
[ 40, 50, 60, 70],
[ 88, 99, 110, 121]],
[[ 0, 12, 24, 36],
[ 52, 65, 78, 91],
[112, 126, 140, 154]]])
The result has a shape of (5, 3, 4) and you can easily move the axis around if you want a different shape. For example using np.moveaxis:
>>> np.moveaxis(res, (0, 1, 2), (2, 0, 1)) # 0 -> 2 ; 1 -> 0, 2 -> 1
array([[[ 0, 0, 0, 0, 0],
[ 0, 3, 6, 9, 12],
[ 0, 6, 12, 18, 24],
[ 0, 9, 18, 27, 36]],
[[ 4, 16, 28, 40, 52],
[ 5, 20, 35, 50, 65],
[ 6, 24, 42, 60, 78],
[ 7, 28, 49, 70, 91]],
[[ 16, 40, 64, 88, 112],
[ 18, 45, 72, 99, 126],
[ 20, 50, 80, 110, 140],
[ 22, 55, 88, 121, 154]]])
With a shape of (3, 4, 5).

Related

Multiplying numpy ndarray with 1d array

So I can see many questions on this forum asking how to multiply numpy ndarrays with a 1d ndarray over a given axis. Most of the answers suggest making use of np.newaxis to meet broadcasting requirements. Here I have a more specific issue where Id like to multiply over axis 2 eg:
>>> import numpy as np
>>> x = np.arange(27).reshape((3,3,3))
>>> y = np.arange(3)
>>> z = x*y[:,np.newaxis,np.newaxis]
>>> x
array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8]],
[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]],
[[18, 19, 20],
[21, 22, 23],
[24, 25, 26]]])
>>> y
array([0, 1, 2])
>>> z
array([[[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]],
[[36, 38, 40],
[42, 44, 46],
[48, 50, 52]]])
This is the kind of multiplication I want.
However, in my case I've got dimensions along axis 0 and 1 that do not match dimensions along axis 2 eg, when I try and implement the above for my arrays I get this:
>>> x = np.arange(144).reshape(8,6,3)
>>> z = x*y[:,np.newaxis,np.newaxis]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (8,6,3) (3,1,1)
I understand why I get this broadcasting error, my issue is that if I adjust my broadcasting eg do a valid multiplication:
>>> z = x*y[np.newaxis,np.newaxis,:]
I am now not multiplying across the correct axis.
Any ideas how to address this issue?
Hi i have manage to get it in the format a long with the axis similar to how you did with your first example but not sure if this is correct?
import numpy as np
test = np.arange(144).reshape(8,6,3)
test2 = np.arange(3)
np.array([test[i] * test2[i] for i in range(len(test.shape))])
>>>array([[[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[ 18, 19, 20],
[ 21, 22, 23],
[ 24, 25, 26],
[ 27, 28, 29],
[ 30, 31, 32],
[ 33, 34, 35]],
[[ 72, 74, 76],
[ 78, 80, 82],
[ 84, 86, 88],
[ 90, 92, 94],
[ 96, 98, 100],
[102, 104, 106]]])

Vectorizing assignment of a tensor to a slice in PyTorch

I'm trying to vectorize a slice assignment of the form
for i in range(a.shape[1]):
for j in range(a.shape[2]):
a[:,i,j,:,i:i+b.shape[2],j:j+b.shape[3]] = b
where b itself is an array. This is because the nested Python loop is too inefficient and is taking up most of the runtime. Is there a way to do this?
For a simpler case, consider the following:
for i in range(a.shape[1]):
a[:,i,:,i:i+b.shape[2]] = b
This is what b and a might look like:
You can see the diagonal, "sliding" structure of the resulting matrix.
We can leverage np.lib.stride_tricks.as_strided based scikit-image's view_as_windows to get sliding windowed views into a 0s padded version of the input and being a view would be efficient on memory and performance. More info on use of as_strided based view_as_windows.
Hence, for the simpler case, it would be -
from skimage.util.shape import view_as_windows
def sliding_2D_windows(b, outshp_axis1):
# outshp_axis1 is desired output's shape along axis=1
n = outshp_axis1-1
b1 = np.pad(b,((0,0),(0,0),(n,n)),'constant')
w_shp = (1,b1.shape[1],b.shape[2]+n)
return view_as_windows(b1,w_shp)[...,0,::-1,0,:,:]
Sample run -
In [192]: b
Out[192]:
array([[[54, 57, 74, 77],
[77, 19, 93, 31],
[46, 97, 80, 98]],
[[98, 22, 68, 75],
[49, 97, 56, 98],
[91, 47, 35, 87]]])
In [193]: sliding_2D_windows(b, outshp_axis1=3)
Out[193]:
array([[[[54, 57, 74, 77, 0, 0],
[77, 19, 93, 31, 0, 0],
[46, 97, 80, 98, 0, 0]],
[[ 0, 54, 57, 74, 77, 0],
[ 0, 77, 19, 93, 31, 0],
[ 0, 46, 97, 80, 98, 0]],
[[ 0, 0, 54, 57, 74, 77],
[ 0, 0, 77, 19, 93, 31],
[ 0, 0, 46, 97, 80, 98]]],
[[[98, 22, 68, 75, 0, 0],
[49, 97, 56, 98, 0, 0],
[91, 47, 35, 87, 0, 0]],
....
[[ 0, 0, 98, 22, 68, 75],
[ 0, 0, 49, 97, 56, 98],
[ 0, 0, 91, 47, 35, 87]]]])
Assuming b has a shape (2,3,x1), and a has a shape (2,x2-x1+1,3,x2). In your screenshot, we can infer that x1=4, x2=6.
import numpy as np
b_shape = (2,3,4)
a_shape = (2,3,3,6)
b = np.arange(1,25).reshape(b_shape)
#array([[[ 1, 2, 3, 4],
# [ 5, 6, 7, 8],
# [ 9, 10, 11, 12]],
#
# [[13, 14, 15, 16],
# [17, 18, 19, 20],
# [21, 22, 23, 24]]])
c = np.pad(b, (*[(0,0) for _ in range(len(b_shape[:-1]))], (0,a_shape[-1]-b_shape[-1])), 'constant')
#array([[[ 1, 2, 3, 4, 0, 0],
# [ 5, 6, 7, 8, 0, 0],
# [ 9, 10, 11, 12, 0, 0]],
#
# [[13, 14, 15, 16, 0, 0],
# [17, 18, 19, 20, 0, 0],
# [21, 22, 23, 24, 0, 0]]])
a = np.stack([np.roll(c, shift=i) for i in range(a_shape[-1]-b_shape[-1]+1)], axis=1)
# array([[[[ 1, 2, 3, 4, 0, 0],
# [ 5, 6, 7, 8, 0, 0],
# [ 9, 10, 11, 12, 0, 0]],
# [[ 0, 1, 2, 3, 4, 0],
# [ 0, 5, 6, 7, 8, 0],
# [ 0, 9, 10, 11, 12, 0]],
# [[ 0, 0, 1, 2, 3, 4],
# [ 0, 0, 5, 6, 7, 8],
# [ 0, 0, 9, 10, 11, 12]]],
# [[[13, 14, 15, 16, 0, 0],
# [17, 18, 19, 20, 0, 0],
# [21, 22, 23, 24, 0, 0]],
# [[ 0, 13, 14, 15, 16, 0],
# [ 0, 17, 18, 19, 20, 0],
# [ 0, 21, 22, 23, 24, 0]],
# [[ 0, 0, 13, 14, 15, 16],
# [ 0, 0, 17, 18, 19, 20],
# [ 0, 0, 21, 22, 23, 24]]]])

How to find the smallest number except one particular index

edit)
Sorry my question wasn't clear. So, I wanted to find the min of a certain row and col without taking their intersection point, as #ParthSindhu said :)
I would like to find the min number from 2d array except the one number. (I'm using numpy array)
array([[30, 15, 41, 26, 12],
[ 4, 19, 22, 40, 1],
[41, 21, 0, 43, 22],
[ 9, 40, 6, 10, 30],
[24, 49, 22, 8, 41]])
For example, in row 2 and col 2, I would like to find the smallest number in each row and col except 0.
So, the answer want is 21 in row 2, and 6 in col 2.
I've tried to implement this code with 1d array,
a = np.arange(9, -1, -1) # a = array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
b = a[np.arange(len(a))!=3] # b = array([9, 8, 7, 5, 4, 3, 2, 1, 0])
But, I could only find the one in the row but not in col.
a[np.arange(len(a))!=1].min()
The code just above returns 6
How could I do the same thing with col?
Sorry if the question is not so clear.
You Could this in case you are ignoring the intersection point:
import numpy as np
a = np.array([[30, 15, 41, 26, 12],
[ 4, 19, 22, 40, 1],
[41, 21, 0, 43, 22],
[ 9, 40, 6, 10, 30],
[24, 49, 22, 8, 41]]
row = 2
col = 2
col_indices = np.delete(np.arange(a.shape[0]), row)
row_indices = np.delete(np.arange(a.shape[1]), col)
col_min = a[col_indices, col].min()
row_min = a[row, row_indices].min()
print(col_min, row_min)
I'm sure there are better ways than this, this is just how i would do it.
You can use np.amin(a, axis = 1) to get an array with the smallest number in each row.
a = np.array([[30, 15, 41, 26, 12],
[ 4, 19, 22, 40, 1],
[41, 21, 0, 43, 22],
[ 9, 40, 6, 10, 30],
[24, 49, 22, 8, 41]])
print(np.amin(a, axis = 1))
This result in
>> [12 1 0 6 8]
Now you can run this again to find the smallest number in this array.
Dummy = np.amin(a, axis = 1)
print(np.amin(Dummy))
And you get the smallest number.
>> 0
You can change the axis if you set axis to 0. So you can perform this operation on each axis of the array.
a = np.array([[30, 15, 41, 26, 12],
[ 4, 19, 22, 40, 1],
[41, 21, 0, 43, 22],
[ 9, 40, 6, 10, 30],
[24, 49, 22, 8, 41]])
print(np.amin(a, axis = 0))
>> [ 4 15 0 8 1]
You could use masked arrays:
a = np.array([[30, 15, 41, 26, 12],
[ 4, 19, 22, 40, 1],
[41, 21, 0, 43, 22],
[ 9, 40, 6, 10, 30],
[24, 49, 22, 8, 41]])
masked_a = np.ma.masked_array(a, mask=a == 0)
min_cols = masked_a.min(axis=0).data
min_rows = masked_a.min(axis=1).data
print(min_rows)
print(min_cols)
[12 1 21 6 8]
[ 4 15 6 8 1]
One possible way, replace 0 with inf:
a = np.array([[30, 15, 41, 26, 12],
[ 4, 19, 22, 40, 1],
[41, 21, 0, 43, 22],
[ 9, 40, 6, 10, 30],
[24, 49, 22, 8, 41]])
no_zero = np.where(a==0, np.inf, a)
no_zero.min(axis=0) # array([ 4., 15., 6., 8., 1.])
no_zero.min(axis=1) # array([12., 1., 21., 6., 8.])

Add row and column numbers to np array

I have a numpy array of shape (1024, 1024, 3). It is an image RGB converted to a numpy array. I want to add the row and column number of the pixel to the numpy array.
Given a numpy array of shape (1024, 1024, 3). How do I convert it to a numpy array of (1024, 1024, 5) with the additional columns being row and column number of the particular pixel?
If one of the cell values is [125, 125, 125] and it is the 5th row and 3rd column, I want to convert it to [5, 3, 125, 125, 125]
Here is a method using np.indices and np.r_:
# small test case
>>> a = np.arange(108).reshape(6, 6, 3)
# create indices
>>> i, j = np.indices(a.shape[:2])
# stack them
>>> result = np.r_["2,3,0", i, j, a]
# admire
>>> result
array([[[ 0, 0, 0, 1, 2],
[ 0, 1, 3, 4, 5],
[ 0, 2, 6, 7, 8],
[ 0, 3, 9, 10, 11],
[ 0, 4, 12, 13, 14],
[ 0, 5, 15, 16, 17]],
[[ 1, 0, 18, 19, 20],
[ 1, 1, 21, 22, 23],
[ 1, 2, 24, 25, 26],
[ 1, 3, 27, 28, 29],
[ 1, 4, 30, 31, 32],
[ 1, 5, 33, 34, 35]],
[[ 2, 0, 36, 37, 38],
[ 2, 1, 39, 40, 41],
[ 2, 2, 42, 43, 44],
[ 2, 3, 45, 46, 47],
[ 2, 4, 48, 49, 50],
[ 2, 5, 51, 52, 53]],
[[ 3, 0, 54, 55, 56],
[ 3, 1, 57, 58, 59],
[ 3, 2, 60, 61, 62],
[ 3, 3, 63, 64, 65],
[ 3, 4, 66, 67, 68],
[ 3, 5, 69, 70, 71]],
[[ 4, 0, 72, 73, 74],
[ 4, 1, 75, 76, 77],
[ 4, 2, 78, 79, 80],
[ 4, 3, 81, 82, 83],
[ 4, 4, 84, 85, 86],
[ 4, 5, 87, 88, 89]],
[[ 5, 0, 90, 91, 92],
[ 5, 1, 93, 94, 95],
[ 5, 2, 96, 97, 98],
[ 5, 3, 99, 100, 101],
[ 5, 4, 102, 103, 104],
[ 5, 5, 105, 106, 107]]])

Generalisation of vector outer product: apply it to every column of a matrix

I have a matrix A = [x1, x2, ..., xm] where each xi is a column vector of size [n, 1]. So A has shape [n, m]. I am trying to find the covariance matrix of each column vector so that if the result is another matrix C, C has shape [n, n, m] and C[:,:,i] = np.outer(xi, xi).
Can someone tell my how to do the above in numpy or point me to a tensor operation that I should check out?
So your outer loop produces:
In [1147]: A = np.arange(12).reshape(3,4)
In [1148]: [np.outer(A[:,i],A[:,i]) for i in range(4)]
Out[1148]:
[array([[ 0, 0, 0],
[ 0, 16, 32],
[ 0, 32, 64]]), array([[ 1, 5, 9],
[ 5, 25, 45],
[ 9, 45, 81]]), array([[ 4, 12, 20],
[ 12, 36, 60],
[ 20, 60, 100]]), array([[ 9, 21, 33],
[ 21, 49, 77],
[ 33, 77, 121]])]
stacking that on the a new 1st dimension produces:
In [1149]: np.stack(_)
Out[1149]:
array([[[ 0, 0, 0],
[ 0, 16, 32],
[ 0, 32, 64]],
....
[ 21, 49, 77],
[ 33, 77, 121]]])
In [1150]: _.shape
Out[1150]: (4, 3, 3) # wrong order - can be transposed.
stack lets us specify a different axis:
In [1153]: np.stack([np.outer(A[:,i],A[:,i]) for i in range(4)],2)
Out[1153]:
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
[ 0, 9, 20, 33]],
[[ 0, 5, 12, 21],
[ 16, 25, 36, 49],
[ 32, 45, 60, 77]],
[[ 0, 9, 20, 33],
[ 32, 45, 60, 77],
[ 64, 81, 100, 121]]])
np.einsum does this nicely as well:
In [1151]: np.einsum('mi,ni->mni',A,A)
Out[1151]:
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
[ 0, 9, 20, 33]],
[[ 0, 5, 12, 21],
[ 16, 25, 36, 49],
[ 32, 45, 60, 77]],
[[ 0, 9, 20, 33],
[ 32, 45, 60, 77],
[ 64, 81, 100, 121]]])
In [1152]: _.shape
Out[1152]: (3, 3, 4)
broadcasted multiply is also nice
In [1156]: A[:,None,:]*A[None,:,:]
Out[1156]:
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
...
[ 32, 45, 60, 77],
[ 64, 81, 100, 121]]])

Categories