在NumPy数组中记录最小值

4

我可以这样说,假设我有一个 NumPy 数组,如下所示:

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])

i = 9 开始,我希望遍历每个元素,向 i = 0 移动,并记录沿途的“迄今为止最小值”数字以及这些数字的索引。在这个例子中:

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]

左侧的最小值为[5, 3, 2, 1],并且它们位于索引[7, 4, 3, 0]处。

你也可以向右遍历:

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]

正确的最小值是 [4, 2],它们位于索引 [11, 12]

x 的长度很大时,有没有更快的方法来进行 min_so_far 搜索?


你能澄清一下你期望的输出是什么吗?因为你当前的代码只获取一个值和它的索引,但你的解释似乎暗示了输出会有多个值。 - Henry Ecker
我已经添加了两个算法产生的输出。 - slaw
2个回答

2
你可以这样做:
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

您可以直接将这些函数应用于您想要的切片,例如:
>>> 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]))

也许我理解错了,但是当我提供与问题中相同的“x”时,“left_mins(x)”似乎只返回“(array([1.]), array([0]))”。相反,“left_mins”需要返回“(array([5, 3, 2, 1]), array([7, 4, 3, 0]))”。 - slaw
1
@slaw 函数需要应用于数组的相关部分。right_mins([x[:10])left_mins([x[10:])。然后两者都需要删除inf值及其对应的索引。当首次发现时,np.inf是最小值。 - Tls Chris
@slaw。你对所建议的修改有特定的使用场景吗?在你修改代码之前,让我们先讨论一下。 - Mad Physicist
@slaw。在这种情况下,将元素设置为“nan”是完全错误的。看看你得到的结果:它是无意义的,因为“x> nan”始终为false。你有两个选择:将无限大视为特殊情况(截断数组),或者只接受警告。 - Mad Physicist
1
idx = np.insert(y[1:] != y[:-1], 0, True) return x[idx], np.where(idx)[0] 还有一种选项是避免 > 比较,因为这可能没有定义。 - CJR
显示剩余4条评论

0

这不完全是你想要的,但它会在遍历数组时为所有子列表提供最小值。它很Pythonic,但对于相当大的NumPy数组来说速度会比较慢。

right_going = [min(x[n:i]) for i in range(n, len (x))]
left_going = [min(x[(n-i):n]) for i in range(n)]

在第一种情况下,值最小的数组索引为n + list_index,而在第二种情况下,它只是列表的索引。然后,您可以通过获取这些列表中重复值的第一个(或最后一个)出现的索引和值来完全实现您想要的功能。


4
或许符合 Python 风格,但绝对不符合 NumPy 风格。 - Mad Physicist
1
@MadPhysicist,鉴于这个问题的顺序性,“numpythonic”(呃!)可能不可行。 np.minimum.accumulate 可以得到各种范围的最小值,但我认为没有 argmin 的“累加”版本。 - hpaulj
@hpaulj。我已经发布了一个非常向量化的解决方案的答案。虽然没有argmin.accumulate,但通常的diff技巧在minimum.accumulate之后也可以正常工作。 - Mad Physicist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接