1

I would like to assign values of an array arr_2 to a different array arr_1. However, I want to do this based on 2 selection criteria. As a working example, I define my selection criteria as such

import numpy as np

# An array of -1 values of shape(10,): [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1]
arr_1 = np.zeros(10, dtype=int) - 1
# An array of 0-9 values of shape(10,): [0 1 2 3 4 5 6 7 8 9]
arr_2 = np.arange(10)

# Create an initial selection of values we want to change
# In this example: even indices: [ T F T F T F T F T F]
selection_a = np.arange(10) % 2 == 0

# Create a second selection based on selection_a: [F F F T T]
selection_b = arr_2[selection_a] > 5

Based on these two selection criteria I would like to assign the values of arr_2 where both conditions hold to the array arr_1. I.e. equivalent to [F F F F F F T F T F].

arr_1[selection_a][selection_b] = arr_2[selection_a][selection_b]

If I inspect both sides of the equation before the assignment, they yield the values that I expect:

print(arr_1[selection_a][selection_b]) # yields [-1 -1]
print(arr_2[selection_a][selection_b]) # yields [ 6, 8]

However, the assignment itself does not assign the values, i.e. arr_1 remains unchanged. My question is, why is this the case?

NB: I know that in most (and maybe even all cases) this can be circumvented by creating a single criterion, however I want to know why using 2 separate criteria won't work.

If anything is unclear, please let me know and I'll try to clarify.

Edit

I investigated this a bit further and the problem seems to be in the left hand side of the equation as something like

arr_1[selection_a][selection_b] = 5

does not work either.

6
  • I'd do something like: idx = arr_2[selection_a][selection_b]; arr_1[idx] = idx Commented Sep 18, 2018 at 9:57
  • Define selection_b as arr_2 > 5 & selection_a then use arr_1[selection_b] = 5 Commented Sep 18, 2018 at 9:57
  • @GiacomoAlzetta selection_a and selection_b have different shapes. Commented Sep 18, 2018 at 9:59
  • 1
    @PM2Ring Not if you define them in that way. What I'm saying is: instead of having selection_b depend on the result in selection_a requiring a nested selection, just define it with the same shape as selection_a and use & to "filter". You can always rewrite a nested condition [a][b][c][d] into [a& b' & c' & d'] where the primed versions b', c', d' (ecc.) have the same shape as a. Commented Sep 18, 2018 at 10:28
  • The key is that arr_1[selection_a] is a copy, not a view. The assignment is modifying a portion of that copy, not a portion of the original. numpy is not parsing the 2 criteria together. Python is performing two separate indexing operations. Commented Sep 19, 2018 at 3:03

4 Answers 4

3

A single use of [...] operators creates a selection in an array, from which you can read and to which you can write. A second use of [...] will read values with no problem. But as far as writing is concerned, the second access will be to a temporary array created to match the selection rules (i.e. a temporary of shape (2,) that is a copy of the original data) in your situation.

Edit: Boolean indexing is part of so-called "Advanced Indexing". Keeping the indexing to simple slices avoids these copy issues.

Sign up to request clarification or add additional context in comments.

Comments

1

Adding to Pierre de Buyl solution, If you want to get your desired result of changing arr_1 array. You can use np.where as follows:

arr_1[selection_a] = np.where(arr_2[selection_a]>5,arr_2[selection_a],arr_1[selection_a])

arr_1

Which gives

array([-1, -1, -1, -1, -1, -1,  6, -1,  8, -1])

Comments

1

It has to do with = in python being interpreted as .__setitem__(), which can pick the memory addresses from the assigned variable. However, it doesn't do this recursively, so while

a[boolean_mask] = 0 

works, as the indexing points to the memory in a,

a[mask1][mask2] = 0

does not, as the indexing points to the memory in a[mask1], which is a copy.

Comments

0

Since the conditions of selection_a and selection_b do not depend on each other, you could just create a combined mask

selection_c = (np.arange(10) % 2 == 0) & (arr_2 >  5)

and use it like

arr_1[selection_c] = 5

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.