17

For example,

x = array([[1,2,3],[3,2,5],[9,0,2]])
some_func(x) gives (2,1)

I know one can do it by a custom function:

def find_min_idx(x):
    k = x.argmin()
    ncol = x.shape[1]
    return k/ncol, k%ncol

However, I am wondering if there's a numpy built-in function that does this faster.

Thanks.

EDIT: thanks for the answers. I tested their speeds as follows:

%timeit np.unravel_index(x.argmin(), x.shape)
#100000 loops, best of 3: 4.67 µs per loop

%timeit np.where(x==x.min())
#100000 loops, best of 3: 12.7 µs per loop

%timeit find_min_idx(x) # this is using the custom function above
#100000 loops, best of 3: 2.44 µs per loop

Seems the custom function is actually faster than unravel_index() and where(). unravel_index() does similar things as the custom function plus the overhead of checking extra arguments. where() is capable of returning multiple indices but is significantly slower for my purpose. Perhaps pure python code is not that slow for doing just two simple arithmetic and the custom function approach is as fast as one can get.

N. H.
  • 185
  • 1
  • 1
  • 6

2 Answers2

22

You may use np.where:

In [9]: np.where(x == np.min(x))
Out[9]: (array([2]), array([1]))

Also as @senderle mentioned in comment, to get values in an array, you can use np.argwhere:

In [21]: np.argwhere(x == np.min(x))
Out[21]: array([[2, 1]])

Updated:

As OP's times show, and much clearer that argmin is desired (no duplicated mins etc.), one way I think may slightly improve OP's original approach is to use divmod:

divmod(x.argmin(), x.shape[1])

Timed them and you will find that extra bits of speed, not much but still an improvement.

%timeit find_min_idx(x)
1000000 loops, best of 3: 1.1 µs per loop

%timeit divmod(x.argmin(), x.shape[1])
1000000 loops, best of 3: 1.04 µs per loop

If you are really concerned about performance, you may take a look at cython.

Anzel
  • 19,825
  • 5
  • 51
  • 52
  • 1
    Might be worth mentioning `argwhere` as well -- depends on whether NH needs the values to be usable as indices or as values in an array. – senderle May 12 '15 at 01:41
  • Though looking at it more that distinction only holds when the minimum value occurs more than once. – senderle May 12 '15 at 01:46
  • @senderle, OP has a working solution so I am not surprised that can be easily converted like `tuple(map(int, np.where(x == np.min(x))))` – Anzel May 12 '15 at 01:49
21

You can use np.unravel_index

print(np.unravel_index(x.argmin(), x.shape))
(2, 1)
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • very nice, does OP wants the minimum value of array or single item among arrays? – Anzel May 12 '15 at 01:41
  • If this is the answer, isn't this whole question a dup of [this](http://stackoverflow.com/questions/3230067/numpy-minimum-in-row-column-format)? – DSM May 12 '15 at 01:43
  • @Anzel, I am not sure was just going by what the OP was using. – Padraic Cunningham May 12 '15 at 01:45
  • 1
    Revisiting my last comment -- this works for indexing or as a sequence of indices, but only because `argmin` returns just one value, even if the minimum occurs multiple times. The `where(x == np.min(x))` solution can capture multiple minima. – senderle May 12 '15 at 01:48
  • @senderle, true, I just presumed this was what the OP wanted as their own method will do the same. – Padraic Cunningham May 12 '15 at 01:51
  • 1
    Quite right. I'm just obsessively working through all the details, probably to an unnecessary degree! – senderle May 12 '15 at 01:52
  • 1
    @DSM, you might be right but I don't know if I am right yet. – Padraic Cunningham May 12 '15 at 01:54
  • @PadraicCunningham thank you for the answer, a single item should suffice. It's indeed a built-in function solution but it doesn't seem to be faster at least for my simple example. – N. H. May 12 '15 at 15:05
  • @Anzel thank you for the answer. where() is what I tried first, but it's considerably slower, probably due to scanning all elements for an extra of two times (testing equality and finding the index of Trues). – N. H. May 12 '15 at 15:17
  • @N.H. I have an update which mainly slightly improved of what you've done already as the times show it's the fastest in your liking. Interestingly you leave comment to me on someone's answer :-) I'm sure Padraic is nice enough not to mind our ignorance. – Anzel May 12 '15 at 17:01