假设我有两个 Numpy 数组:
a = np.array([1,2,2,3,3,3])
b = np.array([2,2,3])
我想从 a
中删除 b
中的所有元素,它们在 b
中出现的次数相同。即
diff(a, b)
>>> np.array([1,3,3])
请注意,对于我的用例,b
将始终是 a
的子集,并且两者都可能是无序的,但是像 numpy.setdiff1d
这样的类似集合的方法不会削减它,因为删除每个元素一定次数很重要。
我目前的懒惰解决方案如下:
def diff(a, b):
for el in b:
idx = (el == a).argmax()
if a[idx] == el:
a = np.delete(a, idx)
return a
但我想知道是否有更高效或更紧凑的“numpy-esque”编写方式?
最佳答案
这是基于 np.searchsorted
的矢量化方法-
import pandas as pd
def diff_v2(a, b):
# Get sorted orders
sidx = a.argsort(kind='stable')
A = a[sidx]
# Get searchsorted indices per sorted order
idx = np.searchsorted(A,b)
# Get increments
s = pd.Series(idx)
inc = s.groupby(s).cumcount().values
# Delete elemnents off traced back positions
return np.delete(a,sidx[idx+inc])
进一步优化
让我们在 groupby cumcount 部分使用 NumPy -
# Perform groupby cumcount on sorted array
def groupby_cumcount(idx):
mask = np.r_[False,idx[:-1]==idx[1:],False]
ids = mask[:-1].cumsum()
count = np.diff(np.flatnonzero(~mask))
return ids - np.repeat(ids[~mask[:-1]],count)
def diff_v3(a, b):
# Get sorted orders
sidx = a.argsort(kind='stable')
A = a[sidx]
# Get searchsorted indices per sorted order
idx = np.searchsorted(A,b)
# Get increments
idx = np.sort(idx)
inc = groupby_cumcount(idx)
# Delete elemnents off traced back positions
return np.delete(a,sidx[idx+inc])
基准测试
使用带有 10000
的设置~2x
的元素a
的重复次数和 b
是 a
的一半大小.
In [52]: np.random.seed(0)
...: a = np.random.randint(0,5000,10000)
...: b = a[np.random.choice(len(a), 5000,replace=False)]
In [53]: %timeit diff(a,b)
...: %timeit diff_v2(a,b)
...: %timeit diff_v3(a,b)
108 ms ± 821 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
3.85 ms ± 53.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.89 ms ± 15.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
接下来,在 100000
元素 -
In [54]: np.random.seed(0)
...: a = np.random.randint(0,50000,100000)
...: b = a[np.random.choice(len(a), 50000,replace=False)]
In [55]: %timeit diff(a,b)
...: %timeit diff_v2(a,b)
...: %timeit diff_v3(a,b)
4.45 s ± 20.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
37.5 ms ± 661 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
28 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
对于正数和排序输出
我们可以使用 np.bincount
-
def diff_v4(a, b):
C = np.bincount(a)
C -= np.bincount(b,minlength=len(C))
return np.repeat(np.arange(len(C)), C)
关于python - 根据元素出现在另一个数组中的次数,删除 NumPy 数组中元素的最高效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62613826/