Numpy 1D 数组: 重复超过 n 次的屏蔽元素

共8个回答, 标签: python arrays numpy binning

给定一个整数数组

【 1 、 2 、 3 、 4 、 5 】

我需要屏蔽重复次数超过的元素N时代。要澄清:主要目标是检索布尔屏蔽数组,稍后将其用于分级计算。

我想出了一个相当复杂的解决方案

将 numpy 作为 np 导入

垃圾箱 = np。阵列 ([1 、 2 、 3 、 4 、 5 、 5])

N = 3
Splits = np.split (垃圾箱,np.where (np.diff (垃圾箱)!= 0) [0] 1)
面具 = []
对于拆分中的 s:
如果 s.shape [0] <= N:
Mask.append (np.ones (s.shape [0]).astype (np.bool _))
其他:
Mask.append (np.One (N),np.Zero (s.shape [0]-N))。astype (np.bool _))

Mask = np.concatenate (mask)

给予例如

垃圾箱 [面具]
Out [90]: array ([1 、 2 、 3 、 4 、 5])

有没有更好的方法来做到这一点?

编辑,#2:

非常感谢您的回答!这是 MSeifert 基准图的一个纤细版本。谢谢你给我指simple_benchmark。只显示 4 个最快的选项:enter image description here

初步结论:

提出的想法弗洛里安 H,由修改保罗 Panzer似乎是解决这个问题的一个好方法,因为它非常直接numpy-only. If you're fine with using numba然而,MSeifert 的解决方案表现优于其他。

第1个答案

免责声明: 这只是 @ FlorianH 想法的更健全的实现:

Def (a,N):
Mask = np.empty (a.size,bool)
Mask [: N] = True
Np.not_equal (a [N:],a [:-N],out = mask [N:])
返回面具

对于更大的阵列,这有很大的不同:

A = np.arange (1000).repeat (np.random.randint (0,10,1000))
N = 3

打印 (timeit (lambda: f (a,N),number = 1000) * 1000,“us”)
#5.443050000394578 美国

# 比较到
Print (timeit (lambda: [True for _ in range (N))] list (bin [:-N)! = 垃圾箱 [N:]),数字 = 1000) * 1000,“我们”)
#76.18969900067896 美国
第2个答案

方法 1:这里有一个矢量化的方式-

从 scipy.ndimage.形态导入 binary_dilation

Def keep_N_per_group (a,N):
K = np.One (N,dtype = bool)
M = np.r _ [真,a [:-1]!= a [1:]
返回 [binary_dilation (m,k,origin =-(N//2))]

样本运行-

在 [42]: a
Out [42]: array ([1 、 2 、 3 、 4 、 5 、 5,5,5])

在 [43]: keep_N_per_group (a,N = 3)
Out [43]: array ([1 、 2 、 3 、 4 、 5])

方法 2:更紧凑的版本-

Def keep_N_per_group_v2 (a,N):
K = np.One (N,dtype = bool)
返回 [binary_dilation (np.ediff1d (a,to_begin = a [0))!= 0,k,原点 =-(N//2))]

方法 3:使用分组计数和np.repeat(虽然不会给我们面具)-

Def keep_N_per_group_v3 (a,N):
M = np.r _ [真,a [:-1]!= a [1:],真]
Idx = np.flatnonzero (m)
C = np.diff (idx)
返回 np.repeat (a [idx [:-1]],np.minimum (c,N))

方法 #4:随着view-based方法-

从 skimage.util 导入 view_as_windows

Def keep_N_per_group_v4 (a,N):
M = np.r _ [真,a [:-1]!= a [1:]
W = view_as_windows (m,N)
Idx = np.flatnonzero (m)
V = idx

``方法 #5:随着view-based method without indices from flatnonzero-

Def keep_N_per_group_v5 (a,N):
M = np.r _ [真,a [:-1]!= a [1:]
W = view_as_windows (m,N)
Last_idx = len (a)-m [::-1]。argmax ()-1
W [m [:-N 1] = 1
M [last_idx: last_idx N] = 1
返回一个 [m]
第3个答案

你可以用索引来做到这一点。对于任何 N,代码将是:

N = 3
垃圾箱 = np。阵列 ([1 、 2 、 3 、 4 、 5 、 5,6])

Mask = [True for _ in range (N)] list (垃圾箱 [:-N]!= 垃圾箱 [N:])
垃圾箱 [面具]

输出:

阵列 ([1 、 2 、 3 、 4 、 5 、 6]
第4个答案

我想用一个解决方案Numba这应该很容易理解。我假设你想 “屏蔽” 连续重复项目:

将 numpy 作为 np 导入
将 numba 作为 nb 导入

@ Nb.njit
Def mask_more_n (arr,n):
Mask = np.One (arr.shape,np.bool _)

电流 = arr [0]
计数 = 0
对于 idx,枚举中的项 (arr):
如果项目 = = 当前:
计数 = 1
其他:
当前 = 项目
计数 = 1
屏蔽 [idx] = 计数 <= n
返回面具

例如:

垃圾箱 = np。阵列 ([1 、 2 、 3 、 4 、 5 、 5])
> 垃圾箱 [mask_more_n (垃圾箱,3)]
阵列 ([1 、 2 、 3 、 4 、 5])
> 垃圾箱 [mask_more_n (垃圾箱,2)]
阵列 ([1 、 2 、 3 、 4 、 5])

性能:

使用simple_benchmark-然而,我没有包括所有的方法。这是一个日志-日志规模:

enter image description here

似乎 numba 解决方案无法击败保罗 · 帕瑟的解决方案,这似乎对大型阵列来说更快一点 (并且不需要额外的依赖)。

然而,两者似乎都优于其他解决方案,但它们确实返回一个屏蔽而不是 “过滤” 数组。

将 numpy 作为 np 导入
将 numba 作为 nb 导入
从 simple_benchmark 导入 BenchmarkBuilder,多参数

B = BenchmarkBuilder ()

垃圾箱 = np。阵列 ([1 、 2 、 3 、 4 、 5 、 5])

@ Nb.njit
Def mask_more_n (arr,n):
Mask = np.One (arr.shape,np.bool _)

电流 = arr [0]
计数 = 0
对于 idx,枚举中的项 (arr):
如果项目 = = 当前:
计数 = 1
其他:
当前 = 项目
计数 = 1
屏蔽 [idx] = 计数 <= n
返回面具

@ B.add_function (warmups = True)
MSeifert (arr,n):
返回 mask_more_n (arr,n)

从 scipy.ndimage.形态导入 binary_dilation

@ B.add_function ()
Def Divakar_1 (a,N):
K = np.One (N,dtype = bool)
M = np.r _ [真,a [:-1]!= a [1:]
返回 [binary_dilation (m,k,origin =-(N//2))]

@ B.add_function ()
Def Divakar_2 (a,N):
K = np.One (N,dtype = bool)
返回 [binary_dilation (np.ediff1d (a,to_begin = a [0))!= 0,k,原点 =-(N//2))]

@ B.add_function ()
Def divakar3 (a,N):
M = np.r _ [真,a [:-1]!= a [1:],真]
Idx = np.flatnonzero (m)
C = np.diff (idx)
返回 np.repeat (a [idx [:-1]],np.minimum (c,N))

从 skimage.util 导入 view_as_windows

@ B.add_function ()
Def divakar4 (a,N):
M = np.r _ [真,a [:-1]!= a [1:]
W = view_as_windows (m,N)
Idx = np.flatnonzero (m)
V = idx
第5个答案

一个更好的方法是使用numpy's unique()-功能。您将在阵列中获得独特的条目,以及它们出现的频率:

垃圾箱 = np。阵列 ([1 、 2 、 3 、 4 、 5 、 5])
N = 3

Unique,index,count = np.unique (垃圾箱,return_index = True,return_counts = True)
Mask = np.full (垃圾箱.shape,True,dtype = bool)
对于 zip 中的 i,c (索引,计数):
如果 c> N:
面具 [i N: i c] = 假

垃圾箱 [面具]

输出:

阵列 ([1 、 2 、 3 、 4 、 5])
第6个答案

您可以使用 while 循环来检查数组元素 N 的位置是否等于当前的位置。注意: 此解决方案假定数组是有序的。

将 numpy 作为 np 导入

垃圾箱 = [1 、 2 、 3 、 4 、 5 、 5]
N = 3
计数器 = N

而计数器 <len (垃圾箱):
Drop_condition = (垃圾箱 [计数器] = = 垃圾箱 [计数器-N])
如果 drop_condition:
垃圾箱 = np.delete (垃圾箱,计数器)
其他:
# 进入下一个元素
计数器 = 1
第7个答案

你可以使用格洛比将长于的公共元素和过滤器列表分组N.

将 numpy 作为 np 导入
从迭代工具导入 groupby,chain

Def ifElse (条件,exec1,exec2):

If 条件: 返回 exec1
其他: 返回 exec2


Def 求解 (垃圾箱,N = 无):

Xss = groupby (垃圾箱)
Xss = map (lambda xs: list (xs [1]),xss)
Xss = map (lambda xs: ifElse (len (xs)> N,xs [: N],xs),xss)
Xs = chain.from_iterable (xss)
返回列表 (xs)

垃圾箱 = np。阵列 ([1 、 2 、 3 、 4 、 5 、 5])
求解 (垃圾箱,N = 3)
第8个答案

解决方案

你可以使用numpy.unique. The variable final_mask can be used to extract the traget elements from the array bins

将 numpy 作为 np 导入

垃圾箱 = np。阵列 ([1 、 2 、 3 、 4 、 5 、 5])
Repeat_max = 3

唯一,计数 = np.unique (垃圾箱,return_counts = True)
Mod_counts = np.array ([x if x <= repeat_max else repeat_max for x in counts])
Mask = np.arange (垃圾箱大小)
# Final _ values = np.hstack ([垃圾箱 = = 值] [: 计数] 对于值,在 zip 中计数 (唯一,mod_counts)])
Final_mask = np.hstack ([mask [垃圾箱 = = 值] [: 计数] 对于值,zip 中的计数 (唯一,mod_counts)])
垃圾箱 [final_mask]

输出:

阵列 ([1 、 2 、 3 、 4 、 5])

相关问题

通过随机抽样其他列数据创建新列 如何使用熊猫获得包括每一个组合的计数 如何有效地展开矩阵的值与小块? "注意: 未定义的变量"、"注意: 未定义的索引" 和 "注意: 未定义的偏移量" 使用 PHP 删除数字数组中的所有重复数字 [重复] 使用数组中的键获取最小值 将一个六位数的数字列拆分为分隔的列,保留一位数的 [] Numpy 1D 数组: 重复超过 n 次的屏蔽元素 在 numpy [复制] 中快速找到对称对