Reputation: 1171
I need to slice a moderately sized 2d Numpy array along two dimensions. As example,
import numpy as np
X = np.random.normal(loc=0, scale=1, size=(3000, 100))
From this array, I need to select a large number of rows and a rather small number of columns, e.g.
row_idx = np.random.random_integers(0, 2999, 2500)
col_idx = np.random.random_integers(0, 99, 10)
Right now, I do this by the following command:
X.take(col_idx, axis=1).take(row_idx, axis=0)
This takes approximately 115µs on my computer. The problem is that I need to do this step several million times per run.
Do you see any chance to speed this up?
Edit (additional information): I have a matrix X which is nxk. The n rows contain 1xk vectors. There are three sets: an active set (V), a left set (L) and a right set (R). Moreover, there are coefficients v0 and v. I need to compute this quantity: http://goo.gl/KNoSl3 (sorry, I can't post images). The formula from the question selects all rows of X which are in the left (right) set and all columns that are in the active set.
Edit 2
I found another small improvement.
X.take(col_idx, axis=1, mode='clip').take(row_idx, axis=0, mode='clip')
is a little faster (roughly 25% on my machine).
Upvotes: 2
Views: 781
Reputation: 19547
Lets do something where we make a 1D array of indices that satisfy our conditions for a n-dimensional grid.
def make_multi_index(arr, *inds):
tmp = np.meshgrid(*inds, indexing='ij')
idx = np.vstack([x.ravel() for x in tmp])
return np.ravel_multi_index(idx, X.shape)
Using your test arrays and the original case for reference:
%timeit X.take(col_idx, axis=1).take(row_idx, axis=0)
10000 loops, best of 3: 95.4 µs per loop
Lets use this functional to build indices, hold them, and then use take to return your desired output:
inds = make_multi_index(X, row_idx, col_idx)
tmp = np.take(X,inds).reshape(row_idx.shape[0], col_idx.shape[0])
np.allclose(tmp, X.take(col_idx, axis=1).take(row_idx, axis=0))
Out[128]: True
So building our indices and keeping them appears to work, now for timings:
%timeit make_multi_index(X, row_idx, col_idx)
1000 loops, best of 3: 356 µs per loop
%timeit np.take(X,inds).reshape(row_idx.shape[0], col_idx.shape[0])
10000 loops, best of 3: 59.9 µs per loop
So as it happens not terribly great- this probably gets better with more dimension that you would like to take from. Anyhow if you keep these indices for more then 10-15 iterations it could help some or if you add an additional dimension and take all inactive datasets simultaneously.
Upvotes: 1
Reputation: 17847
You could use two-dimensional fancy indexing:
X[row_idx,col_idx[:,None]]
But that takes ~1ms on my machine, vs ~300us using your method.
It seems like your method is the best you can do, unless you have additional information about the values in row_idx
and col_idx
.
Upvotes: 0