2014-02-19 2 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의 하위 목록에 저장합니다. 여기서 색인 (integ-1)

이 코드는 매우 빠르게 실행되지만 실제 코드는 훨씬 더 큰 목록을 사용하므로 성능을 향상시킬 필요가 있습니다.

+3

무엇을하고 싶은지 설명하십시오. 코드는 당신이 달성하고자하는 것을보다 직설적으로 수행 할 수있는 것처럼 보입니다. – Carsten

+0

아마도 발전기 또는 itertools가 필요합니다. 그러나 나는 @ 카 스텐에 동의한다. 당신의 목표가 무엇인지 설명하십시오. Btw b는 defaultdict가 될 수 있습니다. –

+1

재미를 위해서, 정렬 로직을위한 한 줄 짜기는'b = [np.where (a == i) for i (1, N)]''입니다. – Carsten

답변

2

이 솔루션을 사용해보십시오! Joe의 답변에서 나는 뻔뻔하게도 훔쳐간 전반부를 보았지만, 이후에는 정렬 및 이진 검색을 사용했습니다.이 검색은 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()을 넣을 수 (NB : 원본 코드에 a = a.tolist()을 할 경우, 당신은 상당한 속도 향상을 얻는다). 또한 안정된 정렬 인 'mergesort'을 사용했지만 최종 결과가 정렬 될 필요가없는 경우 더 빠른 정렬 알고리즘을 사용할 수 있습니다.

+1

몇 가지 테스트를 수행했지만 실제로이 버전은 원래 버전이나 Joe 버전보다 훨씬 빠릅니다. 하나의 경우에만이 버전이 원본보다 더 좋았습니다 ('a'를 '1000', 'N'을 10000으로 사용). 나머지 테스트는 훨씬 빨랐습니다. 죄송합니다. Joe하지만이 답변으로 받아 들여야합니다. @moarningsun 코드를 보내 주셔서 감사합니다! – Gabriel

+1

예 @ 가브리엘, 원래의 "알고리즘"은 단순히 가장 효율적입니다. 복잡한 "알고리즘"으로 먼 길을 갈아 타기 만하면 Numpy의 속도를 활용하고 'a'와 'N'의 적절한 크기를 위해 전체적으로 빠른 속도를 낼 수 있습니다. 원래 알고리즘을 사용하고 예를 들어 Cython 또는 Numba (이것이 얼마나 잘 될지 확신 할 수 없음). –

+1

@ 가브리엘 - 걱정 마세요, 이것은 훨씬 더 좋은 대답입니다! –

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 밀리 초 (~ 70x 속도 향상)입니다.

+0

위대한 답변 @ 조, 대단히 감사합니다. 확인하기 위해 큰 값인'N '('a = np.random.randint (0,100,5000)'과'N = 1000')에 대해이 응답이 실제로 원래 코드보다 약간 느린 것을 확인할 수 있습니까? – Gabriel

+1

@ 가브리엘 - 네, 맞아요. 'N'의 값이 크고 'a'의 길이가 낮은 경우 원래 함수의 경우 약간 더 빠릅니다. 매우 긴'a'의 경우, "new"함수는 다시 빠릅니다. 더 이상의 속도 향상을 얻을 수있는 방법이 없습니다. 행운을 빕니다! –

+0

다시 감사합니다. Joe! – Gabriel

관련 문제