Recording Smallest-So-Far Values in NumPy Array

Let's say I have a NumPy array like:

import numpy as np

x = np.array([1, 5, 4, 2, 3, 6, 7, 5, np.inf, np.inf, np.inf, 4, 2, 8, 3, 5, 9, 2, 3, 4])

And starting at i = 9, I want to traverse each element toward i = 0 and then to record the "smallest-so-far" numbers along the way along with the index of those numbers. In this example:

left_val = []
left_idx = []
min_so_far = np.inf
for i in range(9, -1, -1):
    if x[i] < min_so_far:
        left_val.append(x[i])
        left_idx.append(i)
        min_so_far = x[i]

The left min-so-far values are [5, 3, 2, 1] and they are located at indices [7, 4, 3, 0].

You can also traverse toward the right:

right_val = []
right_idx = []
min_so_far = np.inf
for i in range(9, x.shape[0]):
    if x[i] < min_so_far:
        right_val.append(x[i])
        right_idx.append(i)
        min_so_far = x[i]

The right min-so-far values are [4, 2] and they are located at index [11, 12]

Is there a faster way to do this min_so_far search when the length of x is much larger?


You can do something like this:

def right_mins(x):
    # To avoid leading infs, truncate some more
    start = np.argmax(np.isfinite(x))
    y = np.minimum.accumulate(x[start:])
    idx = np.r_[0, np.flatnonzero(np.diff(y)) + 1] + start
    return x[idx], idx

def left_mins(x):
    y, idx = right_mins(x[::-1])
    return y, x.size - 1 - idx

You can apply these functions directly to the slices that you want, e.g.:

>>> left_mins(x[:10])
(array([5., 3., 2., 1.]), array([7, 4, 3, 0]))
>>> n, i = right_mins(x[9:])
>>> n, i + 9
(array([4., 2.]), array([11, 12]))