9

I have two three dimensional arrays, a and b, and want to find the 2D subarray of b with the elements where a had a minimum along the third axis, i.e.

a=n.random.rand(20).reshape((5,2,2))
b=n.arange(20).reshape((5,2,2))
c=n.argmin(a,2) #indices with minimal value of a
d=n.zeros_like(c) #the array I want
for i in range(5):
  for j in range(2):
     d[i,j] = b[i,j,c[i,j]] 

Is there a way I can get these values without the double loop?

I am aware of this answer: replace min value to another in numpy array but if I want this to work for my 3D arrays I'd have to do a lot of reshaping operations - and I'm wondering if there is something simpler.

3 Answers 3

3

You can use np.ogrid to create a grid for the other dimensions:

x, y, z = arr.shape  # assuming your array is named "arr"
xyz = np.ogrid[0:x, 0:y] + [c]  # c is your second axis index (the argmin)
arr[xyz]

If it's not the last axis then you can simply use insert because ogrid returns a normal python list containing the indices.

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

3 Comments

Just to check: n.ogrid[0:x, 0:y] + [c] adds a third dimension to the ogrid-array, with the elements of c, correct? So this seems very similar to the fancy-indexing answer (which I've accepted as it's even shorter, but this is interesting - thanks!).
@mzzx Basically similar answers, as both create broadcasted range arrays to index into the data array. Difference is just in the way those range arrays are created. My post does the broadcasting explicitly, whereas this post does it with the help of a function.
@mzzx Yes, they are equivalent! If you like it short, you could also write it in one line: arr[np.ogrid[0:arr.shape[0], 0:arr.shape[1]] + [c]] :-)
1

Here's an approach using fancy-indexing -

m,n,r = b.shape
d_out = b[np.arange(m)[:,None],np.arange(n),c]

Comments

1

Here is a Numpythonic way:

In [83]: x, y, z = a.shape
In [84]: b[np.repeat(np.arange(x), y), np.tile(np.arange(y), x), c.ravel()].reshape(x, y)

Here np.repeat(np.arange(x), y) will give you the corresponding indices of the first axis.

In [86]: np.repeat(np.arange(x), y)
Out[86]: array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4])

np.tile(np.arange(y), x) will give you the corresponding indices of the second axis.

In [87]: np.tile(np.arange(y), x)
Out[87]: array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1])

And for the third one you can just use the flattened shape of c.

In [88]: c.ravel()
Out[88]: array([1, 0, 1, 1, 1, 0, 1, 1, 0, 0])

2 Comments

Reshape these to (5,2) and the result should look like the mgrid version of the ogrid suggested by another answer.
@hpaulj Yes,unfortunately I wasn't familiar with that function.

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.