[python] 큰 파일을 한 줄씩 읽는 방법?

전체 파일의 각 줄을 반복하고 싶습니다. 이를 수행하는 한 가지 방법은 전체 파일을 읽고 목록에 저장 한 다음 관심있는 행을 넘어가는 것입니다. 이 방법은 많은 메모리를 사용하므로 대안을 찾고 있습니다.

지금까지 내 코드 :

for each_line in fileinput.input(input_file):
    do_something(each_line)

    for each_line_again in fileinput.input(input_file):
        do_something(each_line_again)

이 코드를 실행하면 오류 메시지가 나타 device active납니다..

어떤 제안?

목적은 파일의 각 줄에 대한 쌍별 문자열 유사성을 계산하는 것입니다. 다른 모든 줄과의 Levenshtein 거리를 계산하고 싶습니다.



답변

파일을 읽는 올바른 완전 Pythonic 방법은 다음과 같습니다.

with open(...) as f:
    for line in f:
        # Do something with 'line'

with열고 예외 이너 블록에서 발생되는 경우를 포함하여, 파일을 닫는 문 손잡이. 이 for line in f파일 객체 f는 반복 가능한 파일 객체 로 취급되며 버퍼링 된 I / O 및 메모리 관리를 자동으로 사용하므로 큰 파일에 대해 걱정할 필요가 없습니다.

그것을 할 수있는 분명한 방법이 하나 있어야합니다.


답변

순위가 높은 두 가지 메모리 효율적인 방법 (첫 번째가 가장 좋음)-

  1. 사용 with-Python 2.5 이상에서 지원
  2. yield당신이 정말로 읽을 양을 제어하고 싶다면 사용

1. 사용 with

with큰 파일을 읽는 훌륭하고 효율적인 파이썬 방법입니다. 장점-1) with실행 블록 을 종료 한 후 파일 객체가 자동으로 닫힙니다 . 2) with블록 내부의 예외 처리 . 3) 메모리 for루프는 f파일 객체를 한 줄씩 반복 합니다. 내부적으로 버퍼링 된 IO (비용이 많이 드는 IO 작업에 최적화) 및 메모리 관리를 수행합니다.

with open("x.txt") as f:
    for line in f:
        do something with data

2. 사용 yield

때로는 각 반복에서 읽을 양을보다 세밀하게 제어하기를 원할 수 있습니다. 이 경우 iter & yield를 사용하십시오 . 이 방법을 사용하면 마지막에 파일을 명시 적으로 닫아야합니다.

def readInChunks(fileObj, chunkSize=2048):
    """
    Lazy function to read a file piece by piece.
    Default chunk size: 2kB.
    """
    while True:
        data = fileObj.read(chunkSize)
        if not data:
            break
        yield data

f = open('bigFile')
for chuck in readInChunks(f):
    do_something(chunk)
f.close()

함정과 완전성을 위해 -아래 방법은 큰 파일을 읽는 데 좋지 않거나 우아하지 않지만 둥근 이해를 위해 읽으십시오.

파이썬에서 파일에서 행을 읽는 가장 일반적인 방법은 다음을 수행하는 것입니다.

for line in open('myfile','r').readlines():
    do_something(line)

그러나이 작업이 완료되면 readlines()함수 (기능에 동일하게 적용됨 read())가 전체 파일을 메모리에로드 한 다음 반복합니다. 큰 파일에 대해 약간 더 나은 방법 (처음 언급 한 두 가지 방법이 가장 좋습니다)은 fileinput다음과 같이 모듈 을 사용하는 것입니다.

import fileinput

for line in fileinput.input(['myfile']):
    do_something(line)

fileinput.input()호출은 행을 순차적으로 읽지 만 file파이썬에서 반복 할 수 있기 때문에 읽거나 읽은 후에도 메모리에 보관하지 않습니다 .

참고 문헌

  1. 문장이있는 파이썬

답변

줄 바꿈을 제거하려면 :

with open(file_path, 'rU') as f:
    for line_terminated in f:
        line = line_terminated.rstrip('\n')
        ...

함께 보편적 인 개행 지원 모든 텍스트 파일 행이 종료 될 것처럼 보일 '\n'어떤 파일의 터미네이터,, '\r', '\n', 또는 '\r\n'.

편집- 범용 개행 지원을 지정하려면 다음을 수행하십시오.

  • 유닉스에서 파이썬 2–open(file_path, mode='rU') 필수 [감사합니다 @Dave ]
  • Windows의 Python 2– open(file_path, mode='rU')옵션
  • 파이썬 3– open(file_path, newline=None)옵션

newline매개 변수는 Python 3에서만 지원되며 기본값은 None입니다. 이 mode매개 변수 'r'는 모든 경우에 기본값 입니다. 는 U다른 메커니즘이 번역 나타나는 윈도우에서 파이썬 3에서 파이썬 2에서 더 이상 사용되지 않습니다 \r\n\n.

문서 :

기본 줄 종결자를 유지하려면

with open(file_path, 'rb') as f:
    with line_native_terminated in f:
        ...

이진 모드는 여전히 파일을로 줄을 분석 할 수 있습니다 in. 각 줄에는 파일에있는 모든 종결자가 있습니다.

감사합니다 @katrielalex대답 , 파이썬의 개방 () 문서 및 iPython의 실험.


답변

이것은 파이썬에서 파일을 읽는 가능한 방법입니다.

f = open(input_file)
for line in f:
    do_stuff(line)
f.close()

전체 목록을 할당하지 않습니다. 그것은 라인을 반복합니다.


답변

내가 어디에서 왔는지에 대한 몇 가지 맥락. 코드 스 니펫이 끝났습니다.

가능하면 H2O와 같은 오픈 소스 도구를 사용하여 초 고성능 병렬 CSV 파일 읽기를 선호하지만이 도구는 기능 세트에 제한이 있습니다. 지도 학습을 위해 H2O 클러스터에 공급하기 전에 데이터 과학 파이프 라인을 만들기 위해 많은 코드를 작성하게되었습니다.

멀티 프로세싱 라이브러리의 풀 객체 및 맵 기능으로 많은 병렬 처리를 추가하여 UCI 리포지토리에서 8GB HIGGS 데이터 세트와 40GB CSV 파일과 같은 파일을 데이터 과학 목적으로 훨씬 빠르게 읽었습니다. 예를 들어 가장 가까운 이웃 검색을 사용하는 클러스터링과 DBSCAN 및 Markov 클러스터링 알고리즘은 심각하게 어려운 메모리 및 월 클럭 시간 문제를 우회하기 위해 병렬 프로그래밍 기술이 필요합니다.

나는 보통 gnu 도구를 사용하여 파일을 행 단위로 나누고 python 프로그램에서 병렬로 파일을 찾아서 읽을 수 있도록 glob-filemask합니다. 나는 일반적으로 1000+ 부분 파일과 같은 것을 사용합니다. 이러한 트릭을 수행하면 처리 속도 및 메모리 제한에 크게 도움이됩니다.

pandas dataframe.read_csv는 단일 스레드이므로 병렬 실행을 위해 map ()을 실행하여 팬더를 훨씬 빠르게 만들기 위해 이러한 트릭을 수행 할 수 있습니다. htop을 사용하여 일반 오래된 순차 팬더 dataframe.read_csv를 사용하면 하나의 코어에서 100 % CPU가 디스크가 아닌 pd.read_csv의 실제 병목 현상임을 알 수 있습니다.

SATA6 버스에서 회전하는 HD가 아니라 16 개의 CPU 코어가 아닌 빠른 비디오 카드 버스에서 SSD를 사용하고 있습니다.

또한 일부 응용 프로그램에서 훌륭하게 작동하는 것으로 밝혀진 또 다른 기술은 하나의 큰 파일을 여러 부분 파일로 사전 분할하는 대신 하나의 거대한 파일 내에서 병렬 CSV 파일 읽기를 수행하여 파일마다 다른 오프셋에서 각 작업자를 시작하는 것입니다. 각 병렬 작업자에서 python의 seek () 및 tell ()을 사용하여 큰 텍스트 파일을 큰 파일의 다른 바이트 오프셋 시작 바이트 및 끝 바이트 위치에서 동시에 동시에 읽을 수 있습니다. 바이트에서 정규식 찾기를 수행하고 줄 바꿈 수를 반환 할 수 있습니다. 이것은 부분 합계입니다. 마지막으로 작업자가 완료 한 후지도 함수가 반환 될 때 전체 합계를 얻기 위해 부분 합계를 합산합니다.

다음은 병렬 바이트 오프셋 트릭을 사용하는 예제 벤치 마크입니다.

나는 2 개의 파일을 사용합니다 : HIGGS.csv는 8GB입니다. UCI 기계 학습 저장소에서 제공됩니다. all_bin .csv는 40.4GB이며 현재 프로젝트에서 가져온 것입니다. 나는 리눅스와 함께 제공되는 GNU wc 프로그램과 내가 개발 한 순수한 python fastread.py 프로그램의 두 가지 프로그램을 사용한다.

HP-Z820:/mnt/fastssd/fast_file_reader$ ls -l /mnt/fastssd/nzv/HIGGS.csv
-rw-rw-r-- 1 8035497980 Jan 24 16:00 /mnt/fastssd/nzv/HIGGS.csv

HP-Z820:/mnt/fastssd$ ls -l all_bin.csv
-rw-rw-r-- 1 40412077758 Feb  2 09:00 all_bin.csv

ga@ga-HP-Z820:/mnt/fastssd$ time python fastread.py --fileName="all_bin.csv" --numProcesses=32 --balanceFactor=2
2367496

real    0m8.920s
user    1m30.056s
sys 2m38.744s

In [1]: 40412077758. / 8.92
Out[1]: 4530501990.807175

약 4.5GB / s (45Gb / s) 파일 슬러 핑 속도입니다. 내 친구 친구, 하드 디스크가 회전하지 않습니다. 실제로 삼성 Pro 950 SSD입니다.

다음은 순수한 C 컴파일 프로그램 인 gnu wc에 의해 동일한 파일에 대한 속도 벤치 마크입니다.

멋진 점은 순수한 파이썬 프로그램 이이 경우 gnu wc 컴파일 된 C 프로그램의 속도와 본질적으로 일치한다는 것을 알 수 있습니다. 파이썬은 해석되지만 C는 컴파일되므로 속도가 매우 흥미 롭습니다. 나는 당신이 동의 할 것이라고 생각합니다. 물론, wc는 실제로 병렬 프로그램으로 변경되어야하며, 실제로 파이썬 프로그램에서 양말을 이길 것입니다. 그러나 오늘날에도 gnu wc는 순차적 인 프로그램입니다. 당신은 당신이 할 수있는 일을하고, 파이썬은 오늘날 병렬 할 수 있습니다. Cython 컴파일은 나를 도울 수 있습니다 (다른 시간 동안). 또한 메모리 매핑 파일은 아직 탐색되지 않았습니다.

HP-Z820:/mnt/fastssd$ time wc -l all_bin.csv
2367496 all_bin.csv

real    0m8.807s
user    0m1.168s
sys 0m7.636s


HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000

real    0m2.257s
user    0m12.088s
sys 0m20.512s

HP-Z820:/mnt/fastssd/fast_file_reader$ time wc -l HIGGS.csv
11000000 HIGGS.csv

real    0m1.820s
user    0m0.364s
sys 0m1.456s

결론 : 속도는 C 프로그램에 비해 순수한 파이썬 프로그램에 적합합니다. 그러나 최소한 라인 계산 목적으로 C 프로그램보다 순수한 파이썬 프로그램을 사용하는 것만으로는 충분하지 않습니다. 일반적 으로이 기술은 다른 파일 처리에 사용될 수 있으므로이 파이썬 코드는 여전히 좋습니다.

질문 : 정규식을 한 번만 컴파일하고 모든 근로자에게 전달하면 속도가 향상됩니까? 답 : 정규식 사전 컴파일은이 응용 프로그램에서 도움이되지 않습니다. 그 이유는 모든 작업자의 프로세스 직렬화 및 생성 오버 헤드가 지배적이라고 생각합니다.

하나 더. 병렬 CSV 파일 읽기가 도움이 되나요? 디스크에 병목 현상이 있습니까, 아니면 CPU입니까? 스택 오버 플로우에 대한 많은 소위 최상위 답변에는 파일을 읽는 데 하나의 스레드 만 필요하다는 공통의 지혜가 포함되어 있습니다. 그래도 확실합니까?

알아 보자:

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000

real    0m2.256s
user    0m10.696s
sys 0m19.952s

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=1
11000000

real    0m17.380s
user    0m11.124s
sys 0m6.272s

예, 그렇습니다. 병렬 파일 읽기는 아주 잘 작동합니다. 잘가요!

추신. 일부 작업자가 알고 싶어하는 경우 단일 작업자 프로세스를 사용할 때 balanceFactor가 2 인 경우 어떻게해야합니까? 글쎄, 끔찍하다.

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=2
11000000

real    1m37.077s
user    0m12.432s
sys 1m24.700s

fastread.py 파이썬 프로그램의 주요 부분 :

fileBytes = stat(fileName).st_size  # Read quickly from OS how many bytes are in a text file
startByte, endByte = PartitionDataToWorkers(workers=numProcesses, items=fileBytes, balanceFactor=balanceFactor)
p = Pool(numProcesses)
partialSum = p.starmap(ReadFileSegment, zip(startByte, endByte, repeat(fileName))) # startByte is already a list. fileName is made into a same-length list of duplicates values.
globalSum = sum(partialSum)
print(globalSum)


def ReadFileSegment(startByte, endByte, fileName, searchChar='\n'):  # counts number of searchChar appearing in the byte range
    with open(fileName, 'r') as f:
        f.seek(startByte-1)  # seek is initially at byte 0 and then moves forward the specified amount, so seek(5) points at the 6th byte.
        bytes = f.read(endByte - startByte + 1)
        cnt = len(re.findall(searchChar, bytes)) # findall with implicit compiling runs just as fast here as re.compile once + re.finditer many times.
    return cnt

PartitionDataToWorkers의 def는 일반적인 순차 코드입니다. 다른 사람이 병렬 프로그래밍이 어떤지 연습하고 싶어하는 경우에 대비하여 생략했습니다. 학습의 이점을 위해 테스트 및 병렬 코드를 제공하는 더 어려운 부분을 무료로 제공했습니다.

덕분에 : Arno와 Cliff와 H2O 직원의 오픈 소스 H2O 프로젝트는 훌륭한 소프트웨어 및 교육용 비디오를 제공하여 위와 같이이 순수한 파이썬 고성능 병렬 바이트 오프셋 리더에 영감을주었습니다. H2O는 java를 사용하여 병렬 파일 읽기를 수행하고, Python 및 R 프로그램에서 호출 할 수 있으며, 큰 CSV 파일을 읽을 때 지구상에서 가장 빠릅니다.


답변

Katrielalex는 하나의 파일을 열고 읽는 방법을 제공했습니다.

그러나 알고리즘이 진행되는 방식에 따라 파일의 각 줄에 대한 전체 파일을 읽습니다. 즉 , N이 파일의 줄 수이면 파일을 읽는 전체 량과 레 벤슈 테인 거리 계산은 N * N이됩니다. 파일 크기가 걱정되고 메모리에 보관하고 싶지 않기 때문에 결과 2 차 런타임이 걱정 됩니다 . 알고리즘은 O (n ^ 2) 알고리즘 클래스에 있으며 전문화로 개선 할 수 있습니다.

메모리와 런타임의 트레이드 오프를 이미 알고 있다고 생각하지만 여러 Levenshtein 거리를 병렬로 계산하는 효율적인 방법이 있는지 조사하고 싶을 것입니다. 그렇다면 여기에서 솔루션을 공유하는 것이 흥미로울 것입니다.

파일에 몇 줄의 줄이 있고 알고리즘을 실행해야하는 컴퓨터 종류 (mem & cpu power)와 허용되는 런타임은 무엇입니까?

코드는 다음과 같습니다.

with f_outer as open(input_file, 'r'):
    for line_outer in f_outer:
        with f_inner as open(input_file, 'r'):
            for line_inner in f_inner:
                compute_distance(line_outer, line_inner)

그러나 문제는 거리 (매트릭스)를 어떻게 저장합니까? 예를 들어 처리를 위해 outer_line을 준비하거나 재사용을 위해 중간 결과를 캐싱하는 이점을 얻을 수 있습니다.


답변

#Using a text file for the example
with open("yourFile.txt","r") as f:
    text = f.readlines()
for line in text:
    print line
  • 읽을 파일을 엽니 다 (r)
  • 전체 파일을 읽고 각 줄을 목록 (텍스트)으로 저장하십시오.
  • 각 줄을 인쇄하는 목록을 반복합니다.

예를 들어, 특정 행을 10보다 큰 길이로 확인하려면 이미 사용 가능한 항목으로 작업하십시오.

for line in text:
    if len(line) > 10:
        print line