Reputation: 1249
I could not find any question concerning what I want to do so I am asking now. Basically, I want slicing in matrices where the row index depends on the column index.
For example:
>>> import numpy as np
>>> x = np.arange(24).reshape(6,4)
>>> x
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]])
Now what I need is:
array([[12, 9, 6, 3],
[16, 13, 10, 7],
[20, 17, 14, 11]])
So I have a given (fixed) set of column indices. The row indices depend on them. For example: row = col-1:col+1
Is there an efficient way to do that? I know I can iterate and slice the entries I need from every single column, but it seems very inefficient to me.
Upvotes: 0
Views: 131
Reputation: 231335
I was going to leave a diagonal
based solution in the comments, but in time tests it proves to be faster than the strided
one.
In [128]: timeit d = as_strided(x[::-1,:], shape=(3,4),
strides=sz*np.array([-4,-3]))[::-1,:]
10000 loops, best of 3: 44 µs per loop
In [129]: timeit np.array([x[::-1,:].diagonal(i) for i in [-2,-1,0]])
10000 loops, best of 3: 25.9 µs per loop
With a larger x
this probably will not be true. But the diagonal
solution has the advantage that it might be easier to understand.
for a (69,40)
x
,
np.array([x[::-1,:].diagonal(i) for i in range(-(n-1),1)])
produces a (30,40)
array that includes all of the length 40 reverse diagonals.
The as_strided
solution is:
as_strided(x[::-1,:], shape=(n,m), strides=-sz*np.array([m,m-1]))[::-1,:]
It wasn't as easy to figure out how to adjust the values to produce the correct strides. But it is faster. Time basically the same as for the smaller array, while the diagonal
approach slows down with size.
Upvotes: 2
Reputation: 25478
You can use as_strided
to do this:
In [1]: from numpy.lib.stride_tricks import as_strided
In [2]: sz = x.itemsize
In [3]: d = as_strided(x[-1::-1,:], shape=(3,4), strides=sz*np.array([-4,-3]))
In [4]: d
Out[5]:
array([[20, 17, 14, 11],
[16, 13, 10, 7],
[12, 9, 6, 3]])
That is, starting with the original array upside-down, fill a 3x4 array with items taken by striding backwards 4 items for the first coordinate and 3 for the second.
If you want, you can then view d
upside-down:
In [6]: d[-1::-1,:] # or np.flipud(d)
Out[6]:
array([[12, 9, 6, 3],
[16, 13, 10, 7],
[20, 17, 14, 11]])
Upvotes: 4