2015-09-15 34 views
2

正如標題所說,我正在尋找一種轉換數組的方式,以便它將成爲其適當元素的頻率數組。numpy將數組的元素轉換爲其頻率的最快方法

我發現np.countnp.histogram,但它不是我所期待的

喜歡的東西:

來源:

array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0]) 

要:提前

array_ = np.array([8,8,8,2,8,8,2,8,8,2,2,8]) 

謝謝!

+0

'array_'是否包含大值? 'np.bincount(array _)[array_]'可以滿足你這個例子的需要,但是如果你的原始'array_'中有很大的值,那麼效率會很低。 –

+1

使用熊貓是否可用?如果是這樣,你可以使用'pd.Series(array _)。map(pd.value_counts(array _))。values'。這種基於哈希表的方法相當快速 - 仍然比Mark Dickinson提出的超快速「計數」方法慢得多,但比「獨特」快得多,而且比「Counter」(在我的機器上)快得多。 –

回答

5

如果陣列中的值都是非負整數,其也不會太大,你可以使用np.bincount。使用原始數組作爲bincount結果的索引可獲得所需的輸出。

>>> array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0]) 
>>> np.bincount(array_) 
array([8, 2, 2]) 
>>> np.bincount(array_)[array_] 
array([8, 8, 8, 2, 8, 8, 2, 8, 8, 2, 2, 8]) 

熊記住,np.bincount結果有大小max(array_) + 1,所以如果你的陣列有較大的值這種方式是低效的:你最終建立一個非常大的中間結果。

另一種方法應該是即使有大的或負的輸入有效的是使用np.uniquereturn_inversereturn_counts參數,如下所示:

>>> array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0]) 
>>> _, inv, counts = np.unique(array_, return_inverse=True, return_counts=True) 
>>> counts[inv] 
array([8, 8, 8, 2, 8, 8, 2, 8, 8, 2, 2, 8]) 

注意,return_counts論點NumPy的1.9.0是新,所以你需要一個最新版本的NumPy。如果你沒有NumPy 1.9.0,一切都不會丟失!您仍然可以使用np.unique的參數return_inverse,它可以讓您返回與原始佈局相同排列的小整數數組。這個新的數組現在是在完美的形狀bincount以它高效地工作:

>>> array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0]) 
>>> _, inverse = np.unique(array_, return_inverse=True) 
>>> np.bincount(inverse)[inverse] 
array([8, 8, 8, 2, 8, 8, 2, 8, 8, 2, 2, 8]) 

又如,具有較大array_內容:

>>> array_ = np.array([0, 71, 598, 71, 0, 0, 243]) 
>>> _, inverse = np.unique(array_, return_inverse=True) 
>>> inverse 
array([0, 1, 3, 1, 0, 0, 2]) 
>>> np.bincount(inverse)[inverse] 
array([3, 2, 1, 2, 3, 3, 1]) 

所有這些解決方案中純NumPy的工作,所以他們應該比通過Python Counterdict的解決方案效率更高。但是,一如既往,如果效率是一個問題,那麼你應該找出最適合的方法。特別要注意的是,np.unique正在進行一些分析,所以它的理論複雜度要高於純粹的np.bincount解決方案。無論在實踐中是否有所作爲都不可能沒有時間表示。 所以我們來做一些計時,使用IPython的timeit(這是在Python 3.4上)。首先,我們將定義功能,我們需要的操作:

In [1]: import numpy as np; from collections import Counter 

In [2]: def freq_bincount(array): 
    ...:  return np.bincount(array)[array] 
    ...: 

In [3]: def freq_unique(array): 
    ...:  _, inverse, counts = np.unique(array, return_inverse=True, return_counts=True) 
    ...:  return counts[inverse] 
    ...: 

In [4]: def freq_counter(array): 
    ...:  c = Counter(array) 
    ...:  return np.array(list(map(c.get, array))) 
    ...: 

現在我們創建一個測試陣列:

In [5]: test_array = np.random.randint(100, size=10**6) 

然後我們做了一些時機。這裏是我的機器上的結果:

In [6]: %timeit freq_bincount(test_array) 
100 loops, best of 3: 2.69 ms per loop 

In [7]: %timeit freq_unique(test_array) 
10 loops, best of 3: 166 ms per loop 

In [8]: %timeit freq_counter(test_array) 
1 loops, best of 3: 317 ms per loop 

還有的np.bincount方法和np.unique方法之間的訂單數量級的差別。來自@ Kasramvd解決方案的Counter方法比np.unique方法稍微慢一些,但這可能會在另一臺機器上或不同版本的Python和NumPy中改變:您應該測試適合您的用例的數據。

3

作爲一個快速的方法,你可以使用colections.Counter這是獲得一個可迭代項目的頻率更Python的方式:

>>> import numpy as np 
>>> array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0]) 
>>> from collections import Counter 
>>> c=Counter(array_) 
>>> np.array(map(c.get,array_)) 
array([8, 8, 8, 2, 8, 8, 2, 8, 8, 2, 2, 8]) 
相關問題