2017-10-04 102 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。 –