Großer, beständiger DataFrame bei Pandas

91

Ich erforsche die Umstellung auf Python und Pandas als langjähriger SAS-Benutzer.

Als ich heute einige Tests durchführte, war ich überrascht, dass Python beim Versuch, pandas.read_csv()eine 128-MB-CSV-Datei zu erstellen, nicht genügend Speicherplatz hatte . Es hatte ungefähr 200.000 Zeilen und 200 Spalten mit meist numerischen Daten.

Mit SAS kann ich eine CSV-Datei in ein SAS-Dataset importieren und sie kann so groß sein wie meine Festplatte.

Gibt es etwas Analoges pandas?

Ich arbeite regelmäßig mit großen Dateien und habe keinen Zugriff auf ein verteiltes Computernetzwerk.

Zelazny7
quelle
Ich bin nicht mit Pandas vertraut, aber vielleicht möchten Sie die Datei durchgehen. pandas.pydata.org/pandas-docs/stable/…
monkut

Antworten:

79

Im Prinzip sollte nicht der Arbeitsspeicher ausgehen, aber es gibt derzeit Speicherprobleme bei read_csvgroßen Dateien, die durch komplexe interne Python-Probleme verursacht werden (dies ist vage, aber seit langem bekannt: http://github.com/pydata / pandas / issue / 407 ).

Im Moment gibt es keine perfekte Lösung (hier ist eine mühsame: Sie könnten die Datei zeilenweise in ein vorab zugewiesenes NumPy-Array oder eine speicherabgebildete Datei transkribieren - np.mmap), aber ich werde daran arbeiten in naher Zukunft. Eine andere Lösung besteht darin, die Datei in kleineren Teilen zu lesen (verwenden iterator=True, chunksize=1000) und dann mit zu verketten pd.concat. Das Problem tritt auf, wenn Sie die gesamte Textdatei in einem großen Schluck in den Speicher ziehen.

Wes McKinney
quelle
1
Angenommen, ich kann die Datei lesen und alle zusammen in einem DataFrame zusammenfassen. Muss sich der DataFrame im Speicher befinden? Mit SAS kann ich mit Datensätzen jeder Größe arbeiten, solange ich über Festplattenspeicher verfüge. Ist es dasselbe mit DataFrames? Ich habe den Eindruck, dass sie durch RAM und nicht durch Festplattenspeicher eingeschränkt sind. Entschuldigung für die Noob-Frage und vielen Dank für Ihre Hilfe. Ich genieße dein Buch.
Zelazny7
3
Richtig, Sie sind durch RAM eingeschränkt. SAS bietet in der Tat eine viel bessere Unterstützung für die "Out-of-Core" -Verarbeitung von Big Data.
Wes McKinney
5
@WesMcKinney Diese Problemumgehungen sollten aufgrund des neuen CSV-Loaders, den Sie in 0.10 gelandet sind, nicht mehr benötigt werden, oder?
Gabriel Grant
78

Wes hat natürlich recht! Ich mische mich nur ein, um einen etwas vollständigeren Beispielcode bereitzustellen. Ich hatte das gleiche Problem mit einer 129-MB-Datei, das behoben wurde durch:

import pandas as pd

tp = pd.read_csv('large_dataset.csv', iterator=True, chunksize=1000)  # gives TextFileReader, which is iterable with chunks of 1000 rows.
df = pd.concat(tp, ignore_index=True)  # df is DataFrame. If errors, do `list(tp)` instead of `tp`
Fickludd
quelle
6
Ich denke du kannst es einfach tun df = concate(tp, ignore_index=True)?
Andy Hayden
@smci Versuchte dies schnell mit den gleichen Daten wiederholt x4 (550 Mb) oder x8 (1.1Gb). Interessanterweise ging x4 mit oder ohne [x für x in tp] einwandfrei durch und x8 stürzte in einem MemoryError ab.
Fickludd
3
Ich erhalte diesen Fehler bei der Verwendung : AssertionError: first argument must be a list-like of pandas objects, you passed an object of type "TextFileReader". Irgendeine Idee, was hier passiert?
Prinz Kumar
3
Dieser Fehler wird in 0.14 (Veröffentlichung in Kürze) behoben, github.com/pydata/pandas/pull/6941 ; Problemumgehung für <0.14.0 ist zu tunpd.concat(list(tp), ignore_index=True)
Jeff
1
Was ist, wenn die Werte Zeichenfolgen oder kategorial sind - ich erhalte den Fehler: inkompatible Kategorien in kategorialen Konkaten
As3adTintin
40

Dies ist ein älterer Thread, aber ich wollte nur meine Problemumgehungslösung hier ablegen. Ich habe den chunksizeParameter anfangs ausprobiert (auch bei recht kleinen Werten wie 10000), aber es hat nicht viel geholfen. hatte immer noch technische Probleme mit der Speichergröße (meine CSV war ~ 7,5 GB).

Im Moment lese ich nur Teile der CSV-Dateien in einem For-Loop-Ansatz und füge sie z. B. Schritt für Schritt einer SQLite-Datenbank hinzu:

import pandas as pd
import sqlite3
from pandas.io import sql
import subprocess

# In and output file paths
in_csv = '../data/my_large.csv'
out_sqlite = '../data/my.sqlite'

table_name = 'my_table' # name for the SQLite database table
chunksize = 100000 # number of lines to process at each iteration

# columns that should be read from the CSV file
columns = ['molecule_id','charge','db','drugsnow','hba','hbd','loc','nrb','smiles']

# Get number of lines in the CSV file
nlines = subprocess.check_output('wc -l %s' % in_csv, shell=True)
nlines = int(nlines.split()[0]) 

# connect to database
cnx = sqlite3.connect(out_sqlite)

# Iteratively read CSV and dump lines into the SQLite table
for i in range(0, nlines, chunksize):

    df = pd.read_csv(in_csv,  
            header=None,  # no header, define column header manually later
            nrows=chunksize, # number of rows to read at each iteration
            skiprows=i)   # skip rows that were already read

    # columns to read        
    df.columns = columns

    sql.to_sql(df, 
                name=table_name, 
                con=cnx, 
                index=False, # don't use CSV file index
                index_label='molecule_id', # use a unique column from DataFrame as index
                if_exists='append') 
cnx.close()    

quelle
4
Super nützlich, um einen realistischen Anwendungsfall für die Funktion zum Lesen von Chunks zu sehen. Vielen Dank.
Alex Kestner
5
Nur eine kleine Bemerkung zu diesem alten Thema: pandas.read_csvGibt direkt (zumindest in der Version, die ich gerade verwende) einen Iterator zurück, wenn Sie einfach iterator=Trueund angeben chunksize=chunksize. Daher würden Sie einfach eine forSchleife über den pd.read_csvAnruf durchführen, anstatt ihn jedes Mal neu zu instanziieren. Dies kostet jedoch nur den Anrufaufwand, es gibt möglicherweise keine signifikanten Auswirkungen.
Joël
1
Hallo Joel. Danke für den Hinweis! Die iterator=Trueund chunksizeParameter existierten damals schon, wenn ich mich richtig erinnere. Vielleicht gab es einen Fehler in einer älteren Version, der die Speicherexplosion verursachte - ich werde es beim nächsten Lesen eines großen DataFrame in Pandas erneut versuchen (ich verwende Blaze jetzt hauptsächlich für solche Aufgaben)
6

Unten ist mein Arbeitsablauf.

import sqlalchemy as sa
import pandas as pd
import psycopg2

count = 0
con = sa.create_engine('postgresql://postgres:pwd@localhost:00001/r')
#con = sa.create_engine('sqlite:///XXXXX.db') SQLite
chunks = pd.read_csv('..file', chunksize=10000, encoding="ISO-8859-1",
                     sep=',', error_bad_lines=False, index_col=False, dtype='unicode')

Basierend auf Ihrer Dateigröße sollten Sie die Blockgröße besser optimieren.

 for chunk in chunks:
        chunk.to_sql(name='Table', if_exists='append', con=con)
        count += 1
        print(count)

Nachdem Sie alle Daten in der Datenbank gespeichert haben, können Sie die benötigten Daten aus der Datenbank abfragen.

BEN_YO
quelle
3

Wenn Sie große CSV-Dateien laden möchten, ist dask möglicherweise eine gute Option. Es ahmt die Pandas-API nach, fühlt sich also Pandas ziemlich ähnlich

Link zu Dask auf Github

user8108173
quelle
Danke, seit ich das gepostet habe, benutze ich dask und das Parkettformat.
Zelazny7
1

Sie können Pytable anstelle von pandas df verwenden. Es ist für große Datenmengen ausgelegt und das Dateiformat ist in hdf5. Die Bearbeitungszeit ist also relativ schnell.

Elm662
quelle