NumPy Lesedatei mit Filterlinien auf der Fliege

Ich habe eine große Anzahl von Zahlen in einer CSV-Datei geschrieben und müssen nur eine Scheibe dieses Arrays laden. Konzeptionell möchte ich np.genfromtxt() anrufen und dann das resultierende Array np.genfromtxt()

  1. Die Datei ist so groß, dass sie nicht in RAM passen kann
  2. Die Anzahl der relevanten Zeilen könnte klein sein, also gibt es keine Notwendigkeit, jede Zeile zu analysieren.

MATLAB hat die Funktion textscan() , die einen Dateideskriptor nehmen kann und nur einen Teil der Datei lesen kann. Gibt es so etwas in NumPy?

Für jetzt habe ich die folgende Funktion definiert, die nur die Zeilen liest, die die gegebene Bedingung erfüllen:

 def genfromtxt_cond(fname, cond=(lambda str: True)): res = [] with open(fname) as file: for line in file: if cond(line): res.append([float(s) for s in line.split()]) return np.array(res, dtype=np.float64) 

Es gibt mehrere Probleme mit dieser Lösung:

  • Nicht allgemein: unterstützt nur den float-Typ, während genfromtxt die Typen erkennt, die von Spalte zu Spalte variieren können; Auch fehlende Werte, Konverter, Skipping, etc .;
  • Nicht effizient: Wenn die Bedingung schwierig ist, kann jede Zeile zweimal analysiert werden, auch die verwendete Datenstruktur und Lesepufferung können suboptimal sein;
  • Erfordert Schreibcode.

Gibt es eine Standardfunktion, die das Filtern implementiert, oder ein Gegenstück zu den textscan von MATLAB?

  • Ist es möglich, eine diskontinuierliche Daten auf der Festplatte zu einem Array mit Python abzubilden?
  • Wie schön zu behandeln [: -0] schneiden?
  • Numpy: 1D Array mit verschiedenen Form
  • Anpassen einer Linie in 3D
  • Nicht-NDFFrame-Objektfehler mit der Funktion pandas.SparseSeries.from_coo ()
  • Zuweisen von komplexen Werten zu knalligen Arrays?
  • Extrahieren Sie csv-Dateispezifische Spalten, um in Python aufzulisten
  • Unterschied zwischen a - = b und a = a - b in Python
  • 3 Solutions collect form web for “NumPy Lesedatei mit Filterlinien auf der Fliege”

    Ich kann an zwei Ansätze denken, die einige der Funktionalitäten bieten, die du verlangst:

    1. Um eine Datei entweder in chunks / oder in schritten von n-lines / etc zu lesen:
      Sie können einen generator an numpy.genfromtxt sowie an numpy.loadtxt übergeben . Auf diese Weise können Sie einen großen Datensatz aus einer Textdatei speichern – effizient unter Beibehaltung aller bequemen Parsing-Funktionen der beiden Funktionen.

    2. Um Daten nur aus Zeilen zu lesen, die mit einem Kriterium übereinstimmen, das als Regex ausgedrückt werden kann:
      Sie können numpy.fromregex verwenden und einen regular expression um genau zu definieren, welche Token aus einer gegebenen Zeile in der Eingabedatei geladen werden sollen. Zeilen, die nicht dem Muster entsprechen, werden ignoriert.

    Um die beiden Ansätze zu illustrieren, werde ich aus meinem Forschungskontext ein Beispiel verwenden.
    Ich muss oft Dateien mit der folgenden Struktur laden:

     6 generated by VMD CM 5.420501 3.880814 6.988216 HM1 5.645992 2.839786 7.044024 HM2 5.707437 4.336298 7.926170 HM3 4.279596 4.059821 7.029471 OD1 3.587806 6.069084 8.018103 OD2 4.504519 4.977242 9.709150 6 generated by VMD CM 5.421396 3.878586 6.989128 HM1 5.639769 2.841884 7.045364 HM2 5.707584 4.343513 7.928119 HM3 4.277448 4.057222 7.022429 OD1 3.588119 6.069086 8.017814 

    Diese Dateien können riesig sein (GBs) und ich interessiere mich nur für die numerischen Daten. Alle Datenblöcke haben in diesem Beispiel die gleiche Größe – 6 und sie sind immer durch zwei Zeilen getrennt. So ist der stride der Blöcke 8 .

    Mit dem ersten Ansatz:

    Zuerst werde ich einen Generator definieren, der die unerwünschten Zeilen herausfiltert:

     def filter_lines(f, stride): for i, line in enumerate(f): if i%stride and (i-1)%stride: yield line 

    Dann öffne ich die Datei, schaffe einen filter_lines -generator (hier muss ich den stride wissen) und genfromtxt diesen generator zu genfromtxt :

     with open(fname) as f: data = np.genfromtxt(filter_lines(f, 8), dtype='f', usecols=(1, 2, 3)) 

    Das funktioniert wie eine Brise. Beachten Sie, dass ich in der Lage bin, usecols zu verwenden, um die erste Spalte der Daten loszuwerden. In der gleichen Weise können Sie alle anderen Merkmale von genfromtxt – Erkennung der Typen, variierende Typen von Spalte zu Spalte, fehlende Werte, Konverter, etc.

    In diesem Beispiel war data.shape (204000, 3) während die Originaldatei aus 272000 Zeilen bestand.

    Hier wird der generator verwendet, um homogen gestrichelte Linien zu filtern, aber man kann sich auch vorstellen, dass er inhomogene Linienblöcke nach (einfachen) Kriterien herausfiltert.

    Mit dem zweiten Ansatz:

    Hier ist die regexp ich verwenden werde:

     regexp = r'\s+\w+' + r'\s+([-.0-9]+)' * 3 + r'\s*\n' 

    Gruppen – dh innen () – definieren die zu entnehmenden Token aus einer bestimmten Zeile. Als fromregex macht fromregex den Job und ignoriert Linien, die nicht dem Muster entsprechen:

     data = np.fromregex(fname, regexp, dtype='f') 

    Das Ergebnis ist genau das gleiche wie im ersten Ansatz.

    Wenn Sie eine Liste von Typen (die Formatbedingung) übergeben, verwenden Sie einen try-Block und verwenden Sie Ausbeute, um genfromtxt als Generator zu verwenden, wir sollten in der Lage sein, textscan() zu replizieren.

     def genfromtext(fname, formatTypes): with open(fname, 'r') as file: for line in file: try: line = line.split(',') # Do you care about line anymore? r = [] for type, cell in zip(formatTypes, line): r.append(type(cell)) except: pass # Fail silently on this line since we hit an error yield r 

    Bearbeiten: Ich habe den Ausnahmeblock vergessen. Es läuft jetzt gut und du kannst genfromtext als Generator wie so verwenden (mit einem zufälligen CSV-Log habe ich sitzen):

     >>> a = genfromtext('log.txt', [str, str, str, int]) >>> a.next() ['10.10.9.45', ' 2013/01/17 16:29:26', '00:00:36', 0] >>> a.next() ['10.10.9.45', ' 2013/01/17 16:22:20', '00:08:14', 0] >>> a.next() ['10.10.9.45', ' 2013/01/17 16:31:05', '00:00:11', 3] 

    Ich sollte wohl bemerken, dass ich mit dem Zip die Komma-Split-Line und das FormatSpec, das die beiden Listen tuplifizieren wird, stoppt (Stoppen, wenn eine der Listen aus Items herauskommt), so dass wir sie über sie meistern können Auf len(line) oder so ähnlich.

    Versuche, Kommentar zu OP zu demonstrieren.

     def fread(name, cond): with open(name) as file: for line in file: if cond(line): yield line.split() def a_genfromtxt_cond(fname, cond=(lambda str: True)): """Seems to work without need to convert to float.""" return np.array(list(fread(fname, cond)), dtype=np.float64) def b_genfromtxt_cond(fname, cond=(lambda str: True)): r = [[int(float(i)) for i in l] for l in fread(fname, cond)] return np.array(r, dtype=np.integer) a = a_genfromtxt_cond("tar.data") print a aa = b_genfromtxt_cond("tar.data") print aa 

    Ausgabe

     [[ 1. 2.3 4.5] [ 4.7 9.2 6.7] [ 4.7 1.8 4.3]] [[1 2 4] [4 9 6] [4 1 4]] 
    Python ist die beste Programmiersprache der Welt.