2014-02-19 46 views
4

我有一小段代碼,我用它來填充整數列表。我需要提高它的性能,也許將整個事物轉化爲numpy陣列,但我不知道如何。快速填充列表

這裏的MWE

import numpy as np 

# List filled with integers. 
a = np.random.randint(0,100,1000) 

N = 10 
b = [[] for _ in range(N-1)] 
for indx,integ in enumerate(a): 
    if 0<elem<N: 
     b[integ-1].append(indx) 

這是它做什麼:

  • a
  • 每一個整數(integ)看它是否位於一個給定的範圍之間(0,N)
  • 如果是,則將其索引存儲在b的子列表中,其中索引所述子列表是用原來的整數減1(integ-1

這段代碼運行非常快,但我實際的代碼使用更大的列表,從而改善其性能的需要。

+3

請說明你想要做什麼。你的代碼看起來應該是一種更直接的方式來實現你想要達到的目標。 – Carsten

+0

可能你需要生成器或itertools。但我同意@Carsten。解釋你的目標是什麼。 Btw b可能是defaultdict(列表) –

+1

只是爲了好玩,排序邏輯的單線是'b = [np.where(a == i)for i in range(1,N)]''。 – Carsten

回答

2

試試這個解決方案!上半場我無恥地從喬的回答中偷走,但之後它使用排序和二進制搜索,這與N更好地擴展。

def new(a, N): 
    mask = (a > 0) & (a < N) 
    elements = a[mask] 
    indices = np.arange(a.size)[mask] 

    sorting_idx = np.argsort(elements, kind='mergesort') 
    ind_sorted = indices[sorting_idx] 

    x = np.searchsorted(elements, range(N), side='right', sorter=sorting_idx) 

    return [ind_sorted[x[i]:x[i+1]] for i in range(N-1)] 

你可以把x = x.tolist()在那裏額外雖然小速度的提高(注:如果您在最初的代碼做一個a = a.tolist(),你得到一個顯著加速)。此外,我使用'mergesort'這是一個穩定的排序,但如果你不需要排序的最終結果,你可以得到一個更快的排序算法。

+1

我做了一些測試,實際上這個版本比原來的版本或者Joe的版本要快得多。只有一種情況下,這個版本比原來的版本差(使用''''''''''和'''10000'''N'),但我執行的其他測試顯着更快。我很抱歉喬,但我必須切換到這個接受的答案。非常感謝代碼@moarningsun! – Gabriel

+1

是@Gabriel,你原來的「算法」就是最高效的。只有通過一個複雜的「算法」漫長的路,你可以利用Numpy的速度,並實現整體加速,適合大小的'a'和'N'。理想情況下,您可以使用原始算法並使用Cython或Numba(不知道這會發生多麼好)。 – 2014-02-24 11:42:58

+1

@Gabriel - 不用擔心,這是一個更好的答案! –

4

下面是做這件事的一種方法:

mask = (a > 0) & (a < N) 
elements = a[mask] 
indicies = np.arange(a.size)[mask] 

b = [indicies[elements == i] for i in range(1, N)] 

如果我們兩個時間:

import numpy as np 

a = np.random.randint(0,100,1000) 
N = 10 

def original(a, N): 
    b = [[] for _ in range(N-1)] 
    for indx,elem in enumerate(a): 
     if 0<elem<N: 
      b[elem-1].append(indx) 
    return b 

def new(a, N): 
    mask = (a > 0) & (a < N) 
    elements = a[mask] 
    indicies = np.arange(a.size)[mask] 

    return [indicies[elements == i] for i in range(1, N)] 

「新」 的方式是相當(〜20倍)速度快:

In [5]: %timeit original(a, N) 
100 loops, best of 3: 1.21 ms per loop 

In [6]: %timeit new(a, N) 
10000 loops, best of 3: 57 us per loop 

結果相同:

In [7]: new_results = new(a, N) 

In [8]: old_results = original(a, N) 

In [9]: for x, y in zip(new_results, old_results): 
    ....:  assert np.allclose(x, y) 
    ....: 

In [10]:   

「新」矢量化版本也對更長的序列進行縮放。如果我們對a使用了一個百萬個項目的序列,原始解決方案需要稍微超過1秒,而新版本只需要17毫秒(加速~70倍)。

+0

很好的答案@Joe,非常感謝。只是爲了檢查,你能否證實對於'N'的大數值(比如'a = np.random.randint(0,100,5000)'和'N = 1000'),這個答案實際上比原始代碼稍慢一點? – Gabriel

+1

@ Gabriel - Woops,是的,你說得對。對於'N'的大數值和低數千個'a'的長度,原始函數的時間稍微快一點。不過,對於很長的'a',「新」功能會再次變得更快。不過,我看不出有任何進一步加速的方法。祝你好運! –

+0

再次感謝喬! – Gabriel