Numpy array indexing with another matrix - python

I looked into other posts related to indexing numpy array with another numpy array, but still could not wrap my head around to accomplish the following:
a = [[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]],
b = [[[1,0],[0,1]],[[1,1],[0,1]]]
a[b] = [[[7,8,9],[4,5,6]],[[10,11,12],[4,5,6]]]
a is an image represented by 3D numpy array, with dimension 2 * 2 * 3 with RGB values for the last dimension. b contains the index that will match to the image. For instance for pixel index (0,0), it should map to index (1,0) of the original image, which should give pixel values [7,8,9]. I wonder if there's a way to achieve this. Thanks!

Here's one way:
In [54]: a = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
In [55]: b = np.array([[[1, 0], [0, 1]], [[1, 1], [0, 1]]])
In [56]: a[b[:, :, 0], b[:, :, 1]]
Out[56]:
array([[[ 7, 8, 9],
[ 4, 5, 6]],
[[10, 11, 12],
[ 4, 5, 6]]])

Related

How to index row elements of a Matrix with a Matrix of indices for each row?

I have a Matrix of indices I e.g.
I = np.array([[1, 0, 2], [2, 1, 0]])
The index at i-th row selects an element from another Matrix M in the i-th row.
So having M e.g.
M = np.array([[6, 7, 8], [9, 10, 11])
M[I] should select:
[[7, 6, 8], [11, 10, 9]]
I could have:
I1 = np.repeat(np.arange(0, I.shape[0]), I.shape[1])
I2 = np.ravel(I)
Result = M[I1, I2].reshape(I.shape)
but this looks very complicated and I am looking for a more elegant solution. Preferably without flattening and reshaping.
In the example I used numpy, but I am actually using jax. So if there is a more efficient solution in jax, feel free to share.
In [108]: I = np.array([[1, 0, 2], [2, 1, 0]])
...: M = np.array([[6, 7, 8], [9, 10, 11]])
...:
...: I,M
I had to add a ']' to M.
Out[108]:
(array([[1, 0, 2],
[2, 1, 0]]),
array([[ 6, 7, 8],
[ 9, 10, 11]]))
Advanced indexing with broadcasting:
In [110]: M[np.arange(2)[:,None],I]
Out[110]:
array([[ 7, 6, 8],
[11, 10, 9]])
THe first index has shape (2,1) which pairs with the (2,3) shape of I to select a (2,3) block of values.
How about this one line code? The idea is to enumerate both the rows and the row indices of the matrix, so you can access the corresponding rows in the indexing matrix.
import numpy as np
I = np.array([[1, 0, 2], [2, 1, 0]])
M = np.array([[6, 7, 8], [9, 10, 11]])
Result = np.array([row[I[i]] for i, row in enumerate(M)])
print(Result)
Output:
[[ 7 6 8]
[11 10 9]]
np.take_along_axis can also be used here to take values of M using indices I over axis=1:
>>> np.take_along_axis(M, I, axis=1)
array([[ 7, 6, 8],
[11, 10, 9]])

numpy filling the diagonal 0 for 3D array

Suppose I have a 3D array, how can I fill the diag of the first two dimensions to zero. For example
a = np.random.rand(2,2,3)
for i in range(3):
np.fill_diagonal(a[:,:,i], 0)
Is there a way to replace the for loop?
The following is one of the solution
a = np.random.rand(2,2,3)
np.einsum('iij->ij',a)[...] = 0
The np.diag function returns a 2D diagonal matrix.
a[:,:,0] = np.diag((1,1))
In [6]: a = np.random.randint(1,10,(2,2,3))
...: for i in range(3):
...: np.fill_diagonal(a[:,:,i], 0)
In [7]: a
Out[7]:
array([[[0, 0, 0],
[7, 4, 4]],
[[8, 2, 7],
[0, 0, 0]]])
Indexing a diagonal is easy - just use the same index array for both dimensions. Thus the 0s we just set are:
In [8]: idx=np.arange(2)
In [9]: a[idx,idx,:]
Out[9]:
array([[0, 0, 0],
[0, 0, 0]])
and used to set a value:
In [10]: a[idx,idx,:] = 10
In [11]: a
Out[11]:
array([[[10, 10, 10],
[ 7, 4, 4]],
[[ 8, 2, 7],
[10, 10, 10]]])

Sort array by other array sort indexing in Python

I'm having a bit of a difficulty. I'm trying to vectorize some code in python in order to make it faster. I have an array which I sort (A) and get the index list (Ind). I have another array (B) which I would like to sort by the index list, without using loops which I think bottlenecks the computation.
A = array([[2, 1, 9],
[1, 1, 5],
[7, 4, 1]])
Ind = np.argsort(A)
This is the result of Ind:
Ind = array([[1, 0, 2],
[0, 1, 2],
[2, 1, 0]], dtype=int64)
B is the array i would like to sort by Ind:
B = array([[ 6, 3, 9],
[ 1, 5, 3],
[ 2, 7, 13]])
I would like to use Ind to rearrange my elements in B as such (B rows sorted by A rows indexes):
B = array([[ 3, 6, 9],
[ 1, 5, 3],
[13, 7, 2]])
Any Ideas? I would be glad to get any good suggestion. I want to mention I am using millions of values, I mean arrays of 30000*5000.
Cheers,
Robert
I would do something like this:
import numpy as np
from numpy import array
A = array([[2, 1, 9],
[1, 1, 5],
[7, 4, 1]])
Ind = np.argsort(A)
B = array([[ 3, 6, 9],
[ 1, 5, 3],
[13, 7, 2]])
# an array of the same shape as A and B with row numbers for each element
rownums = np.tile(np.arange(3), (3, 1)).T
new_B = np.take(B, rownums * 3 + Ind)
print(new_B)
# [[ 6 3 9]
# [ 1 5 3]
# [ 2 7 13]]
You can replace the magic number 3 with the array shape.

Want to append 2 2d arrays in numpy

I'm trying to append 2 2d numpy arrays
a = np.array([[1],
[2],
[3],
[4]])
b = np.array([[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
target is
c = ([[1],
[2],
[3],
[4],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
Tried np.concatenate((a,b),axis=0) and np.concatenate((a,b),axis=1)
and get
ValueError: all the input array dimensions except for the concatenation axis must match exactly
and np.append(a,b)
But nothing seems to work. If I convert to list it gives me the result I want but seems inefficient
c = a.tolist() + b.tolist()
Is there a numpy way to do this?
As the error indicate, the dimensions have to match.
So you could resize a so that it matches the dimension of b and then concatenate (the empty cells are filled with zeros).
a.resize(3,4)
a = a.transpose()
np.concatenate((a,b))
array([[ 1, 0, 0],
[ 2, 0, 0],
[ 3, 0, 0],
[ 4, 0, 0],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
Short answer - no. Numpy arrays need to be 'rectangular'; similar to matrices in linear algebra. You can follow the suggestion here and force it in (at the loss of a lot of functionality) or, if you really need the target, use a data structure like a list which is designed to cope with it.

Special Vector and Element-wise Multiplication

I have 2 arrays. "A" is one of them with arbitrary length (let's assume 1000 entries for a start), where each point holds a n-dimensional vector, where each entry represents a scalar. "B" is the other one, with n entries that each hold a 3-dimensional vector. How can I do a scalar multiplication, so that the result is one array "C", where each entry is the scalar multiplication of each of the n scalars with each of the n 3-Dimensional Vectors?
As an example in 4-D:
a=[[1,2,3,4],[5,6,7,8],....]
b=[[1,0,0],[0,1,0],[0,0,1],[1,1,1]]
and a result
c=[[1*[1,0,0],2*[0,1,0],3*[0,0,1],4*[1,1,1]] , [5*[1,0,0],...],...]
The implementation should be in numpy without to large for loops, because there are expected to be way more than 1000 entries. n is expected to be 7 in our case.
If you start with:
a = np.array([[1,2,3,4],[5,6,7,8]])
b = np.array([[1,0,0],[0,1,0],[0,0,1],[1,1,1]])
Then we can add an extra axis to a, and repeating the array along it gives us...
>>> a[:,:,None].repeat(3, axis=2)
array([[[1, 1, 1],
[2, 2, 2],
[3, 3, 3],
[4, 4, 4]],
[[5, 5, 5],
[6, 6, 6],
[7, 7, 7],
[8, 8, 8]]])
Now, as #Jaime says, there is no need to use the repeat while operating, because NumPy's broadcasting takes care of it:
>>> a[:,:,None] * b
array([[[1, 0, 0],
[0, 2, 0],
[0, 0, 3],
[4, 4, 4]],
[[5, 0, 0],
[0, 6, 0],
[0, 0, 7],
[8, 8, 8]]])

Categories

Resources