【问题标题】:First group of non-zero values (by neglecting single occurrence of zero)第一组非零值(通过忽略单次出现的零)
【发布时间】:2017-12-05 18:08:13
【问题描述】:

这就是我打算在 Python 中做的事情:

我有一个数组 (freq_arr)。我想找到第一组非零元素的索引。我从头开始搜索非零元素,当我找到第一个非零元素(第一个元素是 5,在下面的示例中)时,我记录它的索引(4,在下面的示例中)。我搜索下一个,并记录它的索引(将是 5)。如果我遇到一个零,我想忽略它并继续搜索非零值。这样,我考虑索引 4,5,6,7,8,9 和 10 的值 5,6,0,8,9,0,1。在这些值之后,有五个零,因此我停止搜索.输出中最多可以存在 两个零,然后继续搜索。但是,如果遇到 3 个或更多零,我想停止搜索。

输入:

freq_arr = np.array([0, 0, 0, 0, 5, 6, 0, 8, 9, 0, 1, 0, 0, 0, 0, 3, 6, 0])

输出:

out_arr_indices = [4, 5, 6, 7, 8, 9, 10]

我知道使用 for 循环对此进行编码,但我想避免它,因为它效率不高。请让我知道如何做到这一点。

数组将是一维的。每个元素将在 5000 到 20000 的范围内。

【问题讨论】:

  • 对于像这样有很多计数和异常的操作,我建议从numba 包中查看@jit。写出你的循环代码并给它一个 @jit 装饰器,你通常会几乎和矢量化解决方案一样快,代码更清晰。

标签: python arrays numpy


【解决方案1】:

这是一种使用切片和 argmax 的方法(用于检测非零和零)-

def start_stop_indices(freq_arr, W=3):
    nnz_mask = freq_arr!=0
    start_idx = nnz_mask.argmax()
    m0 = nnz_mask[start_idx:]
    kernel = np.ones(W,dtype=int)
    last_idx = np.convolve(m0, kernel).argmin() + start_idx - W
    return start_idx, last_idx

示例运行 -

In [203]: freq_arr
Out[203]: array([0, 0, 0, 0, 5, 6, 0, 8, 9, 0, 1, 0, 0, 0, 0, 3, 6, 0])

In [204]: start_stop_indices(freq_arr, W=3)
Out[204]: (4, 10)

In [205]: start_stop_indices(freq_arr, W=2)
Out[205]: (4, 10)

In [206]: start_stop_indices(freq_arr, W=1)
Out[206]: (4, 5)

这里还有一个长度=3的固定窗口搜索,避免使用convolution而更多地使用slicing-

def start_stop_indices_v2(freq_arr):
    nnz_mask = freq_arr!=0
    start_idx = nnz_mask.argmax()
    m0 = nnz_mask[start_idx:]
    idx0 = (m0[:-2] | m0[1:-1] | m0[2:]).argmin()
    last_idx = idx0 + start_idx - 1
    return  start_idx, last_idx

【讨论】:

  • 谢谢!这似乎是一种有效(且聪明)的方式,不使用循环。与@rickjerrity 的答案相比,它有什么优势(在速度/内存方面)?
  • @Meghana 好吧,如果那个非零岛非常接近起点并且非零岛的长度很短,那么您可能会更快地看到循环的,但对于一般情况,我认为这会更有效率。我建议您在最后测试一下,看看这两者的表现如何。
  • 谢谢!您提到的两种方法有什么区别?再次,在速度方面?卷积(第一个 sn-p)是否需要更高的资源?
  • @Meghana Well 当W 是一个更大的数字时,卷积方法会更有意义。第二种方法也避免了掩码的否定。所以,我猜W=3,第二个会更有效率。
  • @Meghana 感谢您的启发。更新了第一个以避免否定。第二种方法只对小 W 有意义,所以保持这种方式。对于通用 W,第二种方法需要一个循环,所以不要在那里修补。
【解决方案2】:

如果我正确理解您的问题,您希望遍历列表,连续跳过两个或更少的零,并将非零值的索引添加到输出数组。也许像下面这样

freq_arr = [0, 0, 5, 6, 0, 8, 9, 0, 1, 0, 0, 0, 0, 3, 6, 0]
outputarr = []

count = 0
zerocount = 0

while count < len(freq_arr) and zerocount < 3:
    if freq_arr[count] == 0:
        zerocount += 1
    else:
        zerocount = 0
        outputarr.append(count)
    count += 1

如果您提供更多详细信息,我们可能会提供更好的帮助。

【讨论】:

  • 谢谢!我的代码与此类似,但我使用了 for 循环而不是 while。但是,我的 freq_arr 将有大约 700000 个元素。还是建议用这个代码sn-p?
猜你喜欢
  • 2020-05-19
  • 1970-01-01
  • 2020-12-10
  • 2023-03-26
  • 1970-01-01
  • 2021-01-04
  • 1970-01-01
  • 2014-04-23
  • 2020-03-12
相关资源
最近更新 更多