Python/Numpy/Boolean 索引:在具有 N 个连续真值的位置处修改布尔数组
Python/Numpy/Boolean Indexing: Modify boolean array at locations with N consecutive True values
给定一个 numpy 布尔数组
arr = np.array([1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
我想指出哪里至少有n
个连续的真值(从左到右)。
对于n = 2
:
# True 2x (or more) in a row
# / \ / \
arr = [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1]
# becomes:
res = [0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0]
# ^-----------^--^-------- A pattern of 2 or more consecutive True's ends at each of these locations
对于n = 3
:
# True 3x (or more) in a row
# / \
arr = [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1]
# becomes:
res = [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
# ^-------- A pattern of 3 or more consecutive True's ends at this location
是否有一个 pythonic 方法来解决这个 而没有 使用 for 循环遍历每个元素?性能在这里很重要,因为我的应用程序将对包含 1000 个元素的布尔数组执行此操作 1000 次。
值得一提的注意事项:
- n 可以是大于 2
的任何值
- n——连续的模式可以出现在数组的任何地方;开头、中间或结尾。
- 结果数组的形状必须与原始数组的形状匹配。
非常感谢任何帮助。
答案基准
fully vectorized by alain-t:
10000 loops, best of 5: 0.138 seconds per loop, worse of 5: 0.149 seconds per loop
pad/shift by mozway:
10000 loops, best of 5: 1.62 seconds per loop, worse of 5: 1.71 seconds per loop
sliding_window_view by kubatucka (with padding by mozway):
10000 loops, best of 5: 1.15 seconds per loop, worse of 5: 1.54 seconds per loop
您可以将每个元素与其前身相乘。这将为您提供两个或更多序列的 1s。对结果再做一次以获得 3 个或更多的序列:
import numpy as np
arr = np.array([1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
arr[1:] *= arr[:-1] # two consecutive
arr[0] = 0 # 1st '1' isn't a two-consec.
arr[1:] *= arr[:-1] # three consecutive
print(arr)
[0 0 0 0 0 0 0 0 1 0 0]
你也可以这样试试(速度会快一点):
import numpy as np
arr = np.array([1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
arr[2:] *= arr[:-2] * arr[1:-1]
arr[:2] = 0
print(arr)
[0 0 0 0 0 0 0 0 1 0 0]
n 个连续的通用解决方案无法使用第二种方法,但可以使用第一种方法在循环中执行:
n = 3
arr[1:] *= arr[:-1]
arr[:n-1] = 0
for i in range(n-2):
arr[1:] *= arr[:-1]
请注意,这失去了矢量化的一些好处。
对于完全矢量化的 n 连续方法,您可以 select 最大 运行 个 0 位置与所有项目位置之间的匹配差异。结果的 1 位将包含该位置连续 1 的个数(而 0 位将为零)
n = 3
i = np.arange(arr.size)+1
mask = n <= i-np.maximum.accumulate((1-arr)*i) #True/False array
print(mask*1)
[0 0 0 0 0 0 0 0 1 0 0]
视觉上:
arr [ 1 0 1 1 0 0 1 1 1 0 1 ]
i [ 1 2 3 4 5 6 7 8 9 10 11 ] -+
(1-arr)*i [ 0 2 0 0 5 6 0 0 0 10 0 ] |-> differences
maximum.accumulate [ 0 2 2 2 5 6 6 6 6 10 10 ] -+ |
i-np.maximum... [ 1 0 1 2 0 0 1 2 3 0 1 ] <------+
作为这种方法的一个附带好处,您实际上可以从 i-np.maximum.accumulate((1-arr)*i)
的一次操作中获得所有 n 个连续的值,因此您可以存储它们并检查 n
的不同值,而无需重做计算。
仅限 Numpy:
from numpy.lib.stride_tricks import sliding_window_view
arr = np.array([1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
res = sliding_window_view(arr , window_shape = 3).all(axis=1) # window_shape <- n
>>> array([False, False, False, False, False, False, True, False, False])
你可以pad/shift序列使用numpy.pad
并与之前的值进行比较,最后乘以原始值只保留1:
b = np.pad(arr, (1,0), constant_values=0)[:-1]
# b: array([0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0])
(arr==b)*arr
输出:array([0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0])
通用解决方案:
n = 3
(sum([np.pad(arr, (i, 0), constant_values=0)[:len(arr)]
for i in range(n)]) == n)*arr
输出:array([0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0])
注意。这里的技巧是sum
移位的值,如果前n个值为1,则总和为n
给定一个 numpy 布尔数组
arr = np.array([1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
我想指出哪里至少有n
个连续的真值(从左到右)。
对于n = 2
:
# True 2x (or more) in a row
# / \ / \
arr = [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1]
# becomes:
res = [0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0]
# ^-----------^--^-------- A pattern of 2 or more consecutive True's ends at each of these locations
对于n = 3
:
# True 3x (or more) in a row
# / \
arr = [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1]
# becomes:
res = [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
# ^-------- A pattern of 3 or more consecutive True's ends at this location
是否有一个 pythonic 方法来解决这个 而没有 使用 for 循环遍历每个元素?性能在这里很重要,因为我的应用程序将对包含 1000 个元素的布尔数组执行此操作 1000 次。
值得一提的注意事项:
- n 可以是大于 2 的任何值
- n——连续的模式可以出现在数组的任何地方;开头、中间或结尾。
- 结果数组的形状必须与原始数组的形状匹配。
非常感谢任何帮助。
答案基准
fully vectorized by alain-t:
10000 loops, best of 5: 0.138 seconds per loop, worse of 5: 0.149 seconds per loop
pad/shift by mozway:
10000 loops, best of 5: 1.62 seconds per loop, worse of 5: 1.71 seconds per loop
sliding_window_view by kubatucka (with padding by mozway):
10000 loops, best of 5: 1.15 seconds per loop, worse of 5: 1.54 seconds per loop
您可以将每个元素与其前身相乘。这将为您提供两个或更多序列的 1s。对结果再做一次以获得 3 个或更多的序列:
import numpy as np
arr = np.array([1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
arr[1:] *= arr[:-1] # two consecutive
arr[0] = 0 # 1st '1' isn't a two-consec.
arr[1:] *= arr[:-1] # three consecutive
print(arr)
[0 0 0 0 0 0 0 0 1 0 0]
你也可以这样试试(速度会快一点):
import numpy as np
arr = np.array([1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
arr[2:] *= arr[:-2] * arr[1:-1]
arr[:2] = 0
print(arr)
[0 0 0 0 0 0 0 0 1 0 0]
n 个连续的通用解决方案无法使用第二种方法,但可以使用第一种方法在循环中执行:
n = 3
arr[1:] *= arr[:-1]
arr[:n-1] = 0
for i in range(n-2):
arr[1:] *= arr[:-1]
请注意,这失去了矢量化的一些好处。
对于完全矢量化的 n 连续方法,您可以 select 最大 运行 个 0 位置与所有项目位置之间的匹配差异。结果的 1 位将包含该位置连续 1 的个数(而 0 位将为零)
n = 3
i = np.arange(arr.size)+1
mask = n <= i-np.maximum.accumulate((1-arr)*i) #True/False array
print(mask*1)
[0 0 0 0 0 0 0 0 1 0 0]
视觉上:
arr [ 1 0 1 1 0 0 1 1 1 0 1 ]
i [ 1 2 3 4 5 6 7 8 9 10 11 ] -+
(1-arr)*i [ 0 2 0 0 5 6 0 0 0 10 0 ] |-> differences
maximum.accumulate [ 0 2 2 2 5 6 6 6 6 10 10 ] -+ |
i-np.maximum... [ 1 0 1 2 0 0 1 2 3 0 1 ] <------+
作为这种方法的一个附带好处,您实际上可以从 i-np.maximum.accumulate((1-arr)*i)
的一次操作中获得所有 n 个连续的值,因此您可以存储它们并检查 n
的不同值,而无需重做计算。
仅限 Numpy:
from numpy.lib.stride_tricks import sliding_window_view
arr = np.array([1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
res = sliding_window_view(arr , window_shape = 3).all(axis=1) # window_shape <- n
>>> array([False, False, False, False, False, False, True, False, False])
你可以pad/shift序列使用numpy.pad
并与之前的值进行比较,最后乘以原始值只保留1:
b = np.pad(arr, (1,0), constant_values=0)[:-1]
# b: array([0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0])
(arr==b)*arr
输出:array([0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0])
通用解决方案:
n = 3
(sum([np.pad(arr, (i, 0), constant_values=0)[:len(arr)]
for i in range(n)]) == n)*arr
输出:array([0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0])
注意。这里的技巧是sum
移位的值,如果前n个值为1,则总和为n