Arbeitsabläufe mit großen Datenmengen unter Verwendung von Pandas

981

Ich habe viele Monate lang versucht, eine Antwort auf diese Frage zu finden, während ich Pandas lernte. Ich benutze SAS für meine tägliche Arbeit und es ist großartig für die Unterstützung außerhalb des Kerns. SAS ist jedoch aus zahlreichen anderen Gründen als Software schrecklich.

Eines Tages hoffe ich, meine Verwendung von SAS durch Python und Pandas zu ersetzen, aber mir fehlt derzeit ein Out-of-Core-Workflow für große Datenmengen. Ich spreche nicht von "Big Data", für das ein verteiltes Netzwerk erforderlich ist, sondern von Dateien, die zu groß sind, um in den Speicher zu passen, aber klein genug, um auf eine Festplatte zu passen.

Mein erster Gedanke ist, HDFStoregroße Datenmengen auf der Festplatte zu speichern und nur die Teile, die ich zur Analyse benötige, in Datenrahmen zu ziehen. Andere haben MongoDB als einfachere Alternative erwähnt. Meine Frage lautet:

Was sind einige Best-Practice-Workflows, um Folgendes zu erreichen:

  1. Laden von Flatfiles in eine permanente Datenbankstruktur auf der Festplatte
  2. Abfragen dieser Datenbank, um Daten abzurufen, die in eine Pandas-Datenstruktur eingespeist werden sollen
  3. Aktualisieren der Datenbank nach dem Manipulieren von Stücken in Pandas

Beispiele aus der Praxis wären sehr willkommen, insbesondere von allen, die Pandas für "große Datenmengen" verwenden.

Bearbeiten - ein Beispiel dafür, wie dies funktionieren soll:

  1. Importieren Sie iterativ eine große Flat-Datei und speichern Sie sie in einer permanenten Datenbankstruktur auf der Festplatte. Diese Dateien sind normalerweise zu groß, um in den Speicher zu passen.
  2. Um Pandas verwenden zu können, möchte ich Teilmengen dieser Daten lesen (normalerweise nur wenige Spalten gleichzeitig), die in den Speicher passen.
  3. Ich würde neue Spalten erstellen, indem ich verschiedene Operationen an den ausgewählten Spalten ausführe.
  4. Ich müsste dann diese neuen Spalten an die Datenbankstruktur anhängen.

Ich versuche, eine bewährte Methode zur Durchführung dieser Schritte zu finden. Beim Lesen von Links zu Pandas und Pytables scheint das Anhängen einer neuen Spalte ein Problem zu sein.

Bearbeiten - Auf Jeffs Fragen speziell antworten:

  1. Ich baue Konsumentenkreditrisikomodelle. Die Arten von Daten umfassen Telefon-, SSN- und Adressmerkmale; Eigenschaftswerte; abfällige Informationen wie Strafregister, Insolvenzen usw. Die Datensätze, die ich täglich verwende, enthalten im Durchschnitt fast 1.000 bis 2.000 Felder gemischter Datentypen: kontinuierliche, nominale und ordinale Variablen sowohl für numerische als auch für Zeichendaten. Ich füge selten Zeilen hinzu, aber ich führe viele Operationen aus, die neue Spalten erstellen.
  2. Typische Operationen umfassen das Kombinieren mehrerer Spalten unter Verwendung der bedingten Logik zu einer neuen zusammengesetzten Spalte. Zum Beispiel if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B'. Das Ergebnis dieser Operationen ist eine neue Spalte für jeden Datensatz in meinem Datensatz.
  3. Abschließend möchte ich diese neuen Spalten an die Datenstruktur auf der Festplatte anhängen. Ich würde Schritt 2 wiederholen und die Daten mit Kreuztabellen und beschreibenden Statistiken untersuchen, um interessante, intuitive Beziehungen zum Modell zu finden.
  4. Eine typische Projektdatei ist normalerweise ungefähr 1 GB groß. Dateien sind so organisiert, dass eine Zeile aus einer Aufzeichnung von Verbraucherdaten besteht. Jede Zeile hat für jeden Datensatz die gleiche Anzahl von Spalten. Dies wird immer der Fall sein.
  5. Es ist ziemlich selten, dass ich beim Erstellen einer neuen Spalte nach Zeilen unterteilen würde. Es ist jedoch ziemlich üblich, dass ich beim Erstellen von Berichten oder beim Beschreiben von beschreibenden Statistiken eine Teilmenge von Zeilen unterteile. Zum Beispiel möchte ich möglicherweise eine einfache Frequenz für einen bestimmten Geschäftsbereich erstellen, z. B. Kreditkarten für Privatkunden. Zu diesem Zweck würde ich nur die Datensätze auswählen, in denen der Geschäftsbereich = Einzelhandel ist, zusätzlich zu den Spalten, über die ich berichten möchte. Beim Erstellen neuer Spalten würde ich jedoch alle Datenzeilen und nur die Spalten abrufen, die ich für die Operationen benötige.
  6. Der Modellierungsprozess erfordert, dass ich jede Spalte analysiere, nach interessanten Beziehungen mit einer Ergebnisvariablen suche und neue zusammengesetzte Spalten erstelle, die diese Beziehungen beschreiben. Die Spalten, die ich untersuche, werden normalerweise in kleinen Mengen erstellt. Zum Beispiel werde ich mich auf eine Reihe von beispielsweise 20 Spalten konzentrieren, die sich nur mit Immobilienwerten befassen, und beobachten, wie sie sich auf den Ausfall eines Kredits beziehen. Sobald diese untersucht und neue Spalten erstellt wurden, gehe ich zu einer anderen Gruppe von Spalten über, z. B. College-Ausbildung, und wiederhole den Vorgang. Ich erstelle Kandidatenvariablen, die die Beziehung zwischen meinen Daten und einem bestimmten Ergebnis erklären. Ganz am Ende dieses Prozesses wende ich einige Lerntechniken an, die aus diesen zusammengesetzten Spalten eine Gleichung erstellen.

Es ist selten, dass ich dem Datensatz jemals Zeilen hinzufügen würde. Ich werde fast immer neue Spalten erstellen (Variablen oder Funktionen in der Statistik / maschinellen Lernsprache).

Zelazny7
quelle
1
Ist das Verhältnis Kerngröße / volle Größe 1%, 10%? Ist es wichtig - wenn Sie Spalten auf int8 komprimieren oder verrauschte Zeilen herausfiltern könnten, würde dies Ihre Compute-Think-Schleife von beispielsweise Stunden auf Minuten ändern? (Fügen Sie auch Tag große Datenmengen hinzu.)
Denis
1
Das Speichern von float32 anstelle von float64 und int8, wo möglich, sollte trivial sein (ich weiß jedoch nicht, welche Tools / Funktionen float64 intern ausführen)
denis
Können Sie Ihre Aufgabe in Arbeitsblöcke aufteilen?
Andrew Scott Evans
1
Eine schöne Lösung für 2019, um Pandas wie Operationen an "mittleren" Daten durchzuführen, die nicht in den Speicher passen, ist dask
lunguini
Es gibt Alternativen zu Python + Pandas, die Sie vielleicht in Betracht ziehen sollten, wenn Sie gerade erst anfangen. Bedenken Sie, dass Python eine universelle Programmiersprache ist (kein DSL für Datenmunging und -analyse) und dass Pandas eine Bibliothek ist, die darüber hinaus in Angriff genommen wird. Ich würde mir R oder kdb ansehen.
Henry Henrinson

Antworten:

621

Ich verwende routinemäßig Dutzende von Gigabyte Daten auf diese Weise, z. B. habe ich Tabellen auf der Festplatte, die ich über Abfragen lese, Daten erstelle und anhänge.

Es lohnt sich, die Dokumente und spät in diesem Thread zu lesen, um einige Vorschläge zum Speichern Ihrer Daten zu erhalten.

Details, die sich auf die Speicherung Ihrer Daten auswirken, z. B.:
Geben Sie so viele Details wie möglich an. und ich kann Ihnen helfen, eine Struktur zu entwickeln.

  1. Datengröße, Anzahl der Zeilen, Spalten, Spaltentypen; Fügen Sie Zeilen oder nur Spalten hinzu?
  2. Wie werden typische Operationen aussehen? Führen Sie beispielsweise eine Abfrage für Spalten durch, um eine Reihe von Zeilen und bestimmten Spalten auszuwählen, und führen Sie dann eine Operation (im Speicher) aus, erstellen Sie neue Spalten und speichern Sie diese.
    (Wenn wir ein Spielzeugbeispiel geben, können wir spezifischere Empfehlungen abgeben.)
  3. Was machen Sie nach dieser Verarbeitung? Ist Schritt 2 ad hoc oder wiederholbar?
  4. Eingabe von Flatfiles: Wie viele grobe Gesamtgrößen in GB? Wie sind diese zB nach Aufzeichnungen organisiert? Enthält jedes Feld unterschiedliche Felder oder haben sie einige Datensätze pro Datei mit allen Feldern in jeder Datei?
  5. Wählen Sie jemals Teilmengen von Zeilen (Datensätzen) anhand von Kriterien aus (z. B. wählen Sie die Zeilen mit Feld A> 5 aus)? und dann etwas tun, oder wählen Sie einfach die Felder A, B, C mit allen Datensätzen aus (und dann etwas tun)?
  6. Arbeiten Sie an all Ihren Spalten (in Gruppen) oder gibt es einen guten Anteil, den Sie möglicherweise nur für Berichte verwenden (z. B. möchten Sie die Daten beibehalten, müssen diese Spaltenexplizität jedoch erst dann einholen Endergebniszeit)?

Lösung

Stellen Sie sicher, dass Sie mindestens Pandas0.10.1 installiert haben.

Lesen Sie iterierende Dateien Stück für Stück und mehrere Tabellenabfragen .

Da pytables für den zeilenweisen Betrieb optimiert ist (was Sie abfragen), erstellen wir für jede Gruppe von Feldern eine Tabelle. Auf diese Weise ist es einfach, eine kleine Gruppe von Feldern auszuwählen (was mit einer großen Tabelle funktioniert, aber es ist effizienter, dies auf diese Weise zu tun ... Ich denke, ich kann diese Einschränkung möglicherweise in Zukunft beheben ... das ist es ohnehin intuitiver):
(Das Folgende ist Pseudocode.)

import numpy as np
import pandas as pd

# create a store
store = pd.HDFStore('mystore.h5')

# this is the key to your storage:
#    this maps your fields to a specific group, and defines 
#    what you want to have as data_columns.
#    you might want to create a nice class wrapping this
#    (as you will want to have this map and its inversion)  
group_map = dict(
    A = dict(fields = ['field_1','field_2',.....], dc = ['field_1',....,'field_5']),
    B = dict(fields = ['field_10',......        ], dc = ['field_10']),
    .....
    REPORTING_ONLY = dict(fields = ['field_1000','field_1001',...], dc = []),

)

group_map_inverted = dict()
for g, v in group_map.items():
    group_map_inverted.update(dict([ (f,g) for f in v['fields'] ]))

Lesen Sie die Dateien ein und erstellen Sie den Speicher (im Wesentlichen das, was Sie tun append_to_multiple):

for f in files:
   # read in the file, additional options may be necessary here
   # the chunksize is not strictly necessary, you may be able to slurp each 
   # file into memory in which case just eliminate this part of the loop 
   # (you can also change chunksize if necessary)
   for chunk in pd.read_table(f, chunksize=50000):
       # we are going to append to each table by group
       # we are not going to create indexes at this time
       # but we *ARE* going to create (some) data_columns

       # figure out the field groupings
       for g, v in group_map.items():
             # create the frame for this group
             frame = chunk.reindex(columns = v['fields'], copy = False)    

             # append it
             store.append(g, frame, index=False, data_columns = v['dc'])

Jetzt haben Sie alle Tabellen in der Datei (tatsächlich könnten Sie sie in separaten Dateien speichern, wenn Sie möchten, müssten Sie wahrscheinlich den Dateinamen zur group_map hinzufügen, aber wahrscheinlich ist dies nicht erforderlich).

So erhalten Sie Spalten und erstellen neue:

frame = store.select(group_that_I_want)
# you can optionally specify:
# columns = a list of the columns IN THAT GROUP (if you wanted to
#     select only say 3 out of the 20 columns in this sub-table)
# and a where clause if you want a subset of the rows

# do calculations on this frame
new_frame = cool_function_on_frame(frame)

# to 'add columns', create a new group (you probably want to
# limit the columns in this new_group to be only NEW ones
# (e.g. so you don't overlap from the other tables)
# add this info to the group_map
store.append(new_group, new_frame.reindex(columns = new_columns_created, copy = False), data_columns = new_columns_created)

Wenn Sie für die Nachbearbeitung bereit sind:

# This may be a bit tricky; and depends what you are actually doing.
# I may need to modify this function to be a bit more general:
report_data = store.select_as_multiple([groups_1,groups_2,.....], where =['field_1>0', 'field_1000=foo'], selector = group_1)

Über data_columns, brauchen Sie nicht wirklich definieren ANY data_columns; Mit ihnen können Sie Zeilen basierend auf der Spalte unterwählen. ZB so etwas wie:

store.select(group, where = ['field_1000=foo', 'field_1001>0'])

Sie können für Sie in der Phase der Erstellung des Abschlussberichts am interessantesten sein (im Wesentlichen wird eine Datenspalte von anderen Spalten getrennt, was die Effizienz etwas beeinträchtigen kann, wenn Sie viel definieren).

Vielleicht möchten Sie auch:

  • Erstellen Sie eine Funktion, die eine Liste von Feldern verwendet, die Gruppen in der groups_map nachschlägt, diese dann auswählt und die Ergebnisse verkettet, sodass Sie den resultierenden Frame erhalten (dies ist im Wesentlichen das, was select_as_multiple tut). Auf diese Weise wäre die Struktur für Sie ziemlich transparent.
  • Indizes für bestimmte Datenspalten (beschleunigt die Teilmenge von Zeilen erheblich).
  • Komprimierung aktivieren.

Lassen Sie mich wissen, wenn Sie Fragen haben!

Jeff
quelle
5
Danke für die Links. Der zweite Link macht mir ein bisschen Sorgen, dass ich keine neuen Spalten an die Tabellen in HDFStore anhängen kann? Ist das korrekt? Außerdem habe ich ein Beispiel hinzugefügt, wie ich dieses Setup verwenden würde.
Zelazny7
4
Die eigentliche Struktur im HDF liegt bei Ihnen. Pytables ist zeilenorientiert und hat zum Zeitpunkt der Erstellung feste Spalten. Sie können keine Spalten anhängen, sobald eine Tabelle erstellt wurde. Sie können jedoch eine neue Tabelle erstellen, die genauso indiziert ist wie Ihre vorhandene Tabelle. (Siehe die Beispiele select_as_multiple in den Dokumenten). Auf diese Weise können Sie Objekte beliebiger Größe erstellen und gleichzeitig recht effiziente Abfragen durchführen. Die Art und Weise, wie Sie die Daten verwenden, ist der Schlüssel für die Organisation auf der Festplatte. Senden Sie mir eine Off-List-E-Mail mit Pseudocode eines genaueren Beispiels.
Jeff
1
Ich habe meine Frage aktualisiert, um auf Ihre detaillierten Punkte zu antworten. Ich werde an einem Beispiel arbeiten, um Sie von der Liste zu streichen. Vielen Dank!
Zelazny7
12
@ Jeff, mit Pandas auf 0.17.x wurden die oben beschriebenen Probleme jetzt in Pandas behoben?
Strg-Alt-Löschen
5
@ Jeff möchte gerne ein schnelles Update zu Ihrer Antwort hinzufügen, um für dask zu werben?
Boud
137

Ich denke, den obigen Antworten fehlt ein einfacher Ansatz, den ich sehr nützlich gefunden habe.

Wenn ich eine Datei habe, die zu groß ist, um sie in den Speicher zu laden, teile ich die Datei in mehrere kleinere Dateien auf (entweder nach Zeilen oder Spalten).

Beispiel: Bei Handelsdaten im Wert von 30 Tagen mit einer Größe von ~ 30 GB zerlege ich sie in eine Datei pro Tag mit einer Größe von ~ 1 GB. Anschließend verarbeite ich jede Datei einzeln und aggregiere die Ergebnisse am Ende

Einer der größten Vorteile besteht darin, dass die Dateien parallel verarbeitet werden können (entweder mehrere Threads oder Prozesse).

Der andere Vorteil ist, dass die Dateimanipulation (wie das Hinzufügen / Entfernen von Datumsangaben im Beispiel) durch reguläre Shell-Befehle erfolgen kann, was in fortgeschritteneren / komplizierteren Dateiformaten nicht möglich ist

Dieser Ansatz deckt nicht alle Szenarien ab, ist jedoch in vielen von ihnen sehr nützlich

user1827356
quelle
39
Einverstanden. Bei all dem Hype kann man leicht vergessen, dass Befehlszeilentools 235x schneller sein können als ein Hadoop-Cluster
zelusp
83

Zwei Jahre nach der Frage gibt es jetzt ein "out-of-core" -Pandas- Äquivalent: dask . Es ist ausgezeichnet! Obwohl es nicht alle Pandas-Funktionen unterstützt, können Sie damit wirklich weit kommen.

Privat
quelle
6
und für ein vollständig ausgearbeitetes Beispiel mit dask, schauen Sie
einfach
Abhängig von Ihren Daten ist es sinnvoll, einen Blick in den Pystore zu werfen . Es beruht auf dask.
gies0r
66

Wenn Ihre Datensätze zwischen 1 und 20 GB liegen, sollten Sie eine Workstation mit 48 GB RAM erwerben. Dann können Pandas den gesamten Datensatz im RAM speichern. Ich weiß, dass dies nicht die Antwort ist, nach der Sie hier suchen, aber wissenschaftliches Rechnen auf einem Notebook mit 4 GB RAM ist nicht sinnvoll.

rjurney
quelle
7
"Wissenschaftliches Rechnen auf einem Notebook mit 4 GB RAM ist nicht sinnvoll" Definieren Sie vernünftig. Ich denke, UNIVAC würde eine andere Ansicht vertreten. arstechnica.com/tech-policy/2011/09/…
Grisaitis
2
Einverstanden! Versuchen Sie, im Speicher weiterzuarbeiten, auch wenn dies im Voraus $ $ kostet. Wenn Ihre Arbeit zu einer finanziellen Rendite führt, werden Sie im Laufe der Zeit die Kosten durch Ihre gesteigerte Effizienz wieder hereinholen.
Ansonw
2
Wissenschaftliches Rechnen auf einer Workstation mit 48 GB RAM ist nicht sinnvoll.
Jaroslaw Nikitenko
4
@YaroslavNikitenko Ein r4.2xlarge mit 61 GB / RAM kostet $ .532 / Stunde. Was für ein wissenschaftliches Computing machst du, das nicht so wertvoll ist? Klingt ungewöhnlich, wenn nicht unvernünftig.
rjurney
4
@rjurney sorry, vielleicht hätte ich meinen Kommentar löschen sollen. Ihr Urteil über "unvernünftige" wissenschaftliche Computer scheint sehr subjektiv. Ich mache meine wissenschaftlichen Berechnungen jahrelang auf Laptops, und das scheint mir genug zu sein, weil ich die meiste Zeit Code schreibe. Meine Algorithmen sind aus programmtechnischer Sicht viel schwieriger als aus rechnerischer Sicht. Ich bin mir auch ziemlich sicher, dass man sich beim Schreiben skalierbarer Algorithmen nicht auf aktuelle Hardwareeinschränkungen verlassen sollte. Ihr Kommentar zum Computer anderer Leute mag (abgesehen von der Subjektivität) etwas anstößig klingen. Würde es Ihnen etwas ausmachen, diese wenigen Wörter zu löschen?
Jaroslaw Nikitenko
58

Ich weiß, dass dies ein alter Thread ist, aber ich denke, die Flamme Bibliothek ist einen Besuch wert. Es ist für diese Art von Situationen gebaut.

Aus den Dokumenten:

Blaze erweitert die Benutzerfreundlichkeit von NumPy und Pandas auf verteiltes und Out-of-Core-Computing. Blaze bietet eine ähnliche Schnittstelle wie das NumPy ND-Array oder Pandas DataFrame, ordnet diese bekannten Schnittstellen jedoch einer Vielzahl anderer Computer-Engines wie Postgres oder Spark zu.

Bearbeiten: Übrigens wird es von ContinuumIO und Travis Oliphant, Autor von NumPy, unterstützt.

Chishaku
quelle
Eine weitere Bibliothek, die einen Blick wert sein könnte, ist GraphLab Create: Sie verfügt über eine effiziente DataFrame-ähnliche Struktur, die nicht durch die Speicherkapazität begrenzt ist. blog.dato.com/…
wasserdicht
52

Dies ist bei Pymongo der Fall. Ich habe auch Prototypen mit SQL Server, SQLite, HDF, ORM (SQLAlchemy) in Python erstellt. In erster Linie ist Pymongo eine dokumentbasierte Datenbank, daher wäre jede Person ein Dokument ( dictmit Attributen). Viele Menschen bilden eine Sammlung und Sie können viele Sammlungen haben (Menschen, Börse, Einkommen).

pd.dateframe -> pymongo Hinweis: Ich verwende das chunksizeIn read_csv, um es auf 5 bis 10.000 Datensätze zu beschränken (Pymongo lässt den Socket fallen, wenn er größer ist).

aCollection.insert((a[1].to_dict() for a in df.iterrows()))

Abfrage: gt = größer als ...

pd.DataFrame(list(mongoCollection.find({'anAttribute':{'$gt':2887000, '$lt':2889000}})))

.find() Gibt einen Iterator zurück, den ich normalerweise verwende ichunked , um ihn in kleinere Iteratoren zu zerlegen.

Wie wäre es mit einem Join, da ich normalerweise 10 Datenquellen zum Einfügen bekomme:

aJoinDF = pandas.DataFrame(list(mongoCollection.find({'anAttribute':{'$in':Att_Keys}})))

dann (in meinem Fall muss ich manchmal aJoinDFerst weitermachen, bevor es "verschmelzbar" ist.)

df = pandas.merge(df, aJoinDF, on=aKey, how='left')

Anschließend können Sie die neuen Informationen über die unten stehende Aktualisierungsmethode in Ihre Hauptsammlung schreiben. (logische Sammlung gegen physische Datenquellen).

collection.update({primarykey:foo},{key:change})

Bei kleineren Suchvorgängen einfach denormalisieren. Sie haben beispielsweise Code im Dokument und fügen einfach den Feldcode-Text hinzu und führen beim dictErstellen von Dokumenten eine Suche durch.

Jetzt haben Sie einen schönen Datensatz, der auf einer Person basiert. Sie können Ihre Logik für jeden Fall entfesseln und mehr Attribute erstellen. Schließlich können Sie Ihre 3-to-Memory-Max-Schlüsselindikatoren in Pandas einlesen und Pivots / Agg / Daten untersuchen. Dies funktioniert bei mir für 3 Millionen Datensätze mit Zahlen / großem Text / Kategorien / Codes / Floats / ...

Sie können auch die beiden in MongoDB integrierten Methoden verwenden (MapReduce und Aggregat Framework). Weitere Informationen zum Aggregat-Framework finden Sie hier , da es einfacher zu sein scheint als MapReduce und für eine schnelle Aggregatarbeit praktisch ist. Beachten Sie, dass ich meine Felder oder Beziehungen nicht definieren musste und einem Dokument Elemente hinzufügen kann. Beim aktuellen Stand des sich schnell ändernden Numpy-, Pandas- und Python-Toolset hilft mir MongoDB, einfach an die Arbeit zu gehen :)

brian_the_bungler
quelle
Hallo, ich spiele auch mit Ihrem Beispiel herum und stoße beim Versuch, in eine Datenbank einzufügen, auf diesen Fehler : In [96]: test.insert((a[1].to_dict() for a in df.iterrows())) --------------- InvalidDocument: Cannot encode object: 0. Irgendwelche Ideen, was falsch sein könnte? Mein Datenrahmen besteht aus allen int64-D-Typen und ist sehr einfach.
Zelazny7
2
Ja, ich habe das gleiche für einen einfachen DF-Bereich getan und der int64 von numpy scheint Pymongo zu stören. Alle Daten, die ich mit Konvertierungen aus CSV (vs künstlich über range ()) gespielt habe, haben lange Typen und daher keine Probleme. In numpy kann man konvertieren, aber ich sehe das als ablenkend an. Ich muss zugeben, dass die 10.1-Artikel für HDF aufregend aussehen.
Brian_the_bungler
43

Ich habe dies etwas spät entdeckt, arbeite aber mit einem ähnlichen Problem (Hypothekenvorauszahlungsmodelle). Meine Lösung bestand darin, die HDFStore-Ebene von pandas zu überspringen und gerade pytables zu verwenden. Ich speichere jede Spalte als einzelnes HDF5-Array in meiner endgültigen Datei.

Mein grundlegender Workflow besteht darin, zuerst eine CSV-Datei aus der Datenbank abzurufen. Ich gzip es, also ist es nicht so groß. Dann konvertiere ich das in eine zeilenorientierte HDF5-Datei, indem ich es in Python durchlaufe, jede Zeile in einen realen Datentyp konvertiere und in eine HDF5-Datei schreibe. Das dauert einige zehn Minuten, benötigt aber keinen Speicher, da es nur zeilenweise arbeitet. Dann "transponiere" ich die zeilenorientierte HDF5-Datei in eine spaltenorientierte HDF5-Datei.

Die Transponierung der Tabelle sieht folgendermaßen aus:

def transpose_table(h_in, table_path, h_out, group_name="data", group_path="/"):
    # Get a reference to the input data.
    tb = h_in.getNode(table_path)
    # Create the output group to hold the columns.
    grp = h_out.createGroup(group_path, group_name, filters=tables.Filters(complevel=1))
    for col_name in tb.colnames:
        logger.debug("Processing %s", col_name)
        # Get the data.
        col_data = tb.col(col_name)
        # Create the output array.
        arr = h_out.createCArray(grp,
                                 col_name,
                                 tables.Atom.from_dtype(col_data.dtype),
                                 col_data.shape)
        # Store the data.
        arr[:] = col_data
    h_out.flush()

Das Zurücklesen sieht dann so aus:

def read_hdf5(hdf5_path, group_path="/data", columns=None):
    """Read a transposed data set from a HDF5 file."""
    if isinstance(hdf5_path, tables.file.File):
        hf = hdf5_path
    else:
        hf = tables.openFile(hdf5_path)

    grp = hf.getNode(group_path)
    if columns is None:
        data = [(child.name, child[:]) for child in grp]
    else:
        data = [(child.name, child[:]) for child in grp if child.name in columns]

    # Convert any float32 columns to float64 for processing.
    for i in range(len(data)):
        name, vec = data[i]
        if vec.dtype == np.float32:
            data[i] = (name, vec.astype(np.float64))

    if not isinstance(hdf5_path, tables.file.File):
        hf.close()
    return pd.DataFrame.from_items(data)

Jetzt führe ich dies normalerweise auf einem Computer mit einer Menge Speicher aus, sodass ich bei der Speichernutzung möglicherweise nicht vorsichtig genug bin. Beispielsweise liest die Ladeoperation standardmäßig den gesamten Datensatz.

Das funktioniert im Allgemeinen für mich, aber es ist ein bisschen klobig und ich kann die ausgefallene Pytables-Magie nicht verwenden.

Bearbeiten: Der eigentliche Vorteil dieses Ansatzes gegenüber dem Standard-Pytable-Array mit Datensätzen besteht darin, dass ich die Daten dann mit h5r in R laden kann, das keine Tabellen verarbeiten kann. Zumindest konnte ich es nicht schaffen, heterogene Tabellen zu laden.

Johann Hibschman
quelle
Würde es Ihnen etwas ausmachen, mir einen Teil Ihres Codes mitzuteilen? Ich bin daran interessiert, wie Sie die Daten aus einem flachen Textformat laden, ohne die Datentypen zu kennen, bevor Sie zu pytables wechseln. Es sieht auch so aus, als würden Sie nur mit Daten eines Typs arbeiten. Ist das korrekt?
Zelazny7
1
Zunächst gehe ich davon aus, dass ich die Arten der Spalten vor dem Laden kenne, anstatt zu versuchen, anhand der Daten zu erraten. Ich speichere eine JSON-Datei "data spec" mit den Spaltennamen und -typen und verwende diese bei der Verarbeitung der Daten. (Die Datei ist normalerweise eine schreckliche BCP-Ausgabe ohne Beschriftungen.) Die von mir verwendeten Datentypen sind Zeichenfolgen, Gleitkommazahlen, Ganzzahlen oder monatliche Daten. Ich verwandle die Zeichenfolgen in Ints, indem ich eine Aufzählungstabelle speichere und die Daten in Ints konvertiere (Monate nach 2000), sodass ich nur noch Ints und Floats in meinen Daten sowie die Aufzählung habe. Ich speichere die Floats jetzt als float64, habe aber mit float32 experimentiert.
Johann Hibschman
1
Wenn Sie Zeit haben, versuchen Sie es bitte mit R: pandas.pydata.org/pandas-docs/dev/… , und wenn Sie Schwierigkeiten haben, können wir es vielleicht optimieren
Jeff
Ich werde es versuchen, wenn ich kann. rhdf5 ist ein Schmerz, da es sich um ein Bioconductor-Paket handelt, anstatt nur wie h5r auf CRAN zu sein. Ich bin unserem Team für technische Architektur ausgeliefert, und als ich das letzte Mal danach gefragt habe, gab es ein Problem mit rhdf5. Auf jeden Fall scheint es nur ein Fehler zu sein, mit einem OLAP-Speicher eher zeilen- als spaltenorientiert zu arbeiten, aber jetzt streife ich.
Johann Hibschman
38

Ein Trick, den ich für Anwendungsfälle mit großen Datenmengen als hilfreich empfand, besteht darin, das Datenvolumen zu reduzieren, indem die Float-Genauigkeit auf 32 Bit reduziert wird. Es ist nicht in allen Fällen anwendbar, aber in vielen Anwendungen ist die 64-Bit-Genauigkeit übertrieben und die 2-fache Speicherersparnis lohnt sich. Um einen offensichtlichen Punkt noch deutlicher zu machen:

>>> df = pd.DataFrame(np.random.randn(int(1e8), 5))
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float64(5)
memory usage: 3.7 GB

>>> df.astype(np.float32).info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float32(5)
memory usage: 1.9 GB
ytsaig
quelle
26

Wie von anderen angemerkt, hat sich nach einigen Jahren ein "Out-of-Core" -Pandas- Äquivalent herausgebildet: dask . Obwohl dask kein Ersatz für Pandas und all seine Funktionen ist, zeichnet es sich aus mehreren Gründen aus:

Dask ist eine flexible Parallel-Computing-Bibliothek für analytisches Computing, die für die dynamische Aufgabenplanung für interaktive Computer-Workloads von „Big Data“ -Sammlungen wie parallele Arrays, Datenrahmen und Listen optimiert ist, die allgemeine Schnittstellen wie NumPy-, Pandas- oder Python-Iteratoren auf größere erweitern. als Speicher oder verteilte Umgebungen und skaliert von Laptops zu Clustern.

Dask betont die folgenden Tugenden:

  • Vertraut: Bietet parallelisierte NumPy-Array- und Pandas DataFrame-Objekte
  • Flexibel: Bietet eine Taskplanungsschnittstelle für benutzerdefinierte Workloads und die Integration in andere Projekte.
  • Native: Ermöglicht verteiltes Computing in Pure Python mit Zugriff auf den PyData-Stack.
  • Schnell: Arbeitet mit geringem Overhead, geringer Latenz und minimaler Serialisierung, die für schnelle numerische Algorithmen erforderlich sind
  • Skaliert nach oben: Läuft stabil auf Clustern mit Tausenden von Kernen. Skaliert nach unten: Trivial, um einen Laptop in einem einzigen Prozess einzurichten und auszuführen
  • Reaktionsschnell: Entwickelt für interaktives Computing bietet es schnelles Feedback und Diagnose, um Menschen zu helfen

und um ein einfaches Codebeispiel hinzuzufügen:

import dask.dataframe as dd
df = dd.read_csv('2015-*-*.csv')
df.groupby(df.user_id).value.mean().compute()

ersetzt einen Pandas-Code wie folgt:

import pandas as pd
df = pd.read_csv('2015-01-01.csv')
df.groupby(df.user_id).value.mean()

und bietet insbesondere über die concurrent.futuresSchnittstelle eine allgemeine Infrastruktur für die Übermittlung benutzerdefinierter Aufgaben:

from dask.distributed import Client
client = Client('scheduler:port')

futures = []
for fn in filenames:
    future = client.submit(load, fn)
    futures.append(future)

summary = client.submit(summarize, futures)
summary.result()
wp78de
quelle
Ich habe diese Antwort hinzugefügt, da der Beitrag von @Private regelmäßig in der Liste der zum Löschen vorgeschlagenen Inhalte und der Länge angezeigt wird.
wp78de
17

Erwähnenswert ist auch hier Ray ,
ein verteiltes Berechnungsframework, das eine eigene Implementierung für Pandas auf verteilte Weise hat.

Ersetzen Sie einfach den Pandas-Import, und der Code sollte wie folgt funktionieren:

# import pandas as pd
import ray.dataframe as pd

#use pd as usual

Weitere Details finden Sie hier:

https://rise.cs.berkeley.edu/blog/pandas-on-ray/

lev
quelle
16

Noch eine Variation

Viele der in Pandas ausgeführten Operationen können auch als Datenbankabfrage ausgeführt werden (SQL, Mongo).

Mit einem RDBMS oder Mongodb können Sie einige der Aggregationen in der DB-Abfrage ausführen (die für große Datenmengen optimiert ist und Cache und Indizes effizient verwendet).

Später können Sie die Nachbearbeitung mit Pandas durchführen.

Der Vorteil dieser Methode besteht darin, dass Sie die DB-Optimierungen für die Arbeit mit großen Datenmengen erhalten und gleichzeitig die Logik in einer deklarativen Syntax auf hoher Ebene definieren - und sich nicht mit den Details der Entscheidung befassen müssen, was im Speicher zu tun ist und was zu tun ist des Kerns.

Und obwohl die Abfragesprache und die Pandas unterschiedlich sind, ist es normalerweise nicht kompliziert, einen Teil der Logik von einem zum anderen zu übersetzen.

Ophir Yoktan
quelle
11

Betrachten Sie Ruffus, wenn Sie den einfachen Weg gehen, eine Datenpipeline zu erstellen, die in mehrere kleinere Dateien unterteilt ist.

Golfaffe
quelle
9

Ich bin kürzlich auf ein ähnliches Problem gestoßen. Ich fand, dass es gut funktioniert, die Daten einfach in Chunks zu lesen und anzuhängen, während ich sie in Chunks auf dieselbe CSV schreibe. Mein Problem bestand darin, eine Datumsspalte basierend auf Informationen in einer anderen Tabelle hinzuzufügen und dabei den Wert bestimmter Spalten wie folgt zu verwenden. Dies kann denjenigen helfen, die von Dask und HDF5 verwirrt sind, aber mit Pandas wie mir besser vertraut sind.

def addDateColumn():
"""Adds time to the daily rainfall data. Reads the csv as chunks of 100k 
   rows at a time and outputs them, appending as needed, to a single csv. 
   Uses the column of the raster names to get the date.
"""
    df = pd.read_csv(pathlist[1]+"CHIRPS_tanz.csv", iterator=True, 
                     chunksize=100000) #read csv file as 100k chunks

    '''Do some stuff'''

    count = 1 #for indexing item in time list 
    for chunk in df: #for each 100k rows
        newtime = [] #empty list to append repeating times for different rows
        toiterate = chunk[chunk.columns[2]] #ID of raster nums to base time
        while count <= toiterate.max():
            for i in toiterate: 
                if i ==count:
                    newtime.append(newyears[count])
            count+=1
        print "Finished", str(chunknum), "chunks"
        chunk["time"] = newtime #create new column in dataframe based on time
        outname = "CHIRPS_tanz_time2.csv"
        #append each output to same csv, using no header
        chunk.to_csv(pathlist[2]+outname, mode='a', header=None, index=None)
Pauken
quelle
8

Ich möchte auf das Vaex-Paket hinweisen.

Vaex ist eine Python-Bibliothek für faule Out-of-Core-DataFrames (ähnlich wie Pandas) zur Visualisierung und Erkundung großer tabellarischer Datensätze. Es kann Statistiken wie Mittelwert, Summe, Anzahl, Standardabweichung usw. in einem N-dimensionalen Raster bis zu einer Milliarde (10 9 ) Objekte / Zeilen pro Sekunde berechnen . Die Visualisierung erfolgt mithilfe von Histogrammen, Dichtediagrammen und 3D-Volumen-Rendering, um eine interaktive Untersuchung von Big Data zu ermöglichen. Vaex verwendet Speicherzuordnung, Null-Speicher-Kopierrichtlinie und verzögerte Berechnungen für beste Leistung (keine Speicherverschwendung).

Schauen Sie sich die Dokumentation an: https://vaex.readthedocs.io/en/latest/ Die API kommt der API von Pandas sehr nahe.

rauben
quelle
0

Warum Pandas? Haben Sie Standard Python ausprobiert ? ?

Die Verwendung von Standardbibliothek Python. Pandas wird auch mit der jüngsten Version der stabilen Version regelmäßig aktualisiert.

Mit der Standard-Python-Bibliothek wird Ihr Code immer ausgeführt.

Eine Möglichkeit besteht darin, eine Vorstellung davon zu haben, wie Ihre Daten gespeichert werden sollen und welche Fragen Sie zu den Daten lösen möchten. Zeichnen Sie dann ein Schema, wie Sie Ihre Daten organisieren können (Think Tables), das Ihnen beim Abfragen der Daten hilft, nicht unbedingt bei der Normalisierung.

Sie können Folgendes nutzen:

  • Liste der Wörterbücher zum Speichern der Daten im Speicher, ein Diktat ist eine Zeile,
  • Generatoren, um die Daten Zeile für Zeile zu verarbeiten, damit Ihr RAM nicht überläuft.
  • Listenverständnis zum Abfragen Ihrer Daten,
  • Verwenden Sie Counter, DefaultDict, ...
  • Speichern Sie Ihre Daten auf Ihrer Festplatte mit der von Ihnen gewählten Speicherlösung. JSON könnte eine davon sein.

Ram und HDD werden mit der Zeit immer billiger und Standard Python 3 ist weit verbreitet und stabil.

Pelikan
quelle
-1

Im Moment arbeite ich "wie" Sie, nur in einem niedrigeren Maßstab, weshalb ich keinen PoC für meinen Vorschlag habe.

Es scheint mir jedoch erfolgreich zu sein, pickle als Caching-System zu verwenden und die Ausführung verschiedener Funktionen in Dateien auszulagern - diese Dateien aus meiner Kommando- / Hauptdatei auszuführen; Zum Beispiel verwende ich eine prepare_use.py, um Objekttypen zu konvertieren, einen Datensatz in Test-, Validierungs- und Vorhersagedatensätze aufzuteilen.

Wie funktioniert Ihr Caching mit Gurke? Ich benutze Strings, um auf Pickle-Dateien zuzugreifen, die dynamisch erstellt werden, abhängig davon, welche Parameter und Datensätze übergeben wurden (damit versuche ich zu erfassen und festzustellen, ob das Programm bereits ausgeführt wurde, indem ich .shape für den Datensatz verwende, dikt für übergeben Parameter). Wenn ich diese Maßnahmen respektiere, erhalte ich einen String, der versucht, eine .pickle-Datei zu finden und zu lesen, und kann, falls gefunden, die Verarbeitungszeit überspringen, um zu der Ausführung zu springen, an der ich gerade arbeite.

Bei der Verwendung von Datenbanken stieß ich auf ähnliche Probleme, weshalb ich Freude an der Verwendung dieser Lösung hatte. Es gibt jedoch mit Sicherheit viele Einschränkungen, z. B. das Speichern großer Pickle-Sets aufgrund von Redundanz. Das Aktualisieren einer Tabelle von vor nach nach einer Transformation kann mit einer ordnungsgemäßen Indizierung durchgeführt werden. Durch die Validierung von Informationen wird ein ganz anderes Buch geöffnet (ich habe versucht, gecrawlte Mietdaten zu konsolidieren und habe die Verwendung einer Datenbank nach 2 Stunden im Grunde genommen eingestellt -, da ich danach gerne zurückgesprungen wäre jeder Transformationsprozess)

Ich hoffe meine 2 Cent helfen dir irgendwie.

Schöne Grüße.

TiRoX
quelle