Taking different columns from each 2D slice of a 3D numpy array - numpy

Assume the following 3D numpy array:
array([[[4, 1, 3, 5, 0, 1, 5, 4, 3],
[2, 3, 3, 2, 1, 0, 5, 5, 4],
[5, 3, 0, 2, 2, 2, 5, 3, 2],
[0, 3, 1, 0, 2, 4, 1, 1, 5],
[2, 0, 0, 1, 4, 0, 3, 5, 3]],
[[2, 2, 4, 1, 3, 4, 1, 1, 5],
[2, 2, 3, 5, 5, 4, 0, 2, 0],
[4, 0, 5, 3, 1, 3, 1, 1, 1],
[4, 5, 0, 0, 5, 3, 3, 2, 4],
[0, 3, 4, 5, 4, 5, 4, 2, 3]],
[[1, 3, 2, 2, 0, 4, 5, 0, 2],
[5, 0, 5, 2, 3, 5, 5, 3, 1],
[0, 5, 3, 2, 2, 0, 4, 2, 3],
[4, 4, 0, 3, 2, 1, 5, 3, 0],
[0, 0, 2, 4, 0, 5, 2, 0, 0]]])
Given a list [3, 4, 8],
is it possible to slice the given tensor without using a for loop?
For example to take the 3rdth column from [0, :, :], 4th column from [1, :, :] and 8th column from [2, :, :] to obtain:
array([[5, 2, 2, 0, 1],
[3, 5, 1, 5, 4],
[2, 1, 3, 0, 0]])

Here's one way with np.take_along_axis -
In [73]: idx = np.array([3,4,8])
# a is input array
In [72]: np.take_along_axis(a,idx[:,None,None],axis=2)[:,:,0]
Out[72]:
array([[5, 2, 2, 0, 1],
[3, 5, 1, 5, 4],
[2, 1, 3, 0, 0]])
Another with the explicit integer-indexing -
In [79]: a[np.arange(len(idx)),:,idx]
Out[79]:
array([[5, 2, 2, 0, 1],
[3, 5, 1, 5, 4],
[2, 1, 3, 0, 0]])

Related

A question about nested indexing of `numpy` arrays

I'm trying to understand what the following does at a conceptual level. Let's say we have two numpy arrays of random integers
arr1
array([[2, 2, 2, 2, 1],
[1, 3, 1, 3, 2],
[2, 2, 2, 1, 3],
[1, 1, 1, 3, 2]])
arr2
array([[1, 3, 1, 1, 3, 3, 2, 2],
[2, 3, 2, 2, 2, 3, 2, 1],
[3, 3, 3, 1, 1, 3, 3, 3],
[1, 1, 2, 1, 2, 1, 1, 1]])
Then, I do a nested indexing of the second array arr2 into the first one arr1, obtaining
arr1[arr2,:]
array([[[1, 3, 1, 3, 2],
[1, 1, 1, 3, 2],
[1, 3, 1, 3, 2],
[1, 3, 1, 3, 2],
[1, 1, 1, 3, 2],
[1, 1, 1, 3, 2],
[2, 2, 2, 1, 3],
[2, 2, 2, 1, 3]],
[[2, 2, 2, 1, 3],
[1, 1, 1, 3, 2],
[2, 2, 2, 1, 3],
[2, 2, 2, 1, 3],
[2, 2, 2, 1, 3],
[1, 1, 1, 3, 2],
[2, 2, 2, 1, 3],
[1, 3, 1, 3, 2]],
[[1, 1, 1, 3, 2],
[1, 1, 1, 3, 2],
[1, 1, 1, 3, 2],
[1, 3, 1, 3, 2],
[1, 3, 1, 3, 2],
[1, 1, 1, 3, 2],
[1, 1, 1, 3, 2],
[1, 1, 1, 3, 2]],
[[1, 3, 1, 3, 2],
[1, 3, 1, 3, 2],
[2, 2, 2, 1, 3],
[1, 3, 1, 3, 2],
[2, 2, 2, 1, 3],
[1, 3, 1, 3, 2],
[1, 3, 1, 3, 2],
[1, 3, 1, 3, 2]]])
which is a new array with shape (4,8,5). It is not clear to me how should I interpret this new object, and how the entries of the two arrays are actually combined together.
Reference on numpy ndarray indexing with integer arrays
TLDR:
out = arr1[arr2, :]
out[i, j, k] == arr1[ arr2[i, j], k ] # for all valid indices i,j,k
Intuition:
The values inside arr2 are being used independently/separately to index the first axis of arr1, and the results are placed into a new array with the same shape as arr2.

Selecting and View Metrices

so i have this Multi-dimensional array with the shape (2,3,4,5)
Here is how it looks like.
rand_5 =
array([[[[0, 2, 8, 9, 6],
[4, 9, 7, 3, 3],
[8, 3, 0, 1, 0],
[0, 6, 7, 7, 9]],
[[3, 0, 7, 7, 7],
[0, 5, 4, 3, 1],
[3, 1, 3, 4, 3],
[1, 9, 5, 9, 1]],
[[2, 3, 2, 2, 5],
[7, 3, 0, 9, 9],
[3, 4, 5, 3, 0],
[4, 8, 6, 7, 2]]],
[[[7, 3, 8, 6, 6],
[5, 6, 5, 7, 1],
[5, 4, 4, 9, 9],
[0, 6, 2, 6, 8]],
[[2, 4, 1, 6, 1],
[5, 1, 6, 9, 8],
[6, 5, 9, 7, 5],
[4, 9, 6, 8, 1]],
[[5, 5, 8, 3, 7],
[7, 9, 4, 7, 5],
[9, 6, 2, 0, 5],
[3, 0, 5, 7, 1]]]])
the third metric in the second index metric(1) is shown below
is rand_5[1,2] =
array([[5, 5, 8, 3, 7],
[7, 9, 4, 7, 5],
[9, 6, 2, 0, 5],
[3, 0, 5, 7, 1]])
QUESTION?
My Question is how can i select from the 2nd,3rd row & 1st,2nd Column from the metric above, such that i have the result shown in the metric below.?
[9,6]
[3,0]
With array slicing:
rand_5[1, 2, 2:4, 0:2]
outputs:
array([[9, 6],
[3, 0]])

rotate diagonal of a 2d numpy array into row

I have a 2d numpy array:
A = array([[1, 7, 5, 0, 5],
[9, 1, 4, 6, 0],
[9, 6, 1, 0, 0],
[2, 5, 0, 0, 0],
[1, 0, 0, 0, 0]])
What I want to achieve is
B = array([[1, 0, 0, 0, 0],
[9, 7, 0, 0, 0],
[9, 1, 5, 0, 0],
[2, 6, 4, 0, 0],
[1, 5, 1, 6, 5]])
So basically every diagonal of A is a row in B with 0 padded. Is there any efficient way to achieve this?
This is what I could come up with:
B = np.empty_like(A)
for i in range(5):
pad_width = (0, 5 - len(np.diag(A[::-1], k=n))
B[i, :] = np.pad(np.diag(A[::-1], k=i-4), pad_width)
Here is the explanation:
You can use np.diag(). This will return the diagonal array at level k, where k is the position of the diagonal you want. If 0, it will return the main diagonal
However, you need to reverse the matrix first A[::-1]
If you run the code so far:
np.diag(A[::-1], k=0)
np.diag(A[::-1], k=-1)
np.diag(A[::-1], k=-2)
You obtain the following output:
array([1, 5, 1, 6, 5])
array([2, 6, 4, 0])
array([9, 1, 5])
You can see that we are obtaining the desired rows in reversed order, and without padding. This last issue has an easy solution: np.pad(), whose first argument is the vector to be padded, and the second argument is the width of the padding (before, after).
Thus, we have to set this width to:
(0, 5 - len(np.diag(A[::-1], k=n)) # You can change it to (0, 5 - n) and make it more efficient, but this way is more understandable
where n is the level of the diagonal.
And there we have it, just initialize B:
B = np.empty_like(A)
And change each vector of B:
for i in range(5):
pad_width = (0, 5 - len(np.diag(A[::-1], k=n))
B[i, :] = np.pad(np.diag(A[::-1], k=i-4), pad_width)
And the output is:
array([[1, 0, 0, 0, 0],
[9, 7, 0, 0, 0],
[9, 1, 5, 0, 0],
[2, 6, 4, 0, 0],
[1, 5, 1, 6, 5]])
Here is one vectorized solution using np.meshgrid:
import numpy as np
A = np.array([[1, 7, 5, 0, 5],
[9, 1, 4, 6, 0],
[9, 6, 1, 0, 0],
[2, 5, 0, 0, 0],
[1, 0, 0, 0, 0]])
B = np.array([[1, 0, 0, 0, 0],
[9, 7, 0, 0, 0],
[9, 1, 5, 0, 0],
[2, 6, 4, 0, 0],
[1, 5, 1, 6, 5]])
n, m = A.shape
ix, iy = np.meshgrid(np.arange(n), np.arange(m))
iy = (iy - np.arange(m)) % m
# array([[0, 4, 3, 2, 1],
# [1, 0, 4, 3, 2],
# [2, 1, 0, 4, 3],
# [3, 2, 1, 0, 4],
# [4, 3, 2, 1, 0]])
B2 = A[iy, ix]
assert (B2 == B).all()

Numpy Where () with All() on a 2D matrix

A= np.random.randint(5, size=(25, 4, 4))
U= np.unique(A, axis =0 )
results = np.where((A==U[0]).all(axis=-1))
Using this Where function matches individual rows, I would like to match the entire 4x4 array not just individual rows.
here are example results:
(array([ 1, 97, 97, 97, 97], dtype=int64), array([0, 0, 1, 2, 3], dtype=int64))
if all four rows were matched the results would contain the same index 4 times as its for the index 97 above, a single row was matched with the index "1".
I assume if the entire array was matched then just one index would have been returned.
An example of desired output if multiple indexes are supplied for one array:
(array([97, 97, 97, 97], dtype=int64), array([0, 1, 2, 3], dtype=int64)
np.where((A.reshape(A.shape[0],-1) == U[0].reshape(-1)).all(axis=1))
Let's see an example
>>> A = np.random.randint(5, size=(25, 4, 4))
>>> A[:3,...]
array([[[0, 2, 0, 1],
[1, 0, 3, 0],
[4, 1, 1, 2],
[0, 1, 0, 0]],
[[1, 3, 2, 3],
[2, 4, 2, 1],
[3, 3, 2, 3],
[4, 2, 1, 1]],
[[4, 0, 3, 3],
[1, 0, 4, 4],
[0, 0, 2, 3],
[4, 1, 2, 2]]])
>>> U = np.unique(A, axis=0)
>>> U[0]
array([[0, 2, 0, 1],
[1, 0, 3, 0],
[4, 1, 1, 2],
[0, 1, 0, 0]])
Now you to want find U[0] in A if I understood correctly. It's easier to match row by row, so let's reshape the 4x4 arrays into rows
>>> A.reshape(A.shape[0], -1)[:3,...]
array([[0, 2, 0, 1, 1, 0, 3, 0, 4, 1, 1, 2, 0, 1, 0, 0],
[1, 3, 2, 3, 2, 4, 2, 1, 3, 3, 2, 3, 4, 2, 1, 1],
[4, 0, 3, 3, 1, 0, 4, 4, 0, 0, 2, 3, 4, 1, 2, 2]])
>>> U[0].reshape(-1)
array([0, 2, 0, 1, 1, 0, 3, 0, 4, 1, 1, 2, 0, 1, 0, 0])
Now we can compare them with np.where but if we're not careful we'll get an elementwise comparison, so we need to use np.all(axis=1) to be sure to compare them row by row:
>>> np.where(np.all(A.reshape(25, -1) == U[0].reshape(-1), axis=1))
(array([0]),)
EDIT it just occurred to me you can use multiple axes with np.all and avoid reshaping altogether:
np.where((A == U[0]).all(axis=(1,2)))

Python - numpy mgrid and reshape

Can someone explain to me what the second line of this code does?
objp = np.zeros((48,3), np.float32)
objp[:,:2] = np.mgrid[0:8,0:6].T.reshape(-1,2)
Can someone explain to me what exactly the np.mgrid[0:8,0:6] part of the code is doing and what exactly the T.reshape(-1,2) part of the code is doing?
Thanks and good job!
The easiest way to see these is to use smaller values for mgrid:
In [11]: np.mgrid[0:2,0:3]
Out[11]:
array([[[0, 0, 0],
[1, 1, 1]],
[[0, 1, 2],
[0, 1, 2]]])
In [12]: np.mgrid[0:2,0:3].T # (matrix) transpose
Out[12]:
array([[[0, 0],
[1, 0]],
[[0, 1],
[1, 1]],
[[0, 2],
[1, 2]]])
In [13]: np.mgrid[0:2,0:3].T.reshape(-1, 2) # reshape to an Nx2 matrix
Out[13]:
array([[0, 0],
[1, 0],
[0, 1],
[1, 1],
[0, 2],
[1, 2]])
Then objp[:,:2] = sets the 0th and 1th columns of objp to this result.
The second line creates a multi-dimensional mesh grid, transposes it, reshapes it so that it represents two columns and inserts it into the first two columns of the objp array.
Breakdown:
np.mgrid[0:8,0:6] creates the following mgrid:
>> np.mgrid[0:8,0:6]
array([[[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[2, 2, 2, 2, 2, 2],
[3, 3, 3, 3, 3, 3],
[4, 4, 4, 4, 4, 4],
[5, 5, 5, 5, 5, 5],
[6, 6, 6, 6, 6, 6],
[7, 7, 7, 7, 7, 7]],
[[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5]]])
The .T transposes the matrix, and the .reshape(-1,2) then reshapes it into two a two-column array shape. These two columns are then the correct shape to replace two columns in the original array.