Slicing non-continous indexes in 2d Numpy array - python

I have a matrix, i.e. 2D numpy array and would like to be able to slice out non-continuous parts of it. For example with the following matrix
[[11 12 13 14 15]
[21 22 23 24 25]
[31 32 33 34 35]
[41 42 43 44 45]
[51 52 53 54 55]]
i would like to be able to extract, say
[[11 12 14 15]
[21 22 24 25]
[51 52 54 55]]
Is there a way to do this?
I can easily extract continuous slice, for example matrix[0:2,0:3] would return
[[11 12 13]
[21 22 23]
but not sure how to extract non-continuous. I read about using np.r_[], which works if used on one dimension only, but not on both.
The solution would need to be scalable as well as it would have to be used on large matrices with many non-continuous indexes (which I was thinking would be passed as list).

You could use NumPy's advanced indexing and ix_() function to index the cross product of two 1-D sequences, the first one containing the indices of the rows you want to extract and the second one containing the indices of the columns.
In [24]: import numpy as np
In [25]: arr = np.asarray([[11, 12, 13, 14, 15],
...: [21, 22, 23, 24, 25],
...: [31, 32, 33, 34, 35],
...: [41, 42, 43, 44, 45],
...: [51, 52, 53, 54, 55]])
...:
In [26]: rows = [0, 1, 4]
In [27]: cols = [0, 1, 3, 4]
In [28]: arr[np.ix_(rows, cols)]
Out[28]:
array([[11, 12, 14, 15],
[21, 22, 24, 25],
[51, 52, 54, 55]])
It is worth pointing out that extending the approach above to index N-dimensional arrays is straightforward. You just need to pass further 1-D sequences to np.ix_.

You can used chained indexing:
arr = np.array([[11, 12, 13, 14, 15],
[21, 22, 23, 24, 25],
[31, 32, 33, 34, 35],
[41, 42, 43, 44, 45],
[51, 52, 53, 54, 55]])
In [28]: arr[[0,1,4]][:, [0,1,3,4]]
Out[28]:
array([[11, 12, 14, 15],
[21, 22, 24, 25],
[51, 52, 54, 55]])

You can do it like this and your list idea works perfectly with it. In delete, you can pass a list instead of just 2
q = np.array([[11, 12, 13, 14, 15],
[21, 22, 23, 24, 25],
[31, 32, 33, 34, 35],
[41, 42, 43, 44, 45],
[51, 52, 53, 54, 55]])
np.delete(q,2, axis=1)[np.array([0,1,4])]
array([[11, 12, 14, 15],
[21, 22, 24, 25],
[51, 52, 54, 55]])

Related

Indexing every other 2x2 block in 2D array

Say I have an array that looks like
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, 27, 28, 29, 30, 31],
[32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47],
[48, 49, 50, 51, 52, 53, 54, 55],
[56, 57, 58, 59, 60, 61, 62, 63]])
And I wanted to extract the following array:
array([[ 0, 1, 4, 5],
[ 8, 9, 12, 13],
[32, 33, 36, 37],
[40, 41, 44, 45]])
Essentially it's the top-left 2x2 block in every 4x4 macro-block.
I saw this example in 1D, but couldn't figure out the 2D case.
Another way I can think of is:
h, w = full.shape
X, Y = np.meshgrid(np.arange(w), np.arange(h))
tl = full[(X%4<2) & (Y%4<2)].reshape((h//2,-1))
But I wonder if there's a cleaner way of doing this.
Here's one way you could do it:
In [73]: a
Out[73]:
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, 27, 28, 29, 30, 31],
[32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47],
[48, 49, 50, 51, 52, 53, 54, 55],
[56, 57, 58, 59, 60, 61, 62, 63]])
In [74]: nr, nc = [s // 2 for s in a.shape] # Shape of the new array
In [75]: b = a.reshape((nr, 2, nc, 2))[::2, :, ::2, :].reshape(nr, nc)
In [76]: b
Out[76]:
array([[ 0, 1, 4, 5],
[ 8, 9, 12, 13],
[32, 33, 36, 37],
[40, 41, 44, 45]])
By specifying the index array, so if a.shape[0] % 2 == 0 (even number):
Note: these methods can handle not only when a.shape[0] % 4 == 0, but also a.shape[0] % 2 == 0 (for all even numbers).
First method:
using advance indexing:
w = 2
ind = np.arange(a.shape[1]).reshape(-1, w)[::2].ravel() # [0 1 4 5]
b = a[ind[:, None], ind[None, :]]
Second method:
by np.delete:
w = 2
ind = np.arange(a.shape[1]).reshape(-1, w)[1::2].ravel() # [2 3 6 7]
b = np.delete(a, ind, axis=0)
b = np.delete(b, ind, axis=1)
Third method:
by splitting and stacking as:
b = np.asarray(np.hsplit(a, a.shape[0] // 2)[::2])
# [[[ 0 1] [[ 4 5]
# [ 8 9] [12 13]
# [16 17] [20 21]
# [24 25] , [28 29]
# [32 33] [36 37]
# [40 41] [44 45]
# [48 49] [52 53]
# [56 57]] [60 61]]]
b = np.asarray(np.vsplit(np.hstack(b), a.shape[0] // 2)[::2])
# [[[ 0 1 4 5]
# [ 8 9 12 13]]
# ,
# [[32 33 36 37]
# [40 41 44 45]]]
b = np.vstack(b).squeeze()

Modifying a part of the main diagonal of a 2d-numpy array

I'm having problems with the following task.
Assume we have a matrix, looking like this:
Mat = np.array([
[11, 12, 13, 14, 15], \
[21, 22, 23, 24, 25], \
[31, 32, 33, 34, 35], \
[41, 42, 43, 44, 45], \
[51, 52, 53, 54, 55]])
What I want to do is to replace the entries 22, 33 and 44 with something different that I calculated before. I know I could do this with for loops but I think there has to be a more elegant way.
I have something like this in mind:
Subselect the main diagonal from [1,1] to [-2,-2] and save it as an array.
Modify this array in the desired manner.
Save the modified array as part of the main diagonal of the matrix.
I found the np.diagonal() to get the diagonal and got so far:
Mat = np.array([
[11, 12, 13, 14, 15], \
[21, 22, 23, 24, 25], \
[31, 32, 33, 34, 35], \
[41, 42, 43, 44, 45], \
[51, 52, 53, 54, 55]])
print(Mat)
snipA = Mat.diagonal()
snipB = snipA[1:len(snipA)-1]
print(snipA)
print(snipB)
There are two problems now. First, I cannot modify snipB in any way. I get the error: "output array is read-only". Second, how can I save a modified snipB into the matrix again?
Any help is appreciated.
You can index and modify a part of the diagonal like so:
>>> subdiag = np.arange(1, len(mat)-1)
>>> mat[subdiag, subdiag]
array([22, 33, 44])
>>> mat[subdiag, subdiag] = 0
>>> mat
array([[11, 12, 13, 14, 15],
[21, 0, 23, 24, 25],
[31, 32, 0, 34, 35],
[41, 42, 43, 0, 45],
[51, 52, 53, 54, 55]])
>>>
>>> mat[subdiag, subdiag] = [22, 33, 44]
>>> mat
array([[11, 12, 13, 14, 15],
[21, 22, 23, 24, 25],
[31, 32, 33, 34, 35],
[41, 42, 43, 44, 45],
[51, 52, 53, 54, 55]])
You can also do this with einsum since numpy 1.10
np.einsum('ii->i', mat)[1:-1] = 0
mat
array([[11, 12, 13, 14, 15],
[21, 0, 23, 24, 25],
[31, 32, 0, 34, 35],
[41, 42, 43, 0, 45],
[51, 52, 53, 54, 55]])

numpy 3 dimension array middle indexing bug

I seems found a bug when I'm using python 2.7 with numpy module:
import numpy as np
x=np.arange(3*4*5).reshape(3,4,5)
x
Here I got the full 'x' array as follows:
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, 27, 28, 29],
[30, 31, 32, 33, 34],
[35, 36, 37, 38, 39]],
[[40, 41, 42, 43, 44],
[45, 46, 47, 48, 49],
[50, 51, 52, 53, 54],
[55, 56, 57, 58, 59]]])
Then I try to indexing single row values in sheet [1]:
x[1][0][:]
Result:
array([20, 21, 22, 23, 24])
But something wrong while I was try to indexing single column in sheet [1]:
x[1][:][0]
Result still be the same as previous:
array([20, 21, 22, 23, 24])
Should it be array([20, 25, 30, 35])??
It seems something wrong while indexing the middle index with range?
No, it's not a bug.
When you use [:] you are using slicing notation and it takes all the list:
l = ["a", "b", "c"]
l[:]
#output:
["a", "b", "c"]
and in your case:
x[1][:]
#output:
array([[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29],
[30, 31, 32, 33, 34],
[35, 36, 37, 38, 39]])
What you realy wish is using numpy indexing notation:
x[1, : ,0]
#output:
array([20, 25, 30, 35])
This is not a bug. x[1][:][0] is not a multiple index ("give me the elements where first dimension is 1, second is any, third is 0"). Instead, you are indexing three times, three objects.
x1 = x[1] # x1 is the first 4x5 subarray
x2 = x1[:] # x2 is same as x1
x3 = x2[0] # x3 is the first row of x2
To use multiple index, you want to do it in a single slice:
x[1, :, 0]

python munkres.py cost matrix

is it possible to allow munkres.py to accept a cost matrix that have more row than column? From it's code shown in github, it seems like it will only pad the row, when the cost matrix have more column than row.
It works either way. The source for the padding function is here.
You can verify it works both ways with this code:
>>> m = Munkres()
# 4 rows, 3 columns
>>> matrix = [[11, 12, 13], [21, 22, 23], [31, 32, 33], [41, 42, 43]]
>>> m.pad_matrix(matrix)
[[11, 12, 13, 0], [21, 22, 23, 0], [31, 32, 33, 0], [41, 42, 43, 0]]
# 3 rows, 4 columns
>>> matrix = [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34]]
>>> m.pad_matrix(matrix)
[[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [0, 0, 0, 0]]

Python NumPy Convert Columns to Rows

Python 2.7.10 and NumPy. I have a matrix like this:
[[[ 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]
[27 28 29]
[30 31 32]
[33 34 35]]
[[36 37 38]
[39 40 41]
[42 43 44]
[45 46 47]]]
Note: The real matrix will have real data, and not consecutive numbers.
I need to rotate, flip, or something (I have tried them all) so as to end up with this:
[[[ 2 5 8 11]
[ 1 4 7 10]
[ 0 3 6 9]
[[14 17 20 23]
[13 16 19 22]
[12 15 18 21]
[[26 29 32 35]
[25 28 31 34]
[24 27 30 33]
[[38 41 44 47]
[37 40 43 46]
[36 39 42 45]]]
Basically, I need the entire columns of the matrix to become the rows.
Thanks.
Flip the positions of columns with [:,:,::-1] and use np.transpose to swap rows with columns -
In [25]: A
Out[25]:
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],
[27, 28, 29],
[30, 31, 32],
[33, 34, 35]]])
In [26]: A[:,:,::-1].transpose(0,2,1)
Out[26]:
array([[[ 2, 5, 8, 11],
[ 1, 4, 7, 10],
[ 0, 3, 6, 9]],
[[14, 17, 20, 23],
[13, 16, 19, 22],
[12, 15, 18, 21]],
[[26, 29, 32, 35],
[25, 28, 31, 34],
[24, 27, 30, 33]]])
Here's a simpler way to do it:
a=numpy.arange(48).reshape((4,4,3)
numpy.fliplr(a.swapaxes(1,2))
#or you could do
numpy.fliplr(a.transpose(0,2,1))
From what I can tell, flipud flips the last dimension, while fliplr flips the second to last dimension. In three dimensions, the last dimension is Z, while the second to last dimension is Y. Hence transposing the data, and flipping the Y dimension works.
Enjoy.
For each 2d subarray in your super-array you can apply the numpy function:
np.rot90() http://docs.scipy.org/doc/numpy/reference/generated/numpy.rot90.html
so:
import numpy as np
array= np.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],
[27, 28, 29],
[30, 31, 32],
[33, 34, 35]],
[[36, 37, 38],
[39, 40, 41],
[42, 43, 44],
[45, 46, 47]]])
desired_output = np.array([np.rot90(sub_array) for sub_array in array])
transpose and flipud are what you are looking for; the swapaxes can also function as transpose
Note that transpose has a version that operates on multiple dimensions.
There may be a simpler expression for this, but this has the advantage of not using elaborate indexing. Example, done in Python 2.7.3 with numpy
f=numpy.flipud
a=numpy.arange(48).reshape((4,4,3))
result=f(f(f(a).T).T).transpose(0,2,1)
In [2]: a=numpy.arange(48).reshape((4,4,3))
Out[3]:
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],
[27, 28, 29],
[30, 31, 32],
[33, 34, 35]],
[[36, 37, 38],
[39, 40, 41],
[42, 43, 44],
[45, 46, 47]]])
In [5]: f(f(f(a).T).T).transpose(0,2,1)
Out[5]:
array([[[ 2, 5, 8, 11],
[ 1, 4, 7, 10],
[ 0, 3, 6, 9]],
[[14, 17, 20, 23],
[13, 16, 19, 22],
[12, 15, 18, 21]],
[[26, 29, 32, 35],
[25, 28, 31, 34],
[24, 27, 30, 33]],
[[38, 41, 44, 47],
[37, 40, 43, 46],
[36, 39, 42, 45]]])
.

Categories

Resources