4

I have a numpy array

src = np.random.rand(320,240)

and another numpy array idx of size (2 x (320*240)). Each column of idx indexes an entry in a result array dst, e.g., idx[:,20] = [3,10] references row 3, column 10 in dst and the assumption is that 20 corresponds to the flattened index of src, i.e., idx establishes a mapping between the entries of src and dst. Assuming dst is initialized with all zeros, how can I copy the entries in src to their destination in dst without a loop?

ASML
  • 199
  • 12
  • What is `res` ? – P. Camilleri May 02 '18 at 22:13
  • Sorry, typo, `res` should be `dst`. Question updated. – ASML May 02 '18 at 22:18
  • A recent similar question: [Reoder the columns of each row of a numpy array based on another numpy array](https://stackoverflow.com/questions/50112085/reoder-the-columns-of-each-row-of-a-numpy-array-based-on-another-numpy-array) – hpaulj May 02 '18 at 22:43

2 Answers2

3

Here is the canonical way of doing it:

>>> import numpy as np
>>> 
>>> src = np.random.rand(4, 3)
>>> src
array([[0.0309325 , 0.72261479, 0.98373595],
       [0.06357406, 0.44763809, 0.45116039],
       [0.63992938, 0.6445605 , 0.01267776],
       [0.76084312, 0.61888759, 0.2138713 ]])
>>> 
>>> idx = np.indices(src.shape).reshape(2, -1)
>>> np.random.shuffle(idx.T)
>>> idx
array([[3, 3, 0, 1, 0, 3, 1, 1, 2, 2, 2, 0],
       [1, 2, 2, 0, 1, 0, 1, 2, 2, 1, 0, 0]])
>>> 
>>> dst = np.empty_like(src)
>>> dst[tuple(idx)] = src.ravel()
>>> dst
array([[0.2138713 , 0.44763809, 0.98373595],
       [0.06357406, 0.63992938, 0.6445605 ],
       [0.61888759, 0.76084312, 0.01267776],
       [0.45116039, 0.0309325 , 0.72261479]])

If you can't be sure that idx is a proper shuffle it's a bit safer to use np.full with a fill value that does not appear in src instead of np.empty.

>>> dst = np.full_like(src, np.nan)
>>> dst[tuple(idx)] = src.ravel()
>>> 
>>> dst
array([[0.27020869, 0.71216066,        nan],
       [0.63812283, 0.69151451, 0.65843901],
       [       nan, 0.02406174, 0.47543061],
       [0.05650845,        nan,        nan]])

If you spot the fill value in dst, something is wrong with idx.

Paul Panzer
  • 51,835
  • 3
  • 54
  • 99
  • Thanks, exactly what I was looking for! Actually, it is quite possible in my case that indices may appear multiple times in idx (and others do not at all). In your version, the number appearing in dst would be the one corresponding to the last appearance of the index in idx, right? Is it possible to control this behavior based on a precedence array p, i.e., if an index appears twice copy the entry whose precedence in p is higher? (if you are curious, this is for multi-view geometry in a renderer) – ASML May 03 '18 at 02:07
  • [Officially](https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html), _For advanced assignments, there is in general no guarantee for the iteration order. This means that if an element is set more than once, it is not possible to predict the final result._ I'm not aware of a quick fix for the precedence. Why don't you make a new question, I think it is an interesting problem. – Paul Panzer May 03 '18 at 02:40
1

You can try:

dst[idx[0, :], idx[1, :]] = src.flat

In [33]: src = np.random.randn(2, 3)

In [34]: src
Out[34]: 
array([[ 0.68636938,  0.60275041,  1.26078727],
       [ 1.17937849, -1.0369404 ,  0.42847611]])

In [35]: dst = np.zeros_like(src)

In [37]: idx = np.array([[0, 1, 0, 1, 0, 0], [1, 2, 0, 1, 2, 0]])

In [38]: dst[idx[0, :], idx[1, :]] = src.flat

In [39]: dst
Out[39]: 
array([[ 0.42847611,  0.68636938, -1.0369404 ],
       [ 0.        ,  1.17937849,  0.60275041]])

dst[0, 1] is src[0, 0], etc.

P. Camilleri
  • 12,664
  • 7
  • 41
  • 76