3

I want to make an assignment to an array by index array, but there are duplicated indexes.

For example:

a = np.arange(5)
index = np.array([1,2,3,1,2,3,1,2,3])
b = np.arange(9)
a[index] = b

Two questions:

  1. For duplicated indexes, does the latest assignment always take effect?

    Is a[1] == 6 true for any case, e.g. for very large array a? Is it possible a[1] == 0 or 3?

    More specifically, I used numpy compiled with MKL (provided by Anaconda), some array operation are in parallel.

    Related post: Handling of duplicate indices in NumPy assignments

  2. If the answer above is no, is there anything can make sure that the assignment always keep in order?

Syrtis Major
  • 3,791
  • 1
  • 30
  • 40
  • 2
    Possible duplicate of [Handling of duplicate indices in NumPy assignments](https://stackoverflow.com/questions/15973827/handling-of-duplicate-indices-in-numpy-assignments) – alkasm Jun 21 '17 at 09:03
  • 1
    FYI, [Numpy Reference](https://docs.scipy.org/doc/numpy-dev/reference/arrays.indexing.html#detailed-notes) -> "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." – Syrtis Major Jun 21 '17 at 12:29
  • Update of stale link in above comment [Numpy Reference - Indexing](https://numpy.org/doc/stable/reference/arrays.indexing.html#detailed-notes) – Syrtis Major Dec 01 '21 at 13:59

2 Answers2

2

Here's one approach to guarantee the assignment into the last indices from the group of identical indices -

# Get sorting indices for index keeping the order with 'mergesort' option
sidx = index.argsort(kind='mergesort')

# Get sorted index array
sindex = index[sidx]

# Get the last indices from each group of identical indices in sorted version
idx = sidx[np.r_[np.flatnonzero(sindex[1:] != sindex[:-1]), index.size-1]]

# Use those last group indices to select indices off index and b to assign
a[index[idx]] = b[idx]

Sample run -

In [141]: a
Out[141]: array([0, 1, 2, 3, 4])

In [142]: index
Out[142]: array([1, 2, 3, 1, 2, 1, 2, 3, 4, 2])

In [143]: b
Out[143]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [144]: sidx = index.argsort(kind='mergesort')
     ...: sindex = index[sidx]
     ...: idx = sidx[np.r_[np.flatnonzero(sindex[1:] != sindex[:-1]), index.size-1]]
     ...: a[index[idx]] = b[idx]
     ...: 

In [145]: a
Out[145]: array([0, 5, 9, 7, 8])
Divakar
  • 218,885
  • 19
  • 262
  • 358
0

An simpler equivalent to Divakar's solution.

def assign_last(a, index, b):
    """a[index] = b
    """
    index = index[::-1]
    b = b[::-1]

    ix_unique, ix_first = np.unique(index, return_index=True)
    # np.unique: return index of first occurrence.
    # ix_unique = index[ix_first]

    a[ix_unique] = b[ix_first]
    return a

a =  array([0, 1, 2, 3, 4])
index = array([1, 2, 3, 1, 2, 1, 2, 3, 4, 2])
b = array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
assign_last(a, index, b)

Output

array([0, 5, 9, 7, 8])
Syrtis Major
  • 3,791
  • 1
  • 30
  • 40