运行np.unique()时,它首先展平数组,对数组进行排序,然后查找唯一值。当我的数组具有形状(10,3000,3000)时,需要大约一秒钟来查找唯一身份,但这很快就会增加,因为我需要多次调用np.unique()。由于我只关心数组中唯一数字的总数,因此排序似乎是浪费时间。
是否有更快的方法可以在除np.unique()之外的大数组中查找唯一值的总数?
答案 0 :(得分:5)
这是一个适用于dtype np.uint8
比np.unique
更快的数组的方法。
首先,创建一个可以使用的数组:
In [128]: a = np.random.randint(1, 128, size=(10, 3000, 3000)).astype(np.uint8)
要进行比较,请使用np.unique
:
In [129]: u = np.unique(a)
这是更快的方法; v
将包含结果:
In [130]: q = np.zeros(256, dtype=int)
In [131]: q[a.ravel()] = 1
In [132]: v = np.nonzero(q)[0]
验证我们得到了相同的结果:
In [133]: np.array_equal(u, v)
Out[133]: True
定时:
In [134]: %timeit u = np.unique(a)
2.86 s ± 9.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [135]: %timeit q = np.zeros(256, dtype=int); q[a.ravel()] = 1; v = np.nonzero(q)
300 ms ± 5.52 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
np.unique()
为2.86秒,替代方法为0.3秒。
答案 1 :(得分:3)
我们可以通过使用this进行分箱计数来利用元素限制在uint8
范围内的事实,然后简单地计算其中的非零数。由于np.bincount
需要1D
数组,我们会使用np.ravel()
展平输入,然后将其输入bincount
。
因此,实施将是 -
(np.bincount(a.ravel())!=0).sum()
运行时测试
帮助函数创建具有各种唯一编号的输入数组 -
def create_input(n_unique):
unq_nums = np.random.choice(np.arange(256), n_unique,replace=0)
return np.random.choice(unq_nums, (10,3000,3000)).astype(np.uint8)
其他方法:
# @Warren Weckesser's soln
def assign_method(a):
q = np.zeros(256, dtype=int)
q[a.ravel()] = 1
return len(np.nonzero(q)[0])
验证拟议方法 -
In [141]: a = create_input(n_unique=120)
In [142]: len(np.unique(a))
Out[142]: 120
In [143]: (np.bincount(a.ravel())!=0).sum()
Out[143]: 120
计时 -
In [124]: a = create_input(n_unique=128)
In [125]: %timeit len(np.unique(a)) # Original soln
...: %timeit assign_method(a) # @Warren Weckesser's soln
...: %timeit (np.bincount(a.ravel())!=0).sum()
...:
1 loop, best of 3: 3.09 s per loop
1 loop, best of 3: 394 ms per loop
1 loop, best of 3: 209 ms per loop
In [126]: a = create_input(n_unique=256)
In [127]: %timeit len(np.unique(a)) # Original soln
...: %timeit assign_method(a) # @Warren Weckesser's soln
...: %timeit (np.bincount(a.ravel())!=0).sum()
...:
1 loop, best of 3: 3.46 s per loop
1 loop, best of 3: 378 ms per loop
1 loop, best of 3: 212 ms per loop
答案 2 :(得分:0)
如果您不介意使用Numba来JIT编译代码,并修改代码以使Numba轻松实现其魔力,那么可以对已经在清单中列出的建议进行一些改进。其他答案。
使用@Divakar帖子中的命名:
from numba import jit
import numpy as np
def create_input(n_unique):
unq_nums = np.random.choice(np.arange(256), n_unique, replace=0)
return np.random.choice(unq_nums, (10, 3000, 3000)).astype(np.uint8)
def unique(a):
return len(np.unique(a))
def assign_method(a):
q = np.zeros(256, dtype=int)
q[a.ravel()] = 1
return len(np.nonzero(q)[0])
def bincount(a):
return (np.bincount(a.ravel())!=0).sum()
def numba_friendly(a):
q = np.zeros(256, dtype=int)
count = 0
for x in a.ravel():
if q[x] == 0:
q[x] = 1
count += 1
return count
unique_jit = jit(unique)
assign_method_jit = jit(assign_method)
bincount_jit = jit(bincount)
numba_friendly_jit = jit(numba_friendly)
基准:
a = create_input(n_unique=128)
%timeit unique(a)
%timeit unique_jit(a)
%timeit assign_method(a)
%timeit assign_method_jit(a)
%timeit bincount(a)
%timeit bincount_jit(a)
%timeit numba_friendly_jit(a)
结果:
unique: 7.5 s ± 1.14 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
unique_jit: 13.4 s ± 2.03 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
assign_method: 388 ms ± 84.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
assign_method_jit: 341 ms ± 27.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
bincount: 2.71 s ± 218 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
bincount_jit: 138 ms ± 40.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
numba_friendly_jit: 56.4 ms ± 8.96 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)