2011-09-06 2 views
2

을 감안할 때 두 개의 파일 형태 중 하나 개 포함 된 항목 :큰 파일 - 파이썬 또는 명령 줄 처리시 제안 사항?

label1 label2 name1 
label1 label3 name2 

양식의 다른 :

label1 label2 name1 0.1 1000 
label9 label6 name7 0.8 0.5 

는 당신이 첫 번째 세 가지 요소에 대한 파일이에서 그 라인을 추출 할 가정 한 줄에 (중요한 순서로) 나타납니다. 이것이 어떻게 빨리 돔에 있을지에 대한 제안?

위의 샘플 데이터가 될 것이다 주어진 이러한 스크립트의 출력 파일 :

label1 label2 name1 0.1 1000 

내가 파이썬 함께 놀다가 :

inp = open(file1.txt, 'r') 
look_up = [i.split() for i in inp.readlines()] 
inp.close() 

inp = open('file2', 'wt') 

holder = [] 

line = inp.readline() 
while line: 
    line = line.split() 
    if [line[0], line[1], line[2]] in look_up: 
     holder.append(line) 
    line = inp.readline() 

그러나이 걸릴 것으로 보인다. 이 파일들은 꽤 큽니다.

감사합니다.

+3

"다소 큰"크기는 어느 정도입니까? 메가 바이트? 기가 바이트? 테라 바이트? –

+2

나는이 질문에 대한 답을 얻고 답변을 얻으 려 할 때 장기간의 시도가 끝날 수 있었는지 궁금 할 것입니다. 일회성 문제의 경우 최적의 솔루션이 아니더라도 가장 쉬운 솔루션이 가장 좋은 경우가 많습니다. –

+0

@ Mark - 장거리 달리기 시도가 16 개의 작업으로 분할되어 클러스터에 배치되었습니다. 나중에 6 시간은 아직도 달리고있다! Eeek! –

답변

8

파이썬 버전이 오히려 비효율적이다 (O (1) 대신 O (n) 조회 시간)을 사용하는 것이 좋습니다.

튜플이 set이거나 문자열이 set 인 대신 사용해보십시오. 두 파일이 서로 다른 구분 기호로 분리 될 수 있기 때문에 튜플이 더 나은 선택이 될 수 있지만 특히 성능 차이가 크지는 않을 것으로 생각됩니다. tuple('something'.split())은 매우 긴 목록의 멤버십 테스트에 비해 상대적으로 빠릅니다.

또한 inp.readlines()으로 전화 할 필요가 없습니다. 즉, 당신은 단지

look_up = set(tuple(line.split()) for line in inp) 

을 할 수있는 그리고 당신은 tuple(line[:3])보다는 [line[0], line[1], line[2]]가 아닌 다른 코드의 다른 부분을 변경하지 않고도 상당한 속도 향상을 볼 수 있습니다. 사실

은 GREP 및 배쉬는 ...이 꽤 완벽한 (테스트되지 않은,하지만 작동합니다.)

while read line 
do 
    grep "$line" "file2.txt" 
done < "file1.txt" 

한 빨리 어떤 확인하려면, 우리가 할 수 generate some test data (~ file1.txt에서 4500 키와 1000000 lines in file2.txt), 벤치마킹 같은 python 버전의 간단한 (대략 ... grep 버전과 다른 순서로 행이 인쇄됩니다.)

[email protected]:~/so> time sh so_temp149.sh > a 

real 1m47.617s 
user 0m51.199s 
sys  0m54.391s 

대 :

with open('file1.txt', 'r') as keyfile: 
    lookup = set(tuple(line.split()) for line in keyfile) 

with open('file2.txt', 'r') as datafile: 
    for line in datafile: 
     if tuple(line.split()[:3]) in lookup: 
      print line, 

파이썬 버전은 ~로 70 배 빠른 밝혀물론

[email protected]:~/so> time python so_temp149.py > b 

real 0m1.631s 
user 0m1.558s 
sys  0m0.071s 

는 두 가지 예는 완전히 다른 방법으로 문제를 접근하고있다. 우리는 두 가지 알고리즘을 비교하고 있으며 두 가지 구현을 비교하지 않았습니다. 예를 들어, file1에 두 개의 핵심 행만있는 경우 bash/grep 솔루션이 쉽게 승부를가집니다.

(bash에는 멤버쉽에 대한 O (1) 조회가있는 일종의 컨테이너가 내장되어 있습니까? (배쉬 4에는 해시 테이블이있을 수 있지만 그것에 대해 아무것도 알지 못합니다 ...)) ...뿐만 아니라, bash는 위의 파이썬 예제와 유사한 알고리즘을 구현하려고 흥미

+2

'집합'목록을 가질 수 없습니다. 모든'set' 요소는 해시 가능해야합니다 (완전히 불변). –

+0

@ 존 Y - 맞아! 나는 생각하지 않고 아무것도 테스트하지 않았다. .. 고마워! –

+1

'set (tuple (line.split()) for inp inline)'이 더 나은 방법 일 수 있습니다. –

1

문자열 "label1 label2 name1"을 키로 사용할 수 있습니다. 값의 세 겹이 아닙니다.

1

첫 번째 파일의 값을 저장하기 위해 해시를 사용합니다. 아니 그 오류 반발하지만 (1 각 항목 사이에만 1 공간)하지만 당신은 일반적인 아이디어를 얻을거야 ... 당신이 목록에서 회원 가입을 테스트하고 있기 때문에

#!/usr/bin/env python 

labels={} 
with open('log') as fd: 
    for line in fd: 
     line=line.strip() 
     labels[line]=True 

with open('log2') as fd: 
    for line in fd: 
     if " ".join(line.split()[0:3]) in labels: 
      print line 
3

해키 bash는/분류/펄 솔루션 :

$ cat > 1 
label1 label2 name1 
label1 label3 name2 

$ cat > 2 
label1 label2 name1 0.1 1000 
label9 label6 name7 0.8 0.5 

$ (cat 1; cat 2;) | sort | perl -ne 'INIT{$pattern_re="(?:\\S+) (?:\\S+) (?:\\S+)"; $current_pattern="";} if(/^($pattern_re)$/o){$current_pattern=$1} else {if(/^($pattern_re)/o) { print if $1 eq $current_pattern} }' 
label1 label2 name1 0.1 1000 

그것은 정렬 한리스트로 두 파일을 병합 (그래서 우리는 동일한 키를 가진 데이터 덩어리를 파일 1의 라인 단위로 가져온다.) 그런 다음 특수 Perl oneliner를 사용하여 파일 1에서 선행하는 "헤더"가있는 올바른 형식의 행만 남겨둔다.

관련 문제