【问题标题】:Removing leading zeros of a numpy array without using a for loop在不使用 for 循环的情况下删除 numpy 数组的前导零
【发布时间】:2018-10-15 03:47:22
【问题描述】:

如何在不使用 for 循环的情况下仅从 numpy 数组中删除前导零?

import numpy as np

x = np.array([0,0,1,1,1,1,0,1,0,0])

# Desired output
array([1, 1, 1, 1, 0, 1, 0, 0])

我写了以下代码

x[min(min(np.where(x>=1))):] 

我想知道是否有更有效的解决方案。

【问题讨论】:

  • np.trim_zeros(x, 'f') - internally this is a for loop,但在很多情况下,这可能是最有效的方式。
  • x 的长度和前导零的数量的典型值是多少?

标签: python python-3.x performance numpy


【解决方案1】:

您可以使用np.trim_zeros(x, 'f')

“f”表示从前面修剪零。 选项“b”将从后面修剪零。 默认选项“fb”从两侧修剪它们。

x = np.array([0,0,1,1,1,1,0,1,0,0])
# [0 0 1 1 1 1 0 1 0 0]
np.trim_zeros(x, 'f')
# [1 1 1 1 0 1 0 0]

【讨论】:

  • 正如其他答案或 cmets 中所说,the trim_zero() implementation 似乎在内部使用 for 循环,那么我们是否应该找到替代解决方案?否则,如果 OP 只是在寻找一个库函数来避免手动编写循环,那么这个答案就足够了,但问题应该更清楚。
【解决方案2】:

由于np.trim_zerosuses a for loop,这里有一个真正的矢量化解决方案:

x = x[np.where(x != 0)[0][0]:]

但是我不确定它从什么时候开始比np.trim_zeros 更有效率。在最坏的情况下(即具有大多数前导零的数组)会更有效。

无论如何,这都是一个有用的学习例子。

双面修边:

>>> idx = np.where(x != 0)[0]
>>> x = x[idx[0]:1+idx[-1]]

【讨论】:

  • 你的回答让我很感兴趣,所以我做了一些研究。内部切片不也被视为 for 循环吗?参考:this post.
  • 那篇文章是关于list的。你确定 numpy 也一样吗?切片它只是对带有slice 对象的__getitem__ 方法的调用,例如x[2:4]x.__getitem__(slice(2,4)) 相同。这取决于 numpy 如何实现该方法。
  • 我不确定,因此提出了这个问题。我对for-less 的实现很好奇,想看看。但是,我无法追溯numpy.ndarray 对象的帖子中的步骤。我也试图窥探 numpy 来源,但无济于事。无论如何,我想我们不应该在这里讨论它。
【解决方案3】:

这是一种短路的 numpy 方法。它利用了 0 对于任何 (?) dtype 的表示都是零字节这一事实。

import numpy as np
import itertools

# check assumption that for example 0.0f is represented as 00 00 00 00
allowed_dtypes = set()
for dt in map(np.dtype, itertools.chain.from_iterable(np.sctypes.values())):
    try:
        if not np.any(np.zeros((1,), dtype=dt).view(bool)):
            allowed_dtypes.add(dt)
    except:
        pass

def trim_fast(a):
    assert a.dtype in allowed_dtypes
    cut = a.view(bool).argmax() // a.dtype.itemsize
    if a[cut] == 0:
        return a[:0]
    else:
        return a[cut:]

与其他方法的比较:

生成情节的代码:

def np_where(a):
    return a[np.where(a != 0)[0][0]:]

def np_trim_zeros(a):
    return np.trim_zeros(a, 'f')

import perfplot

tf, nt, nw = trim_fast, np_trim_zeros, np_where
def trim_fast(A): return [tf(a) for a in A]
def np_trim_zeros(A): return [nt(a) for a in A]
def np_where(A): return [nw(a) for a in A]

perfplot.save('tz.png',
    setup=lambda n: np.clip(np.random.uniform(-n, 1, (100, 20*n)), 0, None),
    n_range=[2**k for k in range(2, 11)],
    kernels=[
        trim_fast,
        np_where,
        np_trim_zeros
        ],
    logx=True,
    logy=True,
    xlabel='zeros per nonzero',
    equality_check=None
    )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 2021-01-29
    • 1970-01-01
    • 2020-02-26
    相关资源
    最近更新 更多