Retain order when taking unique rows in a NumPy array - python

I have three 2D arrays a1, a2, and a3
In [165]: a1
Out[165]:
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
In [166]: a2
Out[166]:
array([[ 9, 10, 11],
[15, 16, 17],
[18, 19, 20]])
In [167]: a3
Out[167]:
array([[6, 7, 8],
[4, 5, 5]])
And I stacked these arrays into a single array:
In [168]: stacked = np.vstack((a1, a2, a3))
In [170]: stacked
Out[170]:
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[ 9, 10, 11],
[15, 16, 17],
[18, 19, 20],
[ 6, 7, 8],
[ 4, 5, 5]])
Now, I want to get rid of the duplicate rows. So, numpy.unique does the job.
In [169]: np.unique(stacked, axis=0)
Out[169]:
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 4, 5, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[15, 16, 17],
[18, 19, 20]])
However, there is one issue here. The original order is lost when taking the unique rows. How can I retain the original ordering and still take the unique rows?
So, the expected output should be:
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[15, 16, 17],
[18, 19, 20],
[ 4, 5, 5]])

Using return_index
_,idx=np.unique(stacked, axis=0,return_index=True)
stacked[np.sort(idx)]
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[15, 16, 17],
[18, 19, 20],
[ 4, 5, 5]])

after get the stacked array
step 1: get the indexes of the rows for sorted unique array
row_indexes = np.unique(stacked, return_index=True, axis=0)[1]
Note: row_indexes is holding the indexes for sorted array
step 2 : now iterate through the stacked array with the sorted index
sorted_index=sorted(row_indexes)
new_arr=[]
for i in range(len(sorted_index)):
new_arr.append(stacked[sorted_index[i]]
thats it!!!!!

Related

Numpy flatten subarray while maintaining the shape

I have been going over this issue with numpy for a while and cant figure out if there is a intuitive way of converting the array while maintaining the position of the sub-array. The sizes of the array will change depending on the input so doing it manually with concatenate is not an option but i do have the dimensions.
a= np.array([[[0,1],[2,3]],[[4,5],[6,7]],[[8,9],[10,11]],[[12,13],[14,15]]])
reshaping just flattens the array like
[1,2,3,4]
[5,6,7,8]
etc
I have also tried np.block but besides setting the positions manually i have not had any success
The result i would like to get in this case is (4,4):
[[ 0, 1, 4, 5],
[ 2, 3, 6, 7],
[ 8, 9,12,13],
[10,11,14,15]]
Does anyone of you smart people know if there is something in numpy that i could use to get this result?
Your original has the 16 consecutive values reshaped into 4d array:
In [67]: x=np.arange(16).reshape(2,2,2,2)
In [68]: x
Out[68]:
array([[[[ 0, 1],
[ 2, 3]],
[[ 4, 5],
[ 6, 7]]],
[[[ 8, 9],
[10, 11]],
[[12, 13],
[14, 15]]]])
Reshape to (4,4) keeps that original order - see the 0,1,2,3...
In [69]: x.reshape(4,4)
Out[69]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
You want to swap values:
In [70]: x.transpose(0,2,1,3)
Out[70]:
array([[[[ 0, 1],
[ 4, 5]],
[[ 2, 3],
[ 6, 7]]],
[[[ 8, 9],
[12, 13]],
[[10, 11],
[14, 15]]]])
which can then be reshaped to (4,4):
In [71]: x.transpose(0,2,1,3).reshape(4,4)
Out[71]:
array([[ 0, 1, 4, 5],
[ 2, 3, 6, 7],
[ 8, 9, 12, 13],
[10, 11, 14, 15]])

Create multidimensional torch tensor time series

I want to create a torch tensor sequence out of a multi-dimensional numpy array. I've achieved it with 1-d arrays, but can't find the proper way with more dimensions...
This is a 1-d vector example:
import numpy as np
import torch
n = np.arange(10)
t = torch.tensor([n[i: i + 3] for i in range(7)])
Being the output:
tensor([[0, 1, 2],
[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[4, 5, 6],
[5, 6, 7],
[6, 7, 8]])
Let's say that instead of a 1-d vector I have a 2-d one.
import numpy as np
import torch
n = np.array([np.arange(10), np.arange(10, 20)])
t = torch.tensor([n[..., i: i + 3] for i in range(7)]).view(2, -1, 3)
The output is:
tensor([[[ 0, 1, 2],
[10, 11, 12],
[ 1, 2, 3],
[11, 12, 13],
[ 2, 3, 4],
[12, 13, 14],
[ 3, 4, 5]],
[[13, 14, 15],
[ 4, 5, 6],
[14, 15, 16],
[ 5, 6, 7],
[15, 16, 17],
[ 6, 7, 8],
[16, 17, 18]]])
And what I’m looking for is:
tensor([[[ 0, 1, 2],
[ 1, 2, 3],
[ 2, 3, 4],
[ 3, 4, 5],
[ 4, 5, 6],
[ 5, 6, 7],
[ 6, 7, 8]],
[[10, 11, 12],
[11, 12, 13],
[12, 13, 14],
[13, 14, 15],
[14, 15, 16],
[15, 16, 17],
[16, 17, 18]]])
As you can see the rows are alternated... Is there any way of achieving it?
P.D: In case there is a more elegant solution to solve the problem, I will be very grateful too! I've tried with methods as torch.repeat_interleave but couldn't achieve anything...
Thanks a lot!
It is not very clear what formula you want to implement, but looks like it should be t[i, j, k] = i*10 + j + k. This formula is nothing but the outer sum of the three index ranges. The most straightforward way to obtain it is
t = torch.tensor(np.add.outer(np.arange(2)*10, np.add.outer(np.arange(7), np.arange(3))))
Which gives
tensor([[[ 0, 1, 2],
[ 1, 2, 3],
[ 2, 3, 4],
[ 3, 4, 5],
[ 4, 5, 6],
[ 5, 6, 7],
[ 6, 7, 8]],
[[10, 11, 12],
[11, 12, 13],
[12, 13, 14],
[13, 14, 15],
[14, 15, 16],
[15, 16, 17],
[16, 17, 18]]], dtype=torch.int32)
You can achieve this by concatenating the sub-tensors along the appropriate axis:
n = np.array([np.arange(10), np.arange(10, 20)])
t = torch.tensor([n[..., i: i + 3] for i in range(7)])
t = torch.cat([_ for _ in t.unsqueeze(-2)], dim=1)

How to slice multidimensional array with Numpy, multiple columns?

I am generating multidimensional array of different sizes, though they'll all have an even number of columns.
>> import numpy as np
>> x = np.arange(24).reshape((3,8))
Which results in:
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]])
I am able to slice with numpy and get the first column in an array:
>> newarr = x[0:,0:2]
array([[ 0, 1],
[ 8, 9],
[16, 17]])
However, I want to have one array that is just a list of the columns where column 1 and 2 are together, 3 and 4 are together, and so on.. For example:
array([[[ 0, 1],
[ 8, 9],
[16, 17]],
[[ 2, 3],
[10, 11],
[18, 19]],
etc....]
)
This code below works but it's clunky and my arrays are not all the same. Some arrays have 16 columns, some have 34, some have 50, etc.
>> newarr = [x[0:,0:2]]+[x[0:,2:4]]+[x[0:,4:6]]
[array([[ 0, 1],
[ 8, 9],
[16, 17]]), array([[ 2, 3],
[10, 11],
[18, 19]])]
There's got to be a better way to do this than
newarr = [x[0:,0:2]]+[x[0:,2:4]]+[x[0:,4:6]]+...+[x[0:,n:n+2]]
Help!
My idea is adding a for loop:
slice_len = 2
x_list = [x[0:, slice_len*i:slice_len*(i+1)] for i in range(x.shape[1] // slice_len)]
Output:
[array([[ 0, 1],
[ 8, 9],
[16, 17]]), array([[ 2, 3],
[10, 11],
[18, 19]]), array([[ 4, 5],
[12, 13],
[20, 21]]), array([[ 6, 7],
[14, 15],
[22, 23]])]

Stacking matrices to make a matrix with sites of parent matrices map as block diagonals

How stack matrices as follows in python such that elements of parent matrices make a block diagonal at the same block diagonal site of the daughter matrix.
example:
I have four matrices AA,AB,BA, BB
I want to make the matrix out as shown in attached image.
In [35]: arr = np.arange(1,17).reshape(4,4)
In [36]: arr2 = arr.reshape(2,2,2,2)
In [37]: arr2
Out[37]:
array([[[[ 1, 2],
[ 3, 4]],
[[ 5, 6],
[ 7, 8]]],
[[[ 9, 10],
[11, 12]],
[[13, 14],
[15, 16]]]])
I did some trial and errors with transpose idea but didn't get any where.
But lets step back an try sliced insertion:
In [42]: out = np.zeros_like(arr)
In [43]: out[::2,::2]=arr2[0,0]
In [44]: out[::2,1::2]=arr2[0,1]
In [45]: out
Out[45]:
array([[1, 5, 2, 6],
[0, 0, 0, 0],
[3, 7, 4, 8],
[0, 0, 0, 0]])
This seems to be a workable solution. That could be put into a loop (or 2).
In [50]: out = np.zeros_like(arr)
In [51]: for i,j in np.ndindex(2,2):
...: out[i::2,j::2] = arr2[i,j]
...:
In [52]: out
Out[52]:
array([[ 1, 5, 2, 6],
[ 9, 13, 10, 14],
[ 3, 7, 4, 8],
[11, 15, 12, 16]])
Splitting out into the 4d array may help us visualize a transformation from Out[37]:
In [57]: out.reshape(2,2,2,2)
Out[57]:
array([[[[ 1, 5],
[ 2, 6]],
[[ 9, 13],
[10, 14]]],
[[[ 3, 7],
[ 4, 8]],
[[11, 15],
[12, 16]]]])
But maybe the more obvious iterative solution is fast enough.
This, for example, creates the correct 2x2 blocks:
In [59]: arr2.transpose(0,2,3,1)
Out[59]:
array([[[[ 1, 5],
[ 2, 6]],
[[ 3, 7],
[ 4, 8]]],
[[[ 9, 13],
[10, 14]],
[[11, 15],
[12, 16]]]])
and one more swap:
In [62]: arr2.transpose(2,0,3,1).reshape(4,4)
Out[62]:
array([[ 1, 5, 2, 6],
[ 9, 13, 10, 14],
[ 3, 7, 4, 8],
[11, 15, 12, 16]])

Viewing cells of a grid in sliding windows with periodic boundaries

Consider a 2D array
>>> A = np.array(range(16)).reshape(4, 4)
>>> A
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
I would like to construct a function f(i,j) which pulls a 3x3 block from elements surrounding A[i,j] with periodic boundary conditions.
For example a non-boundary element would be
>>> f(1,1)
array([[ 0, 1, 2],
[ 4, 5, 6],
[ 8, 9, 10]])
and a boundary element would be
>>> f(0,0)
array([[15, 12, 13],
[ 3, 0, 1],
[ 7, 4, 5]])
view_as_windows comes close but does not wrap around periodic boundaries.
>>> from skimage.util.shape import view_as_windows
>>> view_as_windows(A,(3,3))
array([[[[ 0, 1, 2],
[ 4, 5, 6],
[ 8, 9, 10]],
[[ 1, 2, 3],
[ 5, 6, 7],
[ 9, 10, 11]]],
[[[ 4, 5, 6],
[ 8, 9, 10],
[12, 13, 14]],
[[ 5, 6, 7],
[ 9, 10, 11],
[13, 14, 15]]]])
In this case view_as_windows(A)[0,0] == f(1,1) but f(0,0) is not in view_as_windows(A). I need a view_as_windows(A) type array which has the same number of elements as A, where each element has shape (3,3)
Simply pad with wrapping functionality using np.pad and then use Scikit's view_as_windows -
from skimage.util.shape import view_as_windows
Apad = np.pad(A,1,'wrap')
out = view_as_windows(Apad,(3,3))
Sample run -
In [65]: A
Out[65]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
In [66]: Apad = np.pad(A,1,'wrap')
In [67]: out = view_as_windows(Apad,(3,3))
In [68]: out[0,0]
Out[68]:
array([[15, 12, 13],
[ 3, 0, 1],
[ 7, 4, 5]])
In [69]: out[1,1]
Out[69]:
array([[ 0, 1, 2],
[ 4, 5, 6],
[ 8, 9, 10]])

Categories

Resources