2

I already know that Numpy "double-slice" with fancy indexing creates copies instead of views, and the solution seems to be to convert them to one single slice (e.g. This question). However, I am facing this particular problem where i need to deal with an integer indexing followed by boolean indexing and I am at a loss what to do. The problem (simplified) is as follows:

a = np.random.randn(2, 3, 4, 4)
idx_x = np.array([[1, 2], [1, 2], [1, 2]])
idx_y = np.array([[0, 0], [1, 1], [2, 2]])
print(a[..., idx_y, idx_x].shape) # (2, 3, 3, 2)
mask = (np.random.randn(2, 3, 3, 2) > 0)
a[..., idx_y, idx_x][mask] = 1 # assignment doesn't work

How can I make the assignment work?

3 Answers 3

1

Not sure, but an idea is to do the broadcasting manually and adding the mask respectively just like Tim suggests. idx_x and idx_y both have the same shape (3,2) which will be broadcasted to the shape (6,6) from the cartesian product (3*2)^2.

x = np.broadcast_to(idx_x.ravel(), (6,6))
y = np.broadcast_to(idx_y.ravel(), (6,6))

# this should be the same as
x,y = np.meshgrid(idx_x, idx_y)

Now reshape the mask to the broadcasted indices and use it to select

mask = mask.reshape(6,6)
a[..., x[mask], y[mask]] = 1

The assignment now works, but I am not sure if this is the exact assignment you wanted.

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

7 Comments

This is what I was trying to figure out just now - I was getting myself mixed up with the dimensions! I was going to suggest a solution using np.ravel and np.tile but this is much neater. I'll leave yours as the correct answer ;)
Just tried again with a simple test case - a few notes python a = np.arange(27).reshape([3, 3, 3]); ind_x = np.array([[0, 0], [1, 2]]); ind_y = np.array([[1, 2], [1, 1]]); # x = np.broadcast_to(ind_x.ravel(), (4, 4)); # y = np.broadcast_to(ind_y.ravel(), (4, 4)).T; x, y = np.meshgrid(ind_x, ind_y); mask = a[:, ind_y, ind_x] % 2 == 0; 1. I think y from meshgrid is actually y.T 2. In this 3D example, how should we reshape mask? It shouldn't be hard but I can't get my head around it at the moment!
@TimJim you're right the y from meshgrid is transposed - but the principle remains the same. I am not sure on what the exact input and output should be, maybe OP can provide an example? The code snippet you pasted here is a bit hard to read, maybe you could edit your post with it formatted?
The assignment works but its not the same assignment. You can check with a[..., idx_y, idx_x][mask] (where mask is the original mask, not modified) and not all of them has changed to 1.
oh god i missed the apparent solution. thanks to all your efforts!
|
1

Ok apparently I am making things complicated. No need to combine the indexing. The following code solves the problem elegantly:

b = a[..., idx_y, idx_x]
b[mask] = 1
a[..., idx_y, idx_x] = b
print(a[..., idx_y, idx_x][mask]) # all 1s

2 Comments

D'oh! I'm sure I tried this out when wrapping my head around the re-indexing! I'm glad it works out - typical we all make our lives harder than we need to... time for bed! Nice one.
Nice. a[..., idx_x, idx_y] *= ~mask; a[..., idx_x, idx_y] += mask * 1 should also work if you want to do it arithmetically :)
0

EDIT: Use @Kevin's solution which actually gets the dimensions correct!


I haven't tried it specifically on your sample code but I had a similar issue before. I think I solved it by applying the mask to the indices instead, something like:

a[..., idx_y[mask], idx_x[mask]] = 1

-that way, numpy can assign the values to the a array correctly.


EDIT2: Post some test code as comments remove formatting.

a = np.arange(27).reshape([3, 3, 3])
ind_x = np.array([[0, 0], [1, 2]])
ind_y = np.array([[1, 2], [1, 1]])
x = np.broadcast_to(ind_x.ravel(), (4, 4))
y = np.broadcast_to(ind_y.ravel(), (4, 4)).T
# x1, y2 = np.meshgrid(ind_x, ind_y)  # above should be the same as this
mask = a[:, ind_y, ind_x] % 2 == 0  # what should this reshape to?
# a[..., x[mask], y[mask]] = 1  # Then you can mask away (may also need to reshape a or the masked x or y)

3 Comments

Apparently this does not work. mask is 4-dimensional but idx_y is only 2-dimensional.
@Jerry Sorry, I hadn't checked the dimensions - you should use Kevin's answer. The idea is to apply your second mask to the first and index with that.
I'll try to work through this example, but for now i will stick with the easy solution :) thanks

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.