Wie liest man eine große Datei Zeile für Zeile in Python?

Ich möchte über jede Zeile einer ganzen Datei iterieren. Eine Möglichkeit, dies zu tun, besteht darin, die gesamte Datei zu lesen, sie in einer Liste zu speichern und dann über die Interessenszeile zu gehen. Diese Methode benötigt viel Speicher, also suche ich nach einer Alternative.

Mein Code bisher:

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) 

Die Ausführung dieses Codes gibt eine Fehlermeldung aus: device active .

Irgendwelche Vorschläge?

Der Zweck besteht darin, paarweise String-Ähnlichkeit zu berechnen, was bedeutet, dass ich für jede Zeile in der Datei die Levenshtein-Distanz mit jeder anderen Linie berechnen möchte.

   

Die korrekte, vollständig pythonische Art, eine Datei zu lesen, ist die folgende:

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

Die with statement behandelt das Öffnen und Schließen der Datei, auch wenn im inneren Block eine Ausnahme ausgetriggers wird. Die for line in f behandelt das Dateiobjekt f als iterabel, das automatisch gepufferte E / A- und Speicherverwaltung verwendet, so dass Sie sich keine Gedanken über große Dateien machen müssen.

Es sollte einen – und vorzugsweise nur einen – offensichtlichen Weg geben, dies zu tun.

Zwei speichereffiziente Wege in geordneter Reihenfolge (zuerst ist am besten) –

  1. Verwendung von with – unterstützt von Python 2.5 und höher
  2. Nutzung der yield wenn Sie wirklich kontrollieren wollen, wie viel gelesen werden soll

1. Verwendung von with

with ist die nette und effiziente pythonische Art große Dateien zu lesen. Vorteile – 1) Dateiobjekt wird automatisch geschlossen, nachdem with Ausführungsblock beendet wurde. 2) Ausnahmebehandlung innerhalb des with Blocks. 3) Speicher for Schleife iteriert zeilenweise durch das f Dateiobjekt. intern werden gepufferte IO (zur Optimierung von kostspieligen IO-Operationen) und Speicherverwaltung durchgeführt.

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

2. Verwendung des yield

Manchmal möchte man eine feinere Kontrolle darüber, wie viel in jeder Iteration gelesen werden soll. In diesem Fall verwende iter & yield . Beachten Sie bei dieser Methode, dass Sie explizit die Datei am Ende schließen müssen.

 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() 

Fallstricke und der Vollständigkeit halber – die folgenden Methoden sind nicht so gut oder nicht so elegant zum Lesen von großen Dateien, aber lesen Sie bitte, um abgerundetes Verständnis zu bekommen.

In Python ist die gebräuchlichste Methode zum Lesen von Zeilen aus einer Datei:

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

Wenn dies durchgeführt wird, lädt die function readlines() (das gleiche gilt für read() function read() ) die gesamte Datei in den Speicher und iteriert dann darüber. Ein etwas besserer Ansatz (die ersten beiden Methoden sind die besten) für große Dateien besteht darin, das fileinput Modul wie folgt zu verwenden:

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

Der Aufruf fileinput.input() liest Zeilen sequentiell, speichert sie aber nicht im Speicher, nachdem sie gelesen wurden oder auch nur so, da die file in Python iterierbar ist.

Verweise

  1. Python mit Aussage

Zeilenumbrüche entfernen:

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

Mit der universellen Newline-Unterstützung werden alle Textdateizeilen scheinbar mit '\n' , unabhängig von den Endungen in der Datei, '\r' , '\n' oder '\r\n' .

BEARBEITEN – Um universelle Newline-Unterstützung anzugeben:

  • Python 2 unter Unix – open(file_path, mode='rU') – erforderlich [danke @Dave ]
  • Python 2 unter Windows – open(file_path, mode='rU') – optional
  • Python 3 – open(file_path, newline=None) – optional

Der newline Parameter wird nur in Python 3 unterstützt und standardmäßig auf None . Der Modusparameter ist in allen Fällen auf 'r' voreingestellt. Das U ist in Python 3 veraltet. In Python 2 unter Windows scheint ein anderer Mechanismus \r\n in \n zu übersetzen.

Dokumente:

  • open () für Python 2
  • open () für Python 3

So konservieren Sie native Terminatoren:

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

Der Binärmodus kann die Datei immer noch in Zeilen mit in analysieren. Jede Zeile hat die in der Datei vorhandenen Terminatoren.

Dank @katrielalex ‘ Antwort , Pythons Open () Doc und iPython Experimente.

Dies ist eine mögliche Art, eine Datei in Python zu lesen:

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

Es weist keine vollständige Liste zu. Es iteriert über die Zeilen.

Irgendein Kontext im Voraus, woher ich komme. Code-Snippets sind am Ende.

Wenn es mir möglich ist, verwende ich lieber ein Open-Source-Tool wie H2O, um super hohe parallele CSV-Datei-Lesevorgänge durchzuführen, aber dieses Tool ist in seinen functionen eingeschränkt. Ich schreibe eine Menge Code, um Data Science-Pipelines zu erstellen, bevor ich den H2O-Cluster für das überwachte Lernen speise.

Ich habe Dateien wie 8GB HIGGS Dataset aus UCI Repo und sogar 40GB CSV-Dateien für Data Science-Zwecke deutlich schneller durch Hinzufügen von viel Parallelität mit Pool-Objekt und Map-function der Multiprocessing-Bibliothek. Zum Beispiel erfordert das Clustering mit Nächsten-Nachbarn-Suchen und auch DBSCAN- und Markov-Cluster-Algorithmen einige parallele Programmier-Feinheiten, um einige ernsthaft herausfordernde Speicher- und Wandzeit-Probleme zu umgehen.

Normalerweise möchte ich die Datei reihenweise in Teile zerlegen, indem ich zuerst gnu-tools und dann glob-filemaske alle benutze, um sie parallel im Python-Programm zu finden und zu lesen. Ich benutze so etwas wie 1000+ Teildateien. Diese Tricks helfen immens bei der Verarbeitungsgeschwindigkeit und den Speichergrenzen.

Die Pandas dataframe.read_csv ist single-threaded, also können Sie diese Tricks machen, um Pandas ziemlich schneller zu machen, indem Sie eine map () für die parallele Ausführung ausführen. Sie können htop verwenden, um zu sehen, dass bei reinen alten sequenziellen Pandas dataframe.read_csv 100% CPU auf nur einem core der eigentliche Engpass in pd.read_csv ist, nicht die Festplatte überhaupt.

Ich sollte hinzufügen, ich verwende eine SSD auf schnellen Grafikkarten-Bus, keine drehende HD auf SATA6-Bus, plus 16 CPU-coreen.

Eine andere Technik, die ich bei einigen Anwendungen entdeckt habe, ist eine parallele CSV-Datei, die alle in einer riesigen Datei liest und jeden Worker mit unterschiedlichem Offset in die Datei startet, anstatt eine große Datei in viele Teile zu teilen. Verwenden Sie die Datei seek () und tell () von python in jedem parallelen Worker, um die große Textdatei in Streifen an verschiedenen Byte-Offset-Startbyte- und Endbyteorten in der großen Datei gleichzeitig alle gleichzeitig zu lesen. Sie können ein Regex-Findall für die Bytes ausführen und die Anzahl der Zeilenumbrüche zurückgeben. Dies ist eine Teilsumme. Schließlich summieren Sie die Partialsummen, um die globale Summe zu erhalten, wenn die Map-function nach Beendigung der Worker zurückkehrt.

Im Folgenden finden Sie einige Beispiel-Benchmarks, die den parallelen Byte-Offset-Trick verwenden:

Ich benutze 2 Dateien: HIGGS.csv ist 8 GB. Es stammt vom UCI Machine Learning Repository. all_bin .csv ist 40,4 GB und stammt aus meinem aktuellen Projekt. Ich benutze 2 Programme: GNU wc-Programm, das mit Linux kommt, und das reine python-Programm fastread.py, das ich entwickelt habe.

 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 

Das ist eine Geschwindigkeit von 4,5 GB / s oder 45 Gb / s. Das ist keine drehende Festplatte, mein Freund. Das ist eigentlich eine Samsung Pro 950 SSD.

Unten ist der Geschwindigkeitsbenchmark für dieselbe Datei, die von gnu wc, einem reinen C-kompilierten Programm, gezählt wird.

Was ist cool, Sie können sehen, mein reines Python-Programm im Wesentlichen die Geschwindigkeit des gnu wc kompiliert C-Programm in diesem Fall. Python wird interpretiert, aber C ist kompiliert, also ist das eine ziemlich interessante performance der Geschwindigkeit, ich denke, Sie würden zustimmen. Natürlich muss wc wirklich in ein paralleles Programm geändert werden, und dann würde es wirklich die Socken aus meinem Python-Programm schlagen. Aber wie es heute aussieht, Gnu WC ist nur ein sequenzielles Programm. Sie tun, was Sie können, und Python kann heute parallel arbeiten. Cython-Kompilierung könnte mir vielleicht (für eine andere Zeit) helfen. Auch Memory-Mapped-Dateien wurden noch nicht untersucht.

 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 

Fazit: Die Geschwindigkeit ist für ein reines Python-Programm im Vergleich zu einem C-Programm gut. Es ist jedoch nicht gut genug, das reine Python-Programm zumindest für den Zweck der Zeilenzählung über das C-Programm zu verwenden. Im Allgemeinen kann die Technik für andere Dateiverarbeitung verwendet werden, so dass dieser Python-Code immer noch gut ist.

Frage: Wird die Regex nur einmal zusammengesetzt und an alle Arbeiter weitergegeben, um die Geschwindigkeit zu verbessern? Antwort: Regex-Vorkompilieren hilft NICHT bei dieser Anwendung. Ich nehme an, der Grund ist, dass der Overhead der Serialisierung und Erstellung von processen für alle Arbeiter dominiert.

Eine Sache noch. Hilft das parallele Lesen von CSV-Dateien? Ist die Festplatte der Engpass oder ist es die CPU? Viele so genannte Top-Rated-Antworten auf Stackoverflow enthalten die allgemeine Dev-Weisheit, dass Sie nur einen Thread benötigen, um eine Datei zu lesen, was Sie am besten können, sagen sie. Sind sie sicher?

Lass es uns herausfinden:

 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 

Oh ja, ja, tut es. Paralleles Lesen von Dateien funktioniert sehr gut. Na siehst du!

Ps. Falls einige von Ihnen wissen wollten, was wäre, wenn der balanceFactor bei Verwendung eines einzigen Arbeitsprozesses 2 wäre? Nun, es ist schrecklich:

 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 

Wichtige Teile des Python-Programms 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 

Das Def für PartitionDataToWorkers ist nur gewöhnlicher sequentieller Code. Ich habe es weggelassen, für den Fall, dass jemand anderes üben möchte, wie paralleles Programmieren ist. Ich gab die härteren Teile kostenlos frei: den getesteten und funktionierenden Parallelcode, zu deinem Lernerfolg.

Dank: Das Open-Source-H2O-Projekt von Arno und Cliff und den H2O-Mitarbeitern für ihre großartigen Software- und Lehrvideos, die mir die Inspiration für diesen reinen Python-Hochleistungs-Parallel-Byte-Offset-Leser gegeben haben, wie oben gezeigt. H2O liest parallel mit java, liest Python und R-Programme und ist schnell, schneller als alles andere auf dem Planeten, wenn es große CSV-Dateien liest.

Katrielalex bot die Möglichkeit, eine Datei zu öffnen und zu lesen.

Wie auch immer Ihr Algorithmus vorgeht, liest die ganze Datei für jede Zeile der Datei. Das bedeutet, dass die Gesamtmenge des Lesens einer Datei – und Berechnen der Levenshtein-Distanz – N * N ist, wenn N die Anzahl der Zeilen in der Datei ist. Da Sie sich Sorgen über die Dateigröße machen und sie nicht im Speicher behalten möchten, mache ich mir Sorgen über die resultierende quadratische Laufzeit . Ihr Algorithmus ist in der O (n ^ 2) class von Algorithmen, die oft mit Spezialisierung verbessert werden kann.

Ich vermute, dass Sie hier bereits den Kompromiss zwischen Speicher und Laufzeit kennen, aber vielleicht möchten Sie untersuchen, ob es eine effiziente Möglichkeit gibt, mehrere Levenshtein-Abstände parallel zu berechnen. Dann wäre es interessant, hier Ihre Lösung zu teilen.

Wie viele Zeilen haben Ihre Dateien und auf welcher Art von Maschine (mem & cpu power) muss Ihr Algorithmus laufen und was ist die tolerierte Laufzeit?

Code würde wie folgt aussehen:

 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) 

Aber die Fragen sind, wie speichern Sie die Entfernungen (Matrix?) Und können Sie einen Vorteil der Vorbereitung der äußeren Linie für die Verarbeitung oder Caching einige Zwischenergebnisse für die Wiederverwendung gewinnen.

 #Using a text file for the example with open("yourFile.txt","r") as f: text = f.readlines() for line in text: print line 
  • Öffne deine Datei zum Lesen (r)
  • Lesen Sie die gesamte Datei und speichern Sie jede Zeile in einer Liste (Text)
  • Durchlaufen Sie die Liste, indem Sie jede Zeile drucken.

Wenn Sie beispielsweise eine bestimmte Zeile für eine Länge größer als 10 überprüfen möchten, arbeiten Sie mit dem, was Sie bereits zur Verfügung haben.

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

Aus der Python-Dokumentation für fileinput .input ():

Dies iteriert die Zeilen aller Dateien, die in sys.argv[1:] sind, wobei sys.stdin wenn die Liste leer ist

Weiterhin ist die Definition der function:

 fileinput.FileInput([files[, inplace[, backup[, mode[, openhook]]]]]) 

Lesen zwischen den Zeilen, dies sagt mir, dass files eine Liste sein können, so dass Sie etwas haben könnten wie:

 for each_line in fileinput.input([input_file, input_file]): do_something(each_line) 

Sehen Sie hier für weitere Informationen

Ich würde dringend empfehlen, das Laden der Standarddatei nicht zu verwenden, da es horrend langsam ist. Sie sollten sich die functionen und die IOpro-functionen ansehen (zB numpy.loadtxt ()).

http://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html

https://store.continuum.io/cshop/iopro/

Dann können Sie Ihre paarweise Operation in Blöcke aufteilen:

 import numpy as np import math lines_total = n similarity = np.zeros(n,n) lines_per_chunk = m n_chunks = math.ceil(float(n)/m) for i in xrange(n_chunks): for j in xrange(n_chunks): chunk_i = (function of your choice to read lines i*lines_per_chunk to (i+1)*lines_per_chunk) chunk_j = (function of your choice to read lines j*lines_per_chunk to (j+1)*lines_per_chunk) similarity[i*lines_per_chunk:(i+1)*lines_per_chunk, j*lines_per_chunk:(j+1)*lines_per_chunk] = fast_operation(chunk_i, chunk_j) 

Es ist fast immer viel schneller, Daten in Chunks zu laden und dann Matrixoperationen darauf durchzuführen, als dies Element für Element zu tun !!

Der beste Weg, um eine große Datei Zeile für Zeile zu lesen, ist die Python- Enumerate- function

 with open(file_name, "rU") as read_file: for i, row in enumerate(read_file, 1): #do something #i in line of that line #row containts all data of that line