Your method:
def dedup_reference(a, b):
for el in b:
idx = (el == a).argmax()
if a[idx] == el:
a = np.delete(a, idx)
return a
需要进行输入排序的
扫描方法:
def dedup_scan(arr, sel):
arr.sort()
sel.sort()
mask = np.ones_like(arr, dtype=np.bool)
sel_idx = 0
for i, x in enumerate(arr):
if sel_idx == sel.size:
break
if x == sel[sel_idx]:
mask[i] = False
sel_idx += 1
return arr[mask]
np.unique
计数方法:
def dedup_unique(arr, sel):
d_arr = dict(zip(*np.unique(arr, return_counts=True)))
d_sel = dict(zip(*np.unique(sel, return_counts=True)))
d = {k: v - d_sel.get(k, 0) for k, v in d_arr.items()}
res = np.empty(sum(d.values()), dtype=arr.dtype)
idx = 0
for k, count in d.items():
res[idx:idx+count] = k
idx += count
return res
你也许可以通过巧妙地使用numpy集合函数(例如np.in1d
)来实现与上述相同的功能,但我认为这不比使用字典更快。
以下是一种懒惰的基准测试尝试(已更新以包括@Divakar的diff_v2
和diff_v3
方法):
>>> def timeit_ab(f, n=10):
... cmd = f"{f}(a.copy(), b.copy())"
... t = timeit(cmd, globals=globals(), number=n) / n
... print("{:.4f} {}".format(t, f))
>>> array_copy = lambda x, y: None
>>> funcs = [
... 'array_copy',
... 'dedup_reference',
... 'dedup_scan',
... 'dedup_unique',
... 'diff_v2',
... 'diff_v3',
... ]
>>> def run_test(maxval, an, bn):
... global a, b
... a = np.random.randint(maxval, size=an)
... b = np.random.choice(a, size=bn, replace=False)
... for f in funcs:
... timeit_ab(f)
>>> run_test(10**1, 10000, 5000)
0.0000 array_copy
0.0617 dedup_reference
0.0035 dedup_scan
0.0004 dedup_unique (*)
0.0020 diff_v2
0.0009 diff_v3
>>> run_test(10**2, 10000, 5000)
0.0000 array_copy
0.0643 dedup_reference
0.0037 dedup_scan
0.0007 dedup_unique (*)
0.0023 diff_v2
0.0013 diff_v3
>>> run_test(10**3, 10000, 5000)
0.0000 array_copy
0.0641 dedup_reference
0.0041 dedup_scan
0.0022 dedup_unique
0.0027 diff_v2
0.0016 diff_v3 (*)
>>> run_test(10**4, 10000, 5000)
0.0000 array_copy
0.0635 dedup_reference
0.0041 dedup_scan
0.0082 dedup_unique
0.0029 diff_v2
0.0015 diff_v3 (*)
>>> run_test(10**5, 10000, 5000)
0.0000 array_copy
0.0635 dedup_reference
0.0041 dedup_scan
0.0118 dedup_unique
0.0031 diff_v2
0.0016 diff_v3 (*)
>>> run_test(10**6, 10000, 5000)
0.0000 array_copy
0.0627 dedup_reference
0.0043 dedup_scan
0.0126 dedup_unique
0.0032 diff_v2
0.0016 diff_v3 (*)
要点:
- 随着重复项数量的增加,
dedup_reference
的速度显著下降。
- 如果值范围较小,则
dedup_unique
是最快的。 diff_v3
很快,并且不取决于值的范围。
- 数组复制时间可以忽略不计。
- 字典非常酷。
性能特征强烈依赖于数据量(未经测试)和数据的统计分布。我建议使用自己的数据测试这些方法并选择最快的方法。请注意,各种解决方案会产生不同的输出,并对输入做出不同的假设。
for
循环遍历并切换掩码中的位,然后将该掩码应用于 numpy 数组。这样做可能比使用np.delete
反复重新创建数组要快得多。 - Mateen Ulhaqa
是否按照排序顺序排列? - Divakara
是array([3, 2, 3, 3, 2, 1])
,你希望使用diff
输出array([3, 3, 1])
还是希望得到排序后的输出array([1, 3, 3])
? - Divakar