Python: drop index from tensor - python

I have two tensors.
tensor_A corresponds to a batch of 8 images, with 20 classes of objects and each image is 256 x 256
tensor_B corresponds to 8 arrays of len 20 full of 1 or 0, corresponding to if the object class is present
tensor_A.shape = ([8, 20, 256, 256])
tensor_B.shape = ([8, 20])
from tensor_A, I want to drop indices that correspond to 1 in tensor_B
for example if tensor_B[0] = [1,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0]
I would like to do tensor_A[0, 0, :, :].drop then tensor_A[0, 2, :, :].drop and so on, but all in one step
So far i have identified the indices that correspond to 1, by using the following:
for i in range(8):
(tensor_B[i, :] == 0).nonzero())
# code for dropping here
Not sure how to proceed

What you want won't work because:
# A -> tensor of shape (8, 20, 256, 256)
# B -> tensor of shape (8, 20)
# If B[0] = [1,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0]
dropped_A_0 = A[0, B[0] == 0, :, :]
# dropped_A_0 -> tensor of shape (1, 13, 256, 256)
# If B[1] = [1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0]
dropped_A_1 = A[1, B[1] == 0, :, :]
# dropped_A_1 -> tensor of shape (1, 16, 256, 256)
You see the problem? When you "drop" values from the rows of A, they aren't the same shape any more, and thus cannot exist together as a single tensor. What you can have is a list of the rows of A with dropped values:
dropped_A = []
for i in range(len(A)):
dropped_A.append(A[i, B[i] == 0, :, :])
Another thing you can do is simply set the unneeded values in A to 0.
A[B == 1] = 0

Related

advanced indexing using numpy

I'm trying to use advanced indexing but I cannot get it to work with this simple array
arr = np.array([[[ 1, 10, 100,1000],[ 2, 20, 200,2000]],[[ 3, 30, 300,3000],[ 4,40,400,4000]],[[5, 50, 500,5000],[6, 60,600,6000]]])
d1=np.array([0])
d2=np.array([0,1])
d3=np.array([0,1,2])
arr[d1,d2,d3]
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (1,) (2,) (3,)
and
arr[d1[:,np.newaxis],d2[np.newaxis,:],d3]
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (1,1) (1,2) (3,)
Expected output:
array([[[ 1, 10, 100],
[ 2, 20, 200]]])
You can use np.ix_ to combine several one-dimensional index arrays of different lengths to index a multidimensional array. For example:
arr[np.ix_(d1,d2,d3)]
To add more context, np.ix_ returns a tuple of ndimensional arrays. The same can be achieved "by hand" by adding np.newaxis for appropriate dimensions:
xs, ys, zs = np.ix_(d1,d2,d3)
# xs.shape == (1, 1, 1) == (len(d1), 1, 1 )
# ys.shape == (1, 2, 1) == (1, len(d2), 1 )
# zs.shape == (1, 1, 3) == (1, 1, len(d3))
result_ix = arr[xs, ys, zs]
# using newaxis:
result_newaxis = arr[
d1[:, np.newaxis, np.newaxis],
d2[np.newaxis, :, np.newaxis],
d3[np.newaxis, np.newaxis, :],
]
assert (result_ix == result_newaxis).all()
You need only d1 to select the first cell:
>>> arr[d1]
array([[[ 1, 10, 100],
[ 2, 20, 200]]])

extract elements from numpy array of arrays using list

This should be basic, but having been banging my head against a wall for a while now.
I have an array of arrays (16 x 16 of 128 x 128 sub-arrays), e.g.
a.shape
(16, 16, 128, 128)
I also have a list of indices of interest
print(indices)
[[2, 4], [3, 11], [6, 3], [9, 1], ......]
I want to extract the sub-arrays corresponding to the indices in the list. So, if pass the values of an individual index, this works properly, e.g.
b = a[2, 4]
print(b.shape)
(128, 128)
however, if I try and do this using my list indices, I do not get the same result:
b = a[[indices[0]]]
print(b.shape)
(2, 16, 128, 128)
I get a 2 x 16 array of the sub-arrays, which I do not want. Any help appreciated!
One simple workaround:
b = a[indices[0][0], indices[0][1]]
print(b.shape)
# (128, 128)
Actually, the two b are not the same. The first one is a[2,4] and the second one is a[[indices[0]]] = a[[[2,4]]] which in Python 3.6.9 will result in error.
I guess for your result, a[indices[0]].shape = (2,16,128,128).

How to concatenate many vectors in one matrix (each vector is a column of the new matrix)

I have for simplicity 4 vectors (type of numpy.ndarray) each of size 256 and I want to create a new matrix where each column correspond to each vector. So after concatenation I will have a 256 * 4 matrix
I tried either using hstack or np.concatenate but I don't get the result I want..
Thanks in advance
With vstack and transpose T:
n = 256
a = np.arange(n) + 0
b = np.arange(n) + 1
c = np.arange(n) + 2
d = np.arange(n) + 3
x = np.vstack([a,b,c,d]).T
x
Output:
array([[ 0, 1, 2, 3],
[ 1, 2, 3, 4],
[ 2, 3, 4, 5],
...,
[253, 254, 255, 256],
[254, 255, 256, 257],
[255, 256, 257, 258]])
Or, alternatively, there is a column_stack method as well:
x = np.column_stack([a,b,c,d])
And if you want to get it as an actual matrix instead of ndarray:
m = np.matrix([a,b,c,d]).T
Update:
To do it with a loop you can collect your arrays in a list and then stack them together:
n = 256
l = []
for i in range(4):
l.append(np.arange(n) + i)
x = np.vstack(l).T
x
Or, alternatively, stack them one-by-one on every step of the loop and transpose after the loop:
n = 256
x = np.arange(n)
for i in range(1,4):
x = np.vstack([x, np.arange(n) + i])
x = x.T
x

Numpy select matrix specified by a matrix of indices, from multidimensional array

I have a numpy array a of size 5x5x4x5x5. I have another matrix b of size 5x5. I want to get a[i,j,b[i,j]] for i from 0 to 4 and for j from 0 to 4. This will give me a 5x5x1x5x5 matrix. Is there any way to do this without just using 2 for loops?
Let's think of the matrix a as 100 (= 5 x 5 x 4) matrices of size (5, 5). So, if you could get a liner index for each triplet - (i, j, b[i, j]) - you are done. That's where np.ravel_multi_index comes in. Following is the code.
import numpy as np
import itertools
# create some matrices
a = np.random.randint(0, 10, (5, 5, 4, 5, 5))
b = np.random(0, 4, (5, 5))
# creating all possible triplets - (ind1, ind2, ind3)
inds = list(itertools.product(range(5), range(5)))
(ind1, ind2), ind3 = zip(*inds), b.flatten()
allInds = np.array([ind1, ind2, ind3])
linearInds = np.ravel_multi_index(allInds, (5,5,4))
# reshaping the input array
a_reshaped = np.reshape(a, (100, 5, 5))
# selecting the appropriate indices
res1 = a_reshaped[linearInds, :, :]
# reshaping back into desired shape
res1 = np.reshape(res1, (5, 5, 1, 5, 5))
# verifying with the brute force method
res2 = np.empty((5, 5, 1, 5, 5))
for i in range(5):
for j in range(5):
res2[i, j, 0] = a[i, j, b[i, j], :, :]
print np.all(res1 == res2) # should print True
There's np.take_along_axis exactly for this purpose -
np.take_along_axis(a,b[:,:,None,None,None],axis=2)

How is a 3-d tensor indexed by two 2d tensors?

Here's a snapshot from line 15-20 in DIM
def random_permute(X):
X = X.transpose(1, 2)
b = torch.rand((X.size(0), X.size(1))).cuda()
idx = b.sort(0)[1]
adx = torch.range(0, X.size(1) - 1).long()
X = X[idx, adx[None, :]].transpose(1, 2)
return X
where X is a tensor of size [64, 64, 128], idx a tensor of size [64, 64], adx a tensor of size [64].
How does X = X[idx, adx[None, :]] work? How can we use two 2d tensors to index a 3d tensor? What really happens to X after this indexing?
From my guess X must be a 3D tensor, since it usually represents a batch of training data.
As far as the functionality of this function is concerned, it randomly permutes the input data tensor X and it does this using the following steps:
First it initializes the tensor b with values sampled from uniform distribution.
Next this tensor is sorted along dimension 0 and the sorting indices are pulled out to tensor idx.
The tensor adx is just an integer tensor of values ranging from 0 to 63.
Now, the below line is where all the magic happens:
X[idx, adx[None, :]].transpose(1, 2)
We use the indices we got before idx and adx (adx[None, :] is simply a row vector of two dimension). Once we have that, we transpose the axes 1 and 2 exactly like what we did at the beginning of the function in the line:
X = X.transpose(1, 2)
Here is a contrived example, for better understanding:
# our input tensor
In [51]: X = torch.rand(64, 64, 32)
In [52]: X = X.transpose(1, 2)
In [53]: X.shape
Out[53]: torch.Size([64, 32, 64])
In [54]: b = torch.rand((X.size(0), X.size(1)))
# sort `b` which returns a tuple and take only indices
In [55]: idx = b.sort(0)[1]
In [56]: idx.shape
Out[56]: torch.Size([64, 32])
In [57]: adx = torch.arange(0, X.size(1)).long()
In [58]: adx.shape
Out[58]: torch.Size([32])
In [59]: X[idx, adx[None, :]].transpose(1, 2).shape
Out[59]: torch.Size([64, 64, 32])
The important thing to note here is how we got the same shape in the last step as the shape of the input tensor which is (64, 64, 32).
Things will be more clear if we consider a smaller concrete example. Let
x = np.arange(8).reshape(2, 2, 2)
b = np.random.rand(2, 2)
idx = b.argsort(0) # e.g. idx=[[1, 1], [0, 0]]
adx = np.arange(2)[None, :] # [[0, 1]]
y = x[idx, adx] # implicitly expanding 'adx' to [[0, 1], [0, 1]]
In this example, we'll have y as
y[0, 0] = x[idx[0, 0], adx[0, 0]]=x[1, 0]
y[0, 1] = x[idx[0, 1], adx[0, 1]]=x[1, 1]
y[1, 0] = x[idx[1, 0], adx[1, 0]]=x[0, 0]
...
It may be helpful to see how we do the same in tensorflow:
d0, d1, d2 = x.shape.as_list()
b = np.random.rand(d0, d1)
idx = np.argsort(b, 0)
idx = idx.reshape(-1)
adx = np.arange(0, d1)
adx = np.tile(adx, d0)
y = tf.reshape(tf.gather_nd(x, zip(idx, adx)), (d0, d1, d2))

Categories