Given that I have a square matrix of indices, such as:
idxs = np.array([[1, 1],
[0, 1]])
and an array of square matrices of the same size as each other (not necessarily the same size as idxs
):
mats = array([[[ 0. , 0. ],
[ 0. , 0.5]],
[[ 1. , 0.3],
[ 1. , 1. ]]])
I'd like to replace each index in idxs
with the corresponding matrix in mats
, to obtain:
array([[ 1. , 0.3, 1. , 0.3],
[ 1. , 1. , 1. , 1. ],
[ 0. , 0. , 1. , 0.3],
[ 0. , 0.5, 1. , 1. ]])
mats[idxs]
gives me a nested version of this:
array([[[[ 1. , 0.3],
[ 1. , 1. ]],
[[ 1. , 0.3],
[ 1. , 1. ]]],
[[[ 0. , 0. ],
[ 0. , 0.5]],
[[ 1. , 0.3],
[ 1. , 1. ]]]])
and so I tried using reshape
, but 'twas in vain! mats[idxs].reshape(4,4)
returns:
array([[ 1. , 0.3, 1. , 1. ],
[ 1. , 0.3, 1. , 1. ],
[ 0. , 0. , 0. , 0.5],
[ 1. , 0.3, 1. , 1. ]])
If it helps, I found that skimage.util.view_as_blocks
is the exact inverse of what I need (it can convert my desired result into the nested, mats[idxs]
form).
Is there a (hopefully very) fast way to do this? For the application, my mats
will still have just a few small matrices, but my idxs
will be a square matrix of up to order 2^15, in which case I'll be replacing over a million indices to create a new matrix of order 2^16.
Thanks so much for your help!
We are indexing into the first axis of the input array with those indices. To get the
2D
output, we just need to permute axes and reshape afterwards. Thus, an approach would be withnp.transpose
/np.swapaxes
andnp.reshape
, like so -Sample run -
Performance boost with
np.take
for repeated indicesWith repeated indices, for performance we are better off using
np.take
by indexing alongaxis=0
. Let's list out both these approaches and time it withidxs
having many repeated indices.Function definitions -
Runtime test -
Thus, we are seeing an overall improvement of
1.5x+
.Just to get a sense of the improvement with
np.take
, let's time the indexing part alone -For those datasizes, its
2.5x+
. Not bad!