Reshape PyTorch tensor so that matrices are horizontal - python

I'm trying to combine n matrices in a 3-dimensional PyTorch tensor of shape (n, i, j) into a single 2-dimensional matrix of shape (i, j*n). Here's a simple example where n=2, i=2, j=2:
m = torch.tensor([[[2, 3],
[5, 7]],
[[11, 13],
[17, 19]]])
m.reshape(2, 4)
I was hoping this would produce:
tensor([[ 2, 3, 11, 13],
[ 5, 7, 17, 19]])
But instead it produced:
tensor([[ 2, 3, 5, 7],
[11, 13, 17, 19]])
How do I do this? I tried torch.cat and torch.stack, but they require tuples of tensors. I could try and create tuples, but that seems inefficient. Is there a better way?

To combine n + j with reshape you need them consequent in shape. One can fix it with swapaxes:
m = torch.tensor([[[2, 3],
[5, 7]],
[[11, 13],
[17, 19]]])
m=m.swapaxes( 0,1 )
m.reshape(2, 4)
tensor([[ 2, 3, 11, 13],
[ 5, 7, 17, 19]])

Related

Problem simultaneously indexing several dimensions of a multidimensional numpy array

Consider a 4-dimensional numpy array (variable a). We have a.shape = (16, 5, 66, 717).
From the second dimension containing 4 elements, I want to select the second and the fifth:
b = a[:, [1,4],:,:]
b.shape returns (16, 2, 66, 717), so I guess what I did is correct. Now I want to extract 4 elements from the first dimension (eighth, eleventh, twelfth, thirteenth) and two elements from the second dimension (second and fifth):
b = a[[7,10,12,13,14], [1,4],:,:]
which gives an error:
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (5,) (2,)
I don't understand why this simultaneous indexing across >1 dimensions of numpy array doesn't work. I guess I could sequentially do b = a[:, [1,4],:,:] and c = b[[7,10,12,13,14],:,:,:] to get what I want, but there must be a way to do that in one step. Could you please help?
Make a smaller 3d array:
In [155]: a = np.arange(24).reshape(2,3,4)
In [158]: a
Out[158]:
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]]])
Selecting two "rows" (on the middle dimension):
In [159]: a[:,[0,2],:]
Out[159]:
array([[[ 0, 1, 2, 3],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[20, 21, 22, 23]]])
If we use 2 lists (or arrays) of the same shape, we end up selecting 2 "rows" from [159]:
In [160]: a[[0,1],[0,2],:]
Out[160]:
array([[ 0, 1, 2, 3],
[20, 21, 22, 23]])
If instead the first list/array is a "column vector", we select a (2,2) "block":
In [161]: a[[[0],[1]],[0,2],:]
Out[161]:
array([[[ 0, 1, 2, 3],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[20, 21, 22, 23]]])
ix_ can be used to create the same 2 arrays:
In [162]: np.ix_([0,1],[0,2])
Out[162]:
(array([[0],
[1]]),
array([[0, 2]]))
So using ix_ arrays:
In [163]: I,J = np.ix_([0,1],[0,2])
In [164]: a[I,J,:]
Out[164]:
array([[[ 0, 1, 2, 3],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[20, 21, 22, 23]]])
when I say they broadcast against each other, I mean in the same sense as broadcasting during adding or multiplication:
In [165]: I*10 + J
Out[165]:
array([[ 0, 2],
[10, 12]])
reference: https://numpy.org/doc/stable/user/basics.indexing.html#advanced-indexing
edit
In [166]: np.ix_([7,10,12,13,14], [1,4])
Out[166]:
(array([[ 7],
[10],
[12],
[13],
[14]]),
array([[1, 4]]))
Regarding your error:
In [167]: np.ix_([7,10,12,13,14], [1,4],:,:)
Input In [167]
np.ix_([7,10,12,13,14], [1,4],:,:)
^
SyntaxError: invalid syntax
ix_ is a function. ':' isn't allowed in a function call. It only works in an indexing, where it's converted to a slice. That's why you get a syntax error.

How to add every couple row of matrix?

Assume I have a matrix like
a = np.array([[[ 1, 2], [ 3, 4]],
[[ 5, 6], [ 7, 8]],
[[ 9, 10], [11, 12]],
[[13, 14], [15, 16]]])
The shape is (4, 2, 2). I want to sum the first two and the 2nd two matrices to each other. Final output size should have shape (2, 2, 2) and the output should be
output = np.array([[[ 6, 8], [10, 12]],
[[22, 24], [26, 28]]])
You can see my attempt below:
import numpy as np
a = np.array([[[ 1, 2], [ 3, 4]],
[[ 5, 6], [ 7, 8]],
[[ 9, 10], [11, 12]],
[[13, 14], [15, 16]]])
output = np.add(a[:2], a[2:])
Break the first dimension up into two using a reshape, sum along the second axis:
a.reshape(2, 2, *a.shape[1:]).sum(axis=1)
Your current approach is equivalent to a.reshape(2, 2, *a.shape[1:]).sum(axis=0). The correct way would be to slice every other row of the entire array, rather than every other block of the entire array:
a[::2] + a[1::2]
The latter approach does not generalize well. If you had to add up say every block of seven, you would get
a[::7] + a[1::7] + a[2::7] + a[3::7] + ... + a[6::7]
The former approach is quite flexible, however:
a.reshape(-1, 7, *a.shape[1:]).sum(axis=1)

How to index a 3-D tensor with 2-D tensor in Tensorflow?

I am trying to use a 2-D tensor to index a 3-D tensor in Tensorflow. For example, I have x of shape [2, 3, 4]:
[[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]]
and I want to index it with another tensor y of shape [2, 3], where each element of y index the last dimension of x. For example, if we have y like:
[[0, 2, 3],
[1, 0, 2]]
The output should of shape [2, 3]:
[[0, 6, 11],
[13, 16, 22]]
Create the indices using tf.meshgrid and then use tf.gather_nd to extract the elements:
# create a list of indices for except the last axis
idx_except_last = tf.meshgrid(*[tf.range(s) for s in x.shape[:-1]], indexing='ij')
# concatenate with last axis indices
idx = tf.stack(idx_except_last + [y], axis=-1)
# gather elements based on the indices
tf.gather_nd(x, idx).eval()
# array([[ 0, 6, 11],
# [13, 16, 22]])

splitting ND arrays using numpy

I have a 3D numpy array and I want to partition it by the first 2 dimensions (and select all elements in the last one). Is there a simple way I can do that using numpy?
Example: given array
a = 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]]])
I would like to split it N ways by the first two axes (while retaining all elements in the last one), e.g.,:
a[0:2, 0:2, :], a[2:3, 2:3, :]
But it doesn't need to be evenly split. Seems like numpy.array_split will split on all axes?
In [179]: np.array_split(a,2,0)
Out[179]:
[array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8]],
[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]]]),
array([[[18, 19, 20],
[21, 22, 23],
[24, 25, 26]]])]
is the same as [a[:2,:,:], a[2:,:,:]]
You could loop on those 2 arrays and apply split on the next axis.
In [182]: a2=[np.array_split(aa,2,1) for aa in a1]
In [183]: a2 # edited for clarity
Out[183]:
[[array([[[ 0, 1, 2],
[ 3, 4, 5]],
[[ 9, 10, 11],
[12, 13, 14]]]), # (2,2,3)
array([[[ 6, 7, 8]],
[[15, 16, 17]]])], # (2,1,3)
[array([[[18, 19, 20],
[21, 22, 23]]]), # (1,2,3)
array([[[24, 25, 26]]])]] # (1,1,3)
In [184]: a2[0][0].shape
Out[184]: (2, 2, 3)
In [185]: a2[0][1].shape
Out[185]: (2, 1, 3)
In [187]: a2[1][0].shape
Out[187]: (1, 2, 3)
In [188]: a2[1][1].shape
Out[188]: (1, 1, 3)
With the potential of splitting in uneven arrays in each dimension, it is hard to do this in a full vectorized form. And even if the splits were even it's tricky to do this sort of grid splitting because values are not contiguous. In this example there's a gap between 5 and 9 in the first subarray.
A quick list comprehension will do the trick
[np.array_split(arr, 2, axis=1)
for arr in np.array_split(a, 2, axis=0)]
This will result in a list of lists, the items of which contain the arrays you're looking for.

How to select values in a n-dimensional array

I have been trying to perform a simple operation, but I can't seem to find a simple way to do it using Numpy functions without creating unnecessary copies of the array.
Suppose we have the following 3-dimensional array :
In [171]: x = np.arange(24).reshape((4, 3, 2))
In [172]: x
Out[172]:
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]]])
And the following array :
In [173]: y = np.array([0, 1, 1, 0])
I want to select in x, for each row, the value of the last dimension whose index is the corresponding element in y. In other words, I want :
array([[ 0, 2, 4],
[ 7, 9, 11],
[13, 15, 17],
[18, 20, 22]])
The only solution that I have for now is using a for loop over the first dimension of x and y, as follows :
z = np.zeros((4, 3), dtype=int)
for i, row in enumerate(x):
z[i, :] = row[:, y[i]]
Is there a way of avoiding a for loop here, using numpy functions or fancy indexing?
Thanks!
The tricky aspect is that you don't want all of the 0th-dimension for each slice, you want the slices to correspond to each element in the 0th-dimension. So you could do something like:
>>> x[np.arange(x.shape[0]), :, y]
array([[ 0, 2, 4],
[ 7, 9, 11],
[13, 15, 17],
[18, 20, 22]])
Fancy indexing:
x[np.arange(y.size),:,y]
gives:
array([[ 0, 2, 4],
[ 7, 9, 11],
[13, 15, 17],
[18, 20, 22]])

Categories