2013-08-19 2 views
1

코드 처리에 약 2 시간이 걸립니다. 병목 현상은 for 루프에 있고 문장 인 경우 (코드의 주석 참조) 저는 파이썬으로 초급자입니다. 누군가가 중첩 된 for 및 if 문을 대체 할 효율적인 파이썬 방법을 추천 할 수 있습니까?내 코드에서 더 빠른 for/if 문에 대한 제안 사항?

나는 ~ 30,000,000 열의 테이블이, (X, Y, Z) 값을 각 행 :

20.0 11.3 7
21.0 11.3 0
22.0 11.3 3
...

원하는 출력은 x, y, min (z), count (min (z)) 형식의 테이블입니다. 마지막 열은 그 (x, y)에서 최소 z 값의 최종 개수입니다. 예 :
22.0 11.3 3 1

20.0 11.3 7 7
21.0 11.3 0 10 ...

가 약 600 독특한 좌표, 그래서 출력 테이블은 600x4한다

. 내 코드 :

import numpy as np 
file = open('input.txt','r'); 

coordset = set() 
data = np.zeros((600,4))*np.nan 
irow = 0 
ctr = 0 

for row in file: 
    item = row.split() 
    x = float(item[0]) 
    y = float(item[1]) 
    z = float(item[2]) 

    # build unique grid of coords 
    if ((x,y)) not in coordset: 
     data[irow][0] = x 
     data[irow][1] = y 
     data[irow][2] = z 
     irow = irow + 1  # grows up to 599 

    # lookup table of unique coords 
    coordset.add((x,y)) 

    # BOTTLENECK. replace ifs? for? 
    for i in range(0, irow): 
     if data[i][0]==x and data[i][1]==y: 
      if z > data[i][2]: 
       continue 
      elif z==data[i][2]: 
       ctr = ctr + 1 
       data[i][3]=ctr 
      if z < data[i][2]: 
       data[i][2] = z 
       ctr = 1 
       data[i][3]=ctr 

편집 : 참고로는 @Joowani에 의한 접근 방식은 1m26s에서 계산합니다. 내 원래의 접근 방식, 같은 컴퓨터, 동일한 데이터 파일, 106m23s. edit2 : @Ophion 및 @Sibster 제안에 감사드립니다. 유용한 답변을 +1 할만한 충분한 점수가 없습니다.

+0

은 txt로 저장하려면 30million 행 정말입니까? 데이터를 저장하고 읽을 수있는 좀 더 정교한 형식을 찾아야합니까? 또한 그 때부터 numpy, for 루프를 푸시하기 때문에 가능할 때마다 벡터화 (numpy)를 제안합니다. 따라서 C (따라서 더 빠름) – usethedeathstar

답변

2

귀하의 솔루션은 목록을 통해 반복되므로 (예 : 데이터) 업데이트 할 때마다 번 업데이트가 발생하므로 솔루션이 느리게 보입니다. 더 나은 접근법은 업데이트 당 O (n) 대신 O (1)을 사용하는 사전을 사용하는 것입니다.

file = open('input.txt', 'r') 

#coordinates 
c = {} 

for line in file: 
    #items 
    (x, y, z) = (float(n) for n in line.split()) 

    if (x, y) not in c: 
     c[(x, y)] = [z, 1] 
    elif c[(x, y)][0] > z: 
     c[(x, y)][0], c[(x, y)][1] = z, 1 
    elif c[(x, y)][0] == z: 
     c[(x, y)][1] += 1 

for key in c: 
    print("{} {} {} {}".format(key[0], key[1], c[key][0], c[key][1])) 
+1

일부를 추가하려면 참조 (http://wiki.python.org/moin/TimeComplexity) 사전 작업의 효율성을 설명합니다. – 3150

0

마지막으로 if를 elif로 변경하지 않으시겠습니까?

마치 루프의 반복마다 z < data[i][2]:을 평가하게됩니다.

이미 if z>data[i][2]을 확인하고 z == data[i][2] 그렇게 남아있는 유일한 가능성은 그래서 다음 코드는 동일한 작업을 수행하고 빠르게 처리 될 수 z < data[i][2]:

때문에 당신은 단지 다른 사람으로 대체 할 수 :

 if z > data[i][2]: 
      continue 
     elif z==data[i][2]: 
      ctr = ctr + 1 
      data[i][3]=ctr 
     else: 
      data[i][2] = z 
      ctr = 1 
      data[i][3]=ctr 
0

이 NumPy와 사용 np.unique에서이 작업을 수행하려면 : 여기

는 사전을 사용하여 내 솔루션이 될 것입니다.

def count_unique(arr): 
    row_view=np.ascontiguousarray(a).view(np.dtype((np.void,a.dtype.itemsize * a.shape[1]))) 
    ua, uind = np.unique(row_view,return_inverse=True) 
    unique_rows = ua.view(a.dtype).reshape(ua.shape + (-1,)) 
    count=np.bincount(uind) 
    return np.hstack((unique_rows,count[:,None])) 

먼저 작은 배열에 대한 검사를 할 수 있습니다 :

a=np.random.rand(10,3) 
a=np.around(a,0) 

print a 
[[ 0. 0. 0.] 
[ 0. 1. 1.] 
[ 0. 1. 0.] 
[ 1. 0. 0.] 
[ 0. 1. 1.] 
[ 1. 1. 0.] 
[ 1. 0. 1.] 
[ 1. 0. 1.] 
[ 1. 0. 0.] 
[ 0. 0. 0.]] 

print output 
[[ 0. 0. 0. 2.] 
[ 0. 1. 0. 1.] 
[ 0. 1. 1. 2.] 
[ 1. 0. 0. 2.] 
[ 1. 0. 1. 2.] 
[ 1. 1. 0. 1.]] 

print np.sum(output[:,-1]) 
10 

좋아 보인다!이제 큰 배열을 확인 할 수 있습니다 : 큰 배열 가능성이 병목이 될 것이기 때문

a=np.random.rand(3E7,3) 
a=np.around(a,1) 

output=count_unique(a) 
print output.shape 
(1331, 4) #Close as I can get to 600 unique elements. 

print np.sum(output[:,-1]) 
30000000.0 

모든 메모리에이 일을, 내 컴퓨터에 약 33 초 소요 및 메모리의 3기가바이트 참고로 @ Joowani의 해결책은 약 130 초가 걸렸지 만, 사과와 오렌지 비교가 약간이지만, 우리는 빈약 한 배열로 시작합니다. 귀하의 마일리지는 다를 수 있습니다.

내가 질문 here을 볼 것 NumPy와 배열로 데이터를 읽으려면, 그러나 그것은 다음과 같이 보일 것입니다 : 정말 사용하는 것이 좋습니다 것 txt 파일에서 그 많은 데이터에

arr=np.genfromtxt("./input.txt", delimiter=" ") 

로드를 해당 링크의 예제는 pandas입니다.