2017-10-04 97 views
1

运行np.unique()时,它首先将数组平整,对数组进行排序,然后查找唯一值。当我有形状的阵列(10,3000,3000)时,需要大约一秒钟的时间才能找到唯一身份,但这很快就会加起来,因为我需要多次调用np.unique()。由于我只关心数组中唯一数字的总数,排序似乎是浪费时间。有效地计算唯一元素的数量--NumPy/Python

是否有更快的方法找到除np.unique()以外的大数组中唯一值的总数?

+0

你是什么阵列的数据类型(例如什么是“a.dtype”)? –

+0

@WarrenWeckesser uint8 – onepint16oz

+4

熊猫的独特功能不排序,因此速度更快。您可能需要检查:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.unique.html – ayhan

回答

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) 

所以2.86秒为np.unique(),和0.3秒的替代方法。

+0

这工作完美。谢谢。它似乎也可以用来返回一个排序的唯一数组。我想知道为什么numpy决定实施他们独特的()方式。 – onepint16oz

+0

'numpy.unique'可以处理任意数据类型的数组(所有的各种整数和浮点类型,复数类型和结构化数组)。对于'numpy.unique'来说,使用这种方法处理一个(可能是两个)字节整数类型是一种特殊情况,这似乎是可行的。 –

3

我们可以利用这样一个事实:元素被限制在uint8范围内,通过与np.bincount分箱计数,然后简单地计算其中的非零数。因为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) 

其他方法(ES ):

# @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 
+0

不错。我也尝试过'bincount',但它似乎比较慢。实际上,当我使用你的代码时,'assign_method(a)'为295 ms,'(np.bincount(a.ravel())!= 0).sum()'为425 ms。去搞清楚。 –

+0

@WarrenWeckesser可能是硬件(CPU,RAM)。我在英特尔i7-6700HQ,16GB RAM。但是,您的ideone具有可重现性:https://ideone.com/WDWq9j – Divakar

+0

我使用的是“2013年后期”Macbook Pro,2.6 GHz Intel Core i7,16 GB 1600 MHz DDR3。另外:Python 3.5.2,numpy 1.13.1。 –