Make a sample 2d array:
In [584]: arr = np.random.rand(1000,1000)
Find a small proportion of them:
In [587]: np.where(arr>.999)
Out[587]:
(array([ 1, 1, 1, ..., 997, 999, 999], dtype=int32),
array([273, 471, 584, ..., 745, 310, 679], dtype=int32))
In [588]: _[0].shape
Out[588]: (1034,)
Time various pieces of argwhere:
In [589]: timeit arr>.999
2.65 ms ± 116 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [590]: timeit np.count_nonzero(arr>.999)
2.79 ms ± 26 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [591]: timeit np.nonzero(arr>.999)
6 ms ± 10 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [592]: timeit np.argwhere(arr>.999)
6.06 ms ± 58.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
So about 1/3 of the time is spend doing the > test, and the rest in finding the True elements. Turning the where tuple into a 2 column array is fast.
Now if the goal was to just find the first > value, argmax is fast.
In [593]: np.argmax(arr>.999)
Out[593]: 1273 # can unravel this to (1,273)
In [594]: timeit np.argmax(arr>.999)
2.76 ms ± 143 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
argmax short circuits, so the actual run time will vary on when it finds the first value.
flatnonzero is faster than where:
In [595]: np.flatnonzero(arr>.999)
Out[595]: array([ 1273, 1471, 1584, ..., 997745, 999310, 999679], dtype=int32)
In [596]: timeit np.flatnonzero(arr>.999)
3.05 ms ± 26.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [599]: np.unravel_index(np.flatnonzero(arr>.999),arr.shape)
Out[599]:
(array([ 1, 1, 1, ..., 997, 999, 999], dtype=int32),
array([273, 471, 584, ..., 745, 310, 679], dtype=int32))
In [600]: timeit np.unravel_index(np.flatnonzero(arr>.999),arr.shape)
3.05 ms ± 3.58 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [601]: timeit np.transpose(np.unravel_index(np.flatnonzero(arr>.999),arr.shap
...: e))
3.1 ms ± 5.86 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
This is the same as np.argwhere(arr>.999).
Interesting, the flatnonzero approach cuts the time in half! I didn't expect such a big improvement.
Comparing the iteration speeds:
Iteration on the 2d array from argwhere:
In [607]: pixels = np.argwhere(arr>.999)
In [608]: timeit [pixel for pixel in pixels]
347 µs ± 5.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Iterating on the tuple from where with the zip(*) transpose:
In [609]: idx = np.where(arr>.999)
In [610]: timeit [pixel for pixel in zip(*idx)]
256 µs ± 147 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Iterating on an array is often a little slower than iterating on a list, or in this case zipped arrays.
In [611]: [pixel for pixel in pixels][:5]
Out[611]:
[array([ 1, 273], dtype=int32),
array([ 1, 471], dtype=int32),
array([ 1, 584], dtype=int32),
array([ 1, 826], dtype=int32),
array([ 2, 169], dtype=int32)]
In [612]: [pixel for pixel in zip(*idx)][:5]
Out[612]: [(1, 273), (1, 471), (1, 584), (1, 826), (2, 169)]
One is a list of arrays, the other a list of tuples. But turning those tuples into arrays (individually) is slow:
In [614]: timeit [np.array(pixel) for pixel in zip(*idx)]
2.26 ms ± 4.94 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Iterating on the flat nonzero array is faster
In [617]: fdx = np.flatnonzero(arr>.999)
In [618]: fdx[:5]
Out[618]: array([1273, 1471, 1584, 1826, 2169], dtype=int32)
In [619]: timeit [i for i in fdx]
112 µs ± 23.5 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
but applying unravel to those values individually will take time.
def foo(idx): # a simplified unravel
return idx//1000, idx%1000
In [628]: timeit [foo(i) for i in fdx]
1.12 ms ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Add this 1 ms to the 3 ms to generate fdx, this flatnonzero might still come out ahead. But at its best we are talking about a 2x speed improvement.
argwhereis justwherewith atransposeto turn the tuple of arrays into a 2d array.argwhere, and not theds2>0.05step, or more likely the iteration on all thosepixel?argwhere.whereexpression has to iterate over the array several time. One to create the boolean array. Thennp.count_nonzeroquickly counts the number ofTruevalues. Finallywhere(actuallynp.nonzero) collects the indices of those values. Thetransposetoargwhereshould be a minor part of the action. Ifds2large compared to the number of True pixels, then thiswhereaction could dominate.