Pytorch: accessing a subtensor using lists of indices - python

I have a pair of tensors S and T of dimensions (s1,...,sm) and (t1,...,tn) with si < ti. I want to specify a list of indices in each dimensions of T to "embed" S in T. If I1 is a list of s1 indices in (0,1,...,t1) and likewise for I2 up to In, I would like to do something like
T.select(I1,...,In)=S
that will have the effect that now T has entries equal to the entries of S over the indices (I1,...,In).
for example
`S=
[[1,1],
[1,1]]
T=
[[0,0,0],
[0,0,0],
[0,0,0]]
T.select([0,2],[0,2])=S
T=
[[1,0,1],
[0,0,0],
[1,0,1]]`

If you're flexible with using NumPy only for the indices part, then here's one approach by constructing an open mesh using numpy.ix_() and using this mesh to fill-in the values from the tensor S. If this is not acceptable, then you can use torch.meshgrid()
Below is an illustration of both approaches with descriptions interspersed in comments.
# input tensors to work with
In [174]: T
Out[174]:
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
# I'm using unique tensor just for clarity; But any tensor should work.
In [175]: S
Out[175]:
tensor([[10, 11],
[12, 13]])
# indices where we want the values from `S` to be filled in, along both dimensions
In [176]: idxs = [[0,2], [0,2]]
Now we will leverage np.ix_() or torch.meshgrid() to generate an open mesh by passing in the indices:
# mesh using `np.ix_`
In [177]: mesh = np.ix_(*idxs)
# as an alternative, we can use `torch.meshgrid()`
In [191]: mesh = torch.meshgrid([torch.tensor(lst) for lst in idxs])
# replace the values from tensor `S` using basic indexing
In [178]: T[mesh] = S
# sanity check!
In [179]: T
Out[179]:
tensor([[10, 0, 11],
[ 0, 0, 0],
[12, 0, 13]])

Related

How to find the nearest neighbor in numpy?

There are two array u and v.
u.shape = (N,d)
v.shape = (q,d)
I need to find, for every q, the nearest value's index for each d in u.
For example:
u = [[5,3],
[3,4],
[3,2],
[8,7]] , shape (4,2)
v = [[1,3],
[2,4]] , shape (2,2)
and I found many people said we can do that:
v = v.expand_dims(v,axis=1) # reshape to (2,1,2) for broadcast
result = np.argmin(abs(v-u),axis=1) # (u-v).shape = (2,4,2)
Of course it found the nearest value's index. But! when there are two nearest value, I need to take the "second" one's index.
In that case:
v-u = [[[-4, 0],
[-2, -1],
[-2, 1],
[-7, -4]],
[[-3, 1],
[-1, 0],
[-1, 2],
[-6, -3]]])
along axis=1, there are two -2 in (u-v)[0,:,0] and two -1 in (u-v)[1,:,0]
If we directly use:
result = np.argmin(abs(v-u),axis=1)
result will be:
array([[1, 0],
[1, 1]], dtype=int64)
It returns the indices corresponding to the first occurrence but I need the second one, i,e
array([[2, 0],
[2, 1]], dtype=int64)
Can anyone help? Thanks!
If there can be at most 2 minimal values, you can retrieve indices of
the last minimum.
To do it:
reverse abs(v-u) along axis 1,
compute argmin, getting a "reversed_index" (actually the index in the
reversed array),
map back to "original" indices using u.shape[0] - 1 - <reversed_index>
formula (in your case of 4 rows, reversed index == 3 corresponds to
original index == 0)
The whole code is:
u.shape[0] - 1 - np.argmin(abs(v-u)[:,::-1,:],axis=1)
Other choice, when there can be more than 2 min values, is to write
a specialized version of argmin, for an 1-D input array, returning
the index of the second minimal value if there are more of them:
def argmin2(arr):
ind = arr.argpartition(1)[:2]
return ind[0] if arr[ind[0]] < arr[ind[1]] else ind[1]
and then apply it to abs(v-u) along axis 1:
np.apply_along_axis(argmin2, 1, abs(v-u))

For a given condition, get indices of values in 2D tensor A, use those to index a 3D tensor B

For a given 2D tensor I want to retrieve all indices where the value is 1. I expected to be able to simply use torch.nonzero(a == 1).squeeze(), which would return tensor([1, 3, 2]). However, instead, torch.nonzero(a == 1) returns a 2D tensor (that's okay), with two values per row (that's not what I expected). The returned indices should then be used to index the second dimension (index 1) of a 3D tensor, again returning a 2D tensor.
import torch
a = torch.Tensor([[12, 1, 0, 0],
[4, 9, 21, 1],
[10, 2, 1, 0]])
b = torch.rand(3, 4, 8)
print('a_size', a.size())
# a_size torch.Size([3, 4])
print('b_size', b.size())
# b_size torch.Size([3, 4, 8])
idxs = torch.nonzero(a == 1)
print('idxs_size', idxs.size())
# idxs_size torch.Size([3, 2])
print(b.gather(1, idxs))
Evidently, this does not work, leading to aRunTimeError:
RuntimeError: invalid argument 4: Index tensor must have same
dimensions as input tensor at
C:\w\1\s\windows\pytorch\aten\src\TH/generic/THTensorEvenMoreMath.cpp:453
It seems that idxs is not what I expect it to be, nor can I use it the way I thought. idxs is
tensor([[0, 1],
[1, 3],
[2, 2]])
but reading through the documentation I don't understand why I also get back the row indices in the resulting tensor. Now, I know I can get the correct idxs by slicing idxs[:, 1] but then still, I cannot use those values as indices for the 3D tensor because the same error as before is raised. Is it possible to use the 1D tensor of indices to select items across a given dimension?
You could simply slice them and pass it as the indices as in:
In [193]: idxs = torch.nonzero(a == 1)
In [194]: c = b[idxs[:, 0], idxs[:, 1]]
In [195]: c
Out[195]:
tensor([[0.3411, 0.3944, 0.8108, 0.3986, 0.3917, 0.1176, 0.6252, 0.4885],
[0.5698, 0.3140, 0.6525, 0.7724, 0.3751, 0.3376, 0.5425, 0.1062],
[0.7780, 0.4572, 0.5645, 0.5759, 0.5957, 0.2750, 0.6429, 0.1029]])
Alternatively, an even simpler & my preferred approach would be to just use torch.where() and then directly index into the tensor b as in:
In [196]: b[torch.where(a == 1)]
Out[196]:
tensor([[0.3411, 0.3944, 0.8108, 0.3986, 0.3917, 0.1176, 0.6252, 0.4885],
[0.5698, 0.3140, 0.6525, 0.7724, 0.3751, 0.3376, 0.5425, 0.1062],
[0.7780, 0.4572, 0.5645, 0.5759, 0.5957, 0.2750, 0.6429, 0.1029]])
A bit more explanation about the above approach of using torch.where(): It works based on the concept of advanced indexing. That is, when we index into the tensor using a tuple of sequence objects such as tuple of tensors, tuple of lists, tuple of tuples etc.
# some input tensor
In [207]: a
Out[207]:
tensor([[12., 1., 0., 0.],
[ 4., 9., 21., 1.],
[10., 2., 1., 0.]])
For basic slicing, we would need a tuple of integer indices:
In [212]: a[(1, 2)]
Out[212]: tensor(21.)
To achieve the same using advanced indexing, we would need a tuple of sequence objects:
# adv. indexing using a tuple of lists
In [213]: a[([1,], [2,])]
Out[213]: tensor([21.])
# adv. indexing using a tuple of tuples
In [215]: a[((1,), (2,))]
Out[215]: tensor([21.])
# adv. indexing using a tuple of tensors
In [214]: a[(torch.tensor([1,]), torch.tensor([2,]))]
Out[214]: tensor([21.])
And the dimension of the returned tensor would always be one dimension less than the dimension of the input tensor.
Assuming that b's three dimensions are batch_size x sequence_length x features (b x s x feats), the expected results can be achieved as follows.
import torch
a = torch.Tensor([[12, 1, 0, 0],
[4, 9, 21, 1],
[10, 2, 1, 0]])
b = torch.rand(3, 4, 8)
print(b.size())
# b x s x feats
idxs = torch.nonzero(a == 1)[:, 1]
print(idxs.size())
# b
c = b[torch.arange(b.size(0)), idxs]
print(c.size())
# b x feats
import torch
a = torch.Tensor([[12, 1, 0, 0],
[4, 9, 21, 1],
[10, 2, 1, 0]])
b = torch.rand(3, 4, 8)
print('a_size', a.size())
# a_size torch.Size([3, 4])
print('b_size', b.size())
# b_size torch.Size([3, 4, 8])
#idxs = torch.nonzero(a == 1, as_tuple=True)
idxs = torch.nonzero(a == 1)
#print('idxs_size', idxs.size())
print(torch.index_select(b,1,idxs[:,1]))
As a supplementary of #kmario23's solution, you can still achieve the same results like
b[torch.nonzero(a==1,as_tuple=True)]

what is the use case of numpy array of scalar value?

In the latest scipy version, I found:
>>> import numpy as np
>>> from scipy.sparse import csr_matrix
>>> a = csr_matrix((3, 4), dtype=np.int8)
>>> a[0,0]
array(0) #instead of `0`
and you can create numpy array of scaler value (instead of vector/matrix) np.array(0), which is different from np.array([0]). what is the use case of np.array(0)? how to get the value inside the array from np.array(0) (not type conversion use int)?
You've created a sparse matrix, shape (3,4), but no elements:
In [220]: a = sparse.csr_matrix((3, 4), dtype=np.int8)
In [221]: a
Out[221]:
<3x4 sparse matrix of type '<class 'numpy.int8'>'
with 0 stored elements in Compressed Sparse Row format>
In [222]: a.toarray()
Out[222]:
array([[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]], dtype=int8)
Selecting one element:
In [223]: a[0,0]
Out[223]: array(0, dtype=int8)
Converting it to a dense np.matrix:
In [224]: a.todense()
Out[224]:
matrix([[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]], dtype=int8)
In [225]: a.todense()[0,0]
Out[225]: 0
and to other sparse formats:
In [226]: a.tolil()[0,0]
Out[226]: 0
In [227]: a.todok()[0,0]
Out[227]: 0
It looks like csr is some what unique in returning a scalar array like this. I'm not sure if it's intentional, a feature, or a bug. I haven't noticed it before. Usually we work with the whole matrix, rather than specific elements.
But a 0d array is allowed, even if in most cases it isn't useful. If we can have 2d or 1d arrays, why not 0?
There are a couple of ways of extracting that element from a 0d array:
In [233]: np.array(0, 'int8')
Out[233]: array(0, dtype=int8)
In [234]: _.shape
Out[234]: ()
In [235]: __.item()
Out[235]: 0
In [236]: ___[()] # index with an empty tuple
Out[236]: 0
Scipy version 1.3.0 release notes includes:
CSR and CSC sparse matrix fancy indexing performance has been improved substantially
https://github.com/scipy/scipy/pull/7827 - looks like this pull request was a long time in coming, and had a lot of faults (and may still). If this behavior is a change from previous scipy releases, we need to see if there's a related issue (and possibly create one).
https://github.com/scipy/scipy/pull/10207 BUG: Compressed matrix indexing should return a scalar
Looks like it will be fixed in 1.4.
What are they?
They're seemingly an single-element array, like an array with one element.
How do I get the value out of it?
By using:
>>> np.array(0).item()
0
>>>

scipy equivalent of numpy.prod() for sparse matrices

I am looking for an equivalent of numpy.prod to be used with the sparse representations that scipy offers (scipy.sparse). Specifically, I'm trying to compute the product along a single axis. I can do it by first converting to dense (M.todense().prod(axis=0)), but am looking for something more efficient.
For prod reduction operation along each column i.e. axis=0, we would only have non-zero output for columns that have all non-zeros. We can use that fact to have one custom rolled out version, like so -
def sparse_prod_axis0(A):
# Valid mask of row length that has all non-zeros along each col
valid_mask = A.getnnz(axis=0)==A.shape[0] # Thanks to #hpaulj on this!
# Initialize o/p array of zeros
out = np.zeros(A.shape[1],dtype=A.dtype)
# Set valid positions with prod of each col from valid ones
out[valid_mask] = np.prod(A[:,valid_mask].A,axis=0)
return np.matrix(out)
Sample run -
In [92]: from scipy.sparse import csr_matrix
...: a = np.random.randint(0,4,(5,10))
...: A = csr_matrix(a)
...:
In [93]: (A.todense().prod(axis=0))
Out[93]: matrix([[ 0, 0, 6, 48, 0, 0, 0, 0, 72, 0]])
In [94]: sparse_prod_axis0(A)
Out[94]: matrix([[ 0, 0, 6, 48, 0, 0, 0, 0, 72, 0]])

Python: taking the GLCM of a non-rectangular region

I have been using the SLIC implementation of skimage to segment images in superpixels. I would like to use GLCMs to extract additional features from these superpixels for a classification problem. These superpixels are not rectangular. In MATLAB you can set pixels to NaN and they will be ignored by the algorithm (link). I could use this to make bounding boxes around the superpixels and then just setting the unused pixels to NaN.
The greycomatrix function in skimage does not work entirely the same as the MATLAB implementation however. When setting pixels to NaN the function fails on an assert to check if all values are larger than 0.
Is there a Python implementation available which is able to work with nonrectangular ROIs?
Although mahotas is also an excellent computer vision library, there's no need to stop using skimage to do this.
What is necessary, as #Tonechas has pointed out, is to set those NaN values to an integer, since np.nan has type float and the greycomatrix function requires an array of integers.
The easiest option would be setting those NaN's to zero but, if you already have zero values in your pixels and don't want to mix them, you can choose any other constant. After that, all you have to do is filter that chosen value (once again, generally zero) out of the GLCM.
To understand what this means, let's see what skimage tells us about the output of the greycomatrix function:
4-D ndarray
[...] The value P[i,j,d,theta] is the number of times that grey-level j occurs at a distance d and at an angle theta from grey-level i. If normed is False, the output is of type uint32, otherwise it is float64. The dimensions are: levels x levels x number of distances x number of angles.
In other words, the first two dimensions of the array define a matrix that tells us how many times two different values are a certain distant apart. Note that the GLCM does not keep the shape of the input array. Those rows and columns are telling us how the values relate.
Knowing this, it's easy to filter out the values outside our ROI (imagine we've set those NaN's to zero):
glcm = greycomatrix(img, [1], [0]) # Calculate the GLCM "one pixel to the right"
filt_glcm = glcm[1:, 1:, :, :] # Filter out the first row and column
Now you could easily calculate the Haralick properties of your filtered GLCM. For example:
greycoprops(filt_glcm, prop='contrast')
The issue is you have to pass an integer array to greycomatrix, but np.nan has type float (take a look at this thread for details). As a result you cannot encode the pixels outside the ROI as NaN.
An approximate workaround for dealing with non-rectangular ROI's would be setting the pixels outside the ROI to 0 and using the function haralick from mahotas library. This function returns 13 Haralick features extracted from four different GLCM's, corresponding to the four 2-D orientations and the particular value of the distance parameter.
From the documentation:
ignore_zeros can be used to have the function ignore any zero-valued
pixels (as background).
In summary, you need to mask those pixels that fall outside the ROI, and set ignore_zeros to True in the call to haralick.
DEMO
To begin with, let us generate some mock data:
In [213]: import numpy as np
In [214]: shape = (3, 4)
In [215]: levels = 8
In [216]: np.random.seed(2017)
In [217]: x = np.random.randint(0, levels, size=shape)
In [218]: x
Out[218]:
array([[3, 1, 6, 5],
[2, 0, 2, 2],
[3, 7, 7, 7]])
Then we have to remove all the zeros from the image as in this approach the zero intensity level is reserved for the pixels outside the ROI. It is worth to point out that merging intensities 0 and 1 into a single intensity 1 introduces inaccuracies in the results.
In [219]: x[x == 0] = 1
In [220]: x
Out[220]:
array([[3, 1, 6, 5],
[2, 1, 2, 2],
[3, 7, 7, 7]])
Next step consists in defining a mask for the pixels outside the ROI (in this toy example, the four corners of the image) and setting those pixels to 0.
In [221]: non_roi = np.zeros(shape=shape, dtype=np.bool)
In [222]: non_roi[np.ix_([0, -1], [0, -1])] = True
In [223]: non_roi
Out[223]:
array([[ True, False, False, True],
[False, False, False, False],
[ True, False, False, True]], dtype=bool)
In [224]: x[non_roi] = 0
In [225]: x
Out[225]:
array([[0, 1, 6, 0],
[2, 1, 2, 2],
[0, 7, 7, 0]])
We can now perform feature extraction from the GLCM's of a non-rectangular ROI:
In [226]: import mahotas.features.texture as mht
In [227]: features = mht.haralick(x, ignore_zeros=True)
In [228]: features.size
Out[228]: 52
In [229]: features.ravel()
Out[229]: array([ 0.18 , 5.4 , 0.5254833 , ..., 0.81127812,
-0.68810414, 0.96300727])
It may be useful to check how the co-occurrence matrices look. For example, the "pixel to the right" GLCM would be:
In [230]: mht.cooccurence(x, 0)
Out[230]:
array([[0, 1, 0, ..., 0, 1, 2],
[1, 0, 2, ..., 0, 1, 0],
[0, 2, 2, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[1, 1, 0, ..., 0, 0, 0],
[2, 0, 0, ..., 0, 0, 2]])

Categories

Resources