6

I'm trying to rewrite a function using numpy which is originally in MATLAB. There's a logical indexing part which is as follows in MATLAB:

X = reshape(1:16, 4, 4).';
idx = [true, false, false, true];
X(idx, idx)

ans =

     1     4
    13    16

When I try to make it in numpy, I can't get the correct indexing:

X = np.arange(1, 17).reshape(4, 4)
idx = [True, False, False, True] 
X[idx, idx]
# Output: array([6, 1, 1, 6])

What's the proper way of getting a grid from the matrix via logical indexing?

Amro
  • 123,847
  • 25
  • 243
  • 454
petrichor
  • 6,459
  • 4
  • 36
  • 48

3 Answers3

7

You could also write:

>>> X[np.ix_(idx,idx)]
array([[ 1,  4],
       [13, 16]])
Amro
  • 123,847
  • 25
  • 243
  • 454
  • +1; Didn't know about `np.ix_`. However, this approach has worse performance (2x) than "regular indexing". – root Jun 27 '13 at 07:18
  • 1
    @root: this is different from your approach since it allows the sliced matrix to be modified, while yours give read-only access: http://wiki.scipy.org/NumPy_for_Matlab_Users#head-13d7391dd7e2c57d293809cff080260b46d8e664 – Amro Jun 27 '13 at 07:24
  • True, true. You may want to add that to your answer. – root Jun 27 '13 at 07:26
  • Somehow I don't see the difference between the two approaches, can you give an example that illustrates the difference? – Akavall Jun 27 '13 at 12:07
  • 2
    @Akavall -- Amro's supports assignment, while mine is faster; so they have different usecases. That is `X[np.ix_(idx, idx)]=np.zeros((2,2))` modifies the original array, but `X[idx][:,idx]=np.zeros((2,2))` doesn't. – root Jun 27 '13 at 14:34
4
In [1]: X = np.arange(1, 17).reshape(4, 4)

In [2]: idx = np.array([True, False, False, True])  # note that here idx has to
                                                    # be an array (not a list)
                                                    # or boolean values will be 
                                                    # interpreted as integers

In [3]: X[idx][:,idx]
Out[3]: 
array([[ 1,  4],
       [13, 16]])
root
  • 76,608
  • 25
  • 108
  • 120
2

In numpy this is called fancy indexing. To get the items you want you should use a 2D array of indices.

You can use an outer to make from your 1D idx a proper 2D array of indices. The outers, when applied to two 1D sequences, compare each element of one sequence to each element of the other. Recalling that True*True=True and False*True=False, the np.multiply.outer(), which is the same as np.outer(), can give you the 2D indices:

idx_2D = np.outer(idx,idx)
#array([[ True, False, False,  True],
#       [False, False, False, False],
#       [False, False, False, False],
#       [ True, False, False,  True]], dtype=bool)

Which you can use:

x[ idx_2D ]
array([ 1,  4, 13, 16])

In your real code you can use x=[np.outer(idx,idx)] but it does not save memory, working the same as if you included a del idx_2D after doing the slice.

Saullo G. P. Castro
  • 56,802
  • 26
  • 179
  • 234
  • 2
    You don't save any memory by not assigning that intermediate array to a variable: it gets created, used, and then discarded. The same as if you wrote a `del idx_2D` after the indexing. – Jaime Jun 27 '13 at 12:43