【问题标题】:Efficient numpy subarrays extraction from a mask从掩码中提取高效的 numpy 子数组
【发布时间】:2017-04-13 07:17:07
【问题描述】:

我正在搜索一种 pythonic 方法来使用示例中所示的掩码从给定数组中提取多个子数组:

a = np.array([10, 5, 3, 2, 1])
m = np.array([True, True, False, True, True])

输出将是一个数组的集合,如下所示,其中只有掩码m 的 True 值(彼此相邻的 True 值)的连续“区域”表示生成子数组的索引。

L[0] = np.array([10, 5])
L[1] = np.array([2, 1])

【问题讨论】:

标签: python performance numpy


【解决方案1】:

这是一种方法 -

def separate_regions(a, m):
    m0 = np.concatenate(( [False], m, [False] ))
    idx = np.flatnonzero(m0[1:] != m0[:-1])
    return [a[idx[i]:idx[i+1]] for i in range(0,len(idx),2)]

示例运行 -

In [41]: a = np.array([10, 5, 3, 2, 1])
    ...: m = np.array([True, True, False, True, True])
    ...: 

In [42]: separate_regions(a, m)
Out[42]: [array([10,  5]), array([2, 1])]

运行时测试

其他方法-

# @kazemakase's soln
def zip_split(a, m):
    d = np.diff(m)
    cuts = np.flatnonzero(d) + 1

    asplit = np.split(a, cuts)
    msplit = np.split(m, cuts)

    L = [aseg for aseg, mseg in zip(asplit, msplit) if np.all(mseg)]
    return L

时间安排 -

In [49]: a = np.random.randint(0,9,(100000))

In [50]: m = np.random.rand(100000)>0.2

# @kazemakase's's solution
In [51]: %timeit zip_split(a,m)
10 loops, best of 3: 114 ms per loop

# @Daniel Forsman's solution
In [52]: %timeit splitByBool(a,m)
10 loops, best of 3: 25.1 ms per loop

# Proposed in this post
In [53]: %timeit separate_regions(a, m)
100 loops, best of 3: 5.01 ms per loop

增加岛屿的平均长度 -

In [58]: a = np.random.randint(0,9,(100000))

In [59]: m = np.random.rand(100000)>0.1

In [60]: %timeit zip_split(a,m)
10 loops, best of 3: 64.3 ms per loop

In [61]: %timeit splitByBool(a,m)
100 loops, best of 3: 14 ms per loop

In [62]: %timeit separate_regions(a, m)
100 loops, best of 3: 2.85 ms per loop

【讨论】:

  • 我接受这个答案,因为它提供了与所讨论的其他方法的比较,而且是一种更快的方法。谢谢!
  • 有趣的事实:我刚刚发现 np.r_[False, m, False]np.concatenate(([False], m, [False])) 慢 5-10 倍。
【解决方案2】:
def splitByBool(a, m):
    if m[0]:
        return np.split(a, np.nonzero(np.diff(m))[0] + 1)[::2]
    else:
        return np.split(a, np.nonzero(np.diff(m))[0] + 1)[1::2] 

这将返回一个数组列表,在 m 中分成 True

【讨论】:

  • 不错的解决方案。利用TrueFalse 段必须交替的事实。
  • 我喜欢这个解决方案,因为它可以变成单行:np.split(a, np.nonzero(np.diff(m))[0] + 1)[1 - m[0]::2]
  • 甚至np.split(a, np.flatnonzero(np.diff(m)) + 1)[1 - m[0]::2)],可读性更强一点
  • 有趣,每一行都比上一行慢 :) 但更容易阅读,正如你所说。
【解决方案3】:

听起来像是np.split 的自然应用程序。

您首先必须弄清楚在哪里剪切数组,即掩码在TrueFalse 之间变化的位置。接下来丢弃所有掩码为False的元素。

a = np.array([10, 5, 3, 2, 1])
m = np.array([True, True, False, True, True])

d = np.diff(m)
cuts = np.flatnonzero(d) + 1

asplit = np.split(a, cuts)
msplit = np.split(m, cuts)

L = [aseg for aseg, mseg in zip(asplit, msplit) if np.all(mseg)]

print(L[0])  # [10  5]
print(L[1])  # [2 1]

【讨论】:

    猜你喜欢
    • 2015-11-08
    • 2016-03-16
    • 2017-03-15
    • 2021-09-19
    • 2018-04-09
    • 2018-09-01
    • 2014-03-03
    • 2015-07-30
    • 2017-09-01
    相关资源
    最近更新 更多