Wie lese ich eine große CSV-Datei mit Pandas?


194

Ich versuche, eine große CSV-Datei (ca. 6 GB) in Pandas zu lesen, und es wird ein Speicherfehler angezeigt:

MemoryError                               Traceback (most recent call last)
<ipython-input-58-67a72687871b> in <module>()
----> 1 data=pd.read_csv('aphro.csv',sep=';')

...

MemoryError: 

Hilfe dazu?


3
Seltsamerweise wurde eine sehr ähnliche Frage fast ein Jahr vor dieser gestellt ...
DarkCygnus


Beantwortet das deine Frage? "Large Data" -Arbeitsabläufe mit Pandas
AMC

Antworten:


261

Der Fehler zeigt, dass der Computer nicht über genügend Speicher verfügt, um die gesamte CSV gleichzeitig in einen DataFrame einzulesen. Angenommen, Sie benötigen nicht den gesamten Datensatz auf einmal im Speicher. Eine Möglichkeit, das Problem zu vermeiden, besteht darin , die CSV in Blöcken zu verarbeiten (durch Angabe des chunksizeParameters):

chunksize = 10 ** 6
for chunk in pd.read_csv(filename, chunksize=chunksize):
    process(chunk)

Der chunksizeParameter gibt die Anzahl der Zeilen pro Block an. (Der letzte Block kann chunksizenatürlich weniger als Zeilen enthalten .)


17
Im Allgemeinen benötigen Sie das Zweifache des endgültigen Speichers, um etwas einzulesen (von CSV, obwohl andere Formate einen geringeren Speicherbedarf haben). Zu Ihrer Information, dies gilt für den Versuch, fast alles auf einmal zu tun. Viel besser, es zu zerlegen (was eine konstante Speichernutzung hat).
Jeff

24
@altabq: Das Problem hier ist, dass wir nicht genug Speicher haben, um einen einzelnen DataFrame zu erstellen, der alle Daten enthält. Die obige Lösung versucht, mit dieser Situation umzugehen, indem die Blöcke (z. B. durch Aggregieren oder Extrahieren nur der gewünschten Informationen) nacheinander reduziert werden, wodurch Speicherplatz gespart wird. Was auch immer Sie tun, rufen Sie NICHT DF.append(chunk)innerhalb der Schleife auf. Das wird O(N^2)Kopiervorgänge verwenden. Es ist besser, die aggregierten Daten an eine Liste anzuhängen und dann den DataFrame aus der Liste mit einem Aufruf von pd.DataFrameoder pd.concat(abhängig vom Typ der aggregierten Daten) zu erstellen.
Unutbu

12
@altabq: Das Aufrufen DF.append(chunk)in einer Schleife erfordert O(N^2)Kopiervorgänge mit Nder Größe der Chunks, da bei jedem Aufruf DF.appendein neuer DataFrame zurückgegeben wird. Durch Aufrufen pd.DataFrameoder pd.concat einmal außerhalb der Schleife wird der Kopieraufwand verringert O(N).
Unutbu

5
@Pyderman: Ja, der chunksizeParameter bezieht sich auf die Anzahl der Zeilen pro Block. Der letzte Block kann chunksizenatürlich weniger als Zeilen enthalten .
Unutbu

7
@Pyderman: Ja; Aufruf pd.concat([list_of_dfs]) einmal nach der Schleife ist viel schneller als Aufruf pd.concatoder df.appendmehrmals in der Schleife. Natürlich benötigen Sie eine beträchtliche Menge an Speicher, um die gesamte 6-GB-CSV als einen DataFrame zu speichern.
Unutbu

85

Chunking sollte nicht immer die erste Anlaufstelle für dieses Problem sein.

  1. Ist die Datei aufgrund wiederholter nicht numerischer Daten oder unerwünschter Spalten groß?

    In diesem Fall können Sie manchmal massive Speichereinsparungen feststellen, indem Sie Spalten als Kategorien einlesen und die erforderlichen Spalten über denusecols Parameter pd.read_csv auswählen .

  2. Erfordert Ihr Workflow das Schneiden, Bearbeiten und Exportieren?

    In diesem Fall können Sie dask.dataframe verwenden , um zu schneiden, Ihre Berechnungen durchzuführen und iterativ zu exportieren. Das Chunking wird stillschweigend von dask ausgeführt, das auch eine Teilmenge der Pandas-API unterstützt.

  3. Wenn alles andere fehlschlägt, lesen Sie Zeile für Zeile über Chunks.

    Chunk via Pandas oder via CSV-Bibliothek als letztes Mittel.


3
Dask war mir nicht bewusst. +100 dafür!
Noamtm

34

Ich ging so vor:

chunks=pd.read_table('aphro.csv',chunksize=1000000,sep=';',\
       names=['lat','long','rf','date','slno'],index_col='slno',\
       header=None,parse_dates=['date'])

df=pd.DataFrame()
%time df=pd.concat(chunk.groupby(['lat','long',chunk['date'].map(lambda x: x.year)])['rf'].agg(['sum']) for chunk in chunks)

22
Gibt es einen Grund , warum Sie von schaltet read_csvzu read_table?
Pyderman

33

Für große Datenmengen empfehle ich die Verwendung der Bibliothek "dask",
z.

# Dataframes implement the Pandas API
import dask.dataframe as dd
df = dd.read_csv('s3://.../2018-*-*.csv')

Weitere Informationen finden Sie hier in der Dokumentation .

Eine weitere gute Alternative wäre die Verwendung von Modin, da alle Funktionen mit Pandas identisch sind, jedoch verteilte Datenrahmenbibliotheken wie dask genutzt werden.


11
Alle Vorteile gegenüber Pandas, könnte es
begrüßen

2
Ich habe Dask nicht sehr lange verwendet, aber die Hauptvorteile in meinen Anwendungsfällen waren, dass Dask auf mehreren Computern parallel ausgeführt werden kann und Daten auch als Slices in den Speicher passen können.
Simbarashe Timothy Motsi

2
Vielen Dank! Ist dask ein Ersatz für Pandas oder funktioniert es auf Pandas als Schicht
PirateApp

3
Willkommen, es funktioniert als Wrapper für Numpy, Pandas und Scikit-Learn.
Simbarashe Timothy Motsi

1
Ich habe versucht, mit Dask auf mehrere Probleme zu stoßen und habe immer einen Fehler für alles ausgegeben. Auch bei Chunks werden Speicherfehler ausgelöst. Siehe stackoverflow.com/questions/59865572/…
Genarito

10

Die obige Antwort erfüllt das Thema bereits. Wenn Sie alle Daten im Speicher benötigen, schauen Sie sich bcolz an . Es komprimiert die Daten im Speicher. Ich habe wirklich gute Erfahrungen damit gemacht. Aber es fehlen viele Pandas-Funktionen

Bearbeiten: Ich habe Komprimierungsraten bei etwa 1/10 oder Originalgröße, denke ich, natürlich abhängig von der Art der Daten. Wichtige fehlende Merkmale waren Aggregate.


2
Bitte verbessern Sie diese Antwort, indem Sie uns mitteilen, a) welche Kompressionsverhältnisse Sie erhalten und b) welche Hauptmerkmale von Pandas fehlen? Kann es mit NAs umgehen? Saiten? Kategorien? Termine?
smci

Huh? Kann es mit NAs umgehen? Saiten? Kategorien? Termine? Dies sind die Dinge, die Pandas CSV-Lesen langsam und schlaff machen. NAs und Objekte wie Strings (auch kurze) sind ein Killer. Übrigens ist die .ipynb, auf die in Ihrem Blog verwiesen wird, nicht verfügbar.
smci

1
@smci Ich habe deine Notiz gelesen. aber ich schlage vor, Sie werfen einen Blick auf die Dokumente. Ich würde sie selbst lesen müssen.
PlagTag

2
Ok, es kann also nicht mit NAs, Strings oder Datumsangaben umgehen. Ich bezweifle, dass es auch mit Schwimmern umgehen kann.
smci

1
Ich nehme an, Sie könnten Pandas mit der chunksgenannten Methode vorverarbeiten und dann bcolz verwenden, wenn Sie alle Daten im Speicher benötigen, um eine Analyse durchzuführen. Nur ein Gedanke.
JakeCowton

6

Sie können die Daten als Chunks einlesen und jeden Chunk als Pickle speichern.

import pandas as pd 
import pickle

in_path = "" #Path where the large file is
out_path = "" #Path to save the pickle files to
chunk_size = 400000 #size of chunks relies on your available memory
separator = "~"

reader = pd.read_csv(in_path,sep=separator,chunksize=chunk_size, 
                    low_memory=False)    


for i, chunk in enumerate(reader):
    out_file = out_path + "/data_{}.pkl".format(i+1)
    with open(out_file, "wb") as f:
        pickle.dump(chunk,f,pickle.HIGHEST_PROTOCOL)

Im nächsten Schritt lesen Sie die Gurken ein und hängen jede Gurke an Ihren gewünschten Datenrahmen an.

import glob
pickle_path = "" #Same Path as out_path i.e. where the pickle files are

data_p_files=[]
for name in glob.glob(pickle_path + "/data_*.pkl"):
   data_p_files.append(name)


df = pd.DataFrame([])
for i in range(len(data_p_files)):
    df = df.append(pd.read_pickle(data_p_files[i]),ignore_index=True)

3
Wenn Ihr Finale dfvollständig in den Speicher passt (wie impliziert) und dieselbe Datenmenge wie Ihre Eingabe enthält, müssen Sie sicher überhaupt keinen Block erstellen?
Jpp

In diesem Fall müssten Sie einen Block erstellen, wenn Ihre Datei beispielsweise sehr breit ist (z. B. mehr als 100 Spalten mit vielen Zeichenfolgenspalten). Dies erhöht den Speicher, der benötigt wird, um den df im Speicher zu halten. Selbst eine solche 4-GB-Datei könnte zwischen 20 und 30 GB RAM auf einer Box mit 64 GB RAM verbrauchen.
cdabel

4

Die Funktionen read_csv und read_table sind nahezu identisch. Sie müssen jedoch das Trennzeichen "," zuweisen, wenn Sie die Funktion read_table in Ihrem Programm verwenden.

def get_from_action_data(fname, chunk_size=100000):
    reader = pd.read_csv(fname, header=0, iterator=True)
    chunks = []
    loop = True
    while loop:
        try:
            chunk = reader.get_chunk(chunk_size)[["user_id", "type"]]
            chunks.append(chunk)
        except StopIteration:
            loop = False
            print("Iteration is stopped")

    df_ac = pd.concat(chunks, ignore_index=True)

Es wäre hilfreich, wenn Sie Ihre Frage in diesem Beitrag angeben würden. Wie "Was ist der Unterschied zwischen read_csv und read_table?" oder "Warum braucht die Lesetabelle ein Trennzeichen?"
nate_weldon

1
Es hängt davon ab, wie Ihre Datei aussieht. Einige Dateien haben allgemeine Trennzeichen wie "," oder "|" oder "\ t", aber möglicherweise werden andere Dateien mit Trennzeichen wie 0x01, 0x02 (zusammengesetzt) ​​usw. angezeigt. Daher ist read_table eher für ungewöhnliche Trennzeichen geeignet, aber read_csv kann den gleichen Job genauso gut ausführen.
Naufal

3

Lösung 1:

Verwenden von Pandas mit großen Datenmengen

Lösung 2:

TextFileReader = pd.read_csv(path, chunksize=1000)  # the number of rows per chunk

dfList = []
for df in TextFileReader:
    dfList.append(df)

df = pd.concat(dfList,sort=False)

3
Auch hier laden wir die 6-GB-Datei vollständig in den Speicher. Gibt es irgendwelche Optionen, können wir den aktuellen
Block

6
tu es einfach nicht dfList.append, verarbeite einfach jeden chunk ( df) separat
gokul_uf

3

Hier folgt ein Beispiel:

chunkTemp = []
queryTemp = []
query = pd.DataFrame()

for chunk in pd.read_csv(file, header=0, chunksize=<your_chunksize>, iterator=True, low_memory=False):

    #REPLACING BLANK SPACES AT COLUMNS' NAMES FOR SQL OPTIMIZATION
    chunk = chunk.rename(columns = {c: c.replace(' ', '') for c in chunk.columns})

    #YOU CAN EITHER: 
    #1)BUFFER THE CHUNKS IN ORDER TO LOAD YOUR WHOLE DATASET 
    chunkTemp.append(chunk)

    #2)DO YOUR PROCESSING OVER A CHUNK AND STORE THE RESULT OF IT
    query = chunk[chunk[<column_name>].str.startswith(<some_pattern>)]   
    #BUFFERING PROCESSED DATA
    queryTemp.append(query)

#!  NEVER DO pd.concat OR pd.DataFrame() INSIDE A LOOP
print("Database: CONCATENATING CHUNKS INTO A SINGLE DATAFRAME")
chunk = pd.concat(chunkTemp)
print("Database: LOADED")

#CONCATENATING PROCESSED DATA
query = pd.concat(queryTemp)
print(query)


2

Wenn Sie Pandas verwenden, lesen Sie eine große Datei in Chunk und geben Sie dann Zeile für Zeile nach. Hier ist, was ich getan habe

import pandas as pd

def chunck_generator(filename, header=False,chunk_size = 10 ** 5):
   for chunk in pd.read_csv(filename,delimiter=',', iterator=True, chunksize=chunk_size, parse_dates=[1] ): 
        yield (chunk)

def _generator( filename, header=False,chunk_size = 10 ** 5):
    chunk = chunck_generator(filename, header=False,chunk_size = 10 ** 5)
    for row in chunk:
        yield row

if __name__ == "__main__":
filename = r'file.csv'
        generator = generator(filename=filename)
        while True:
           print(next(generator))

1

Ich möchte eine umfassendere Antwort geben, die auf den meisten potenziellen Lösungen basiert, die bereits bereitgestellt werden. Ich möchte auch auf eine weitere mögliche Hilfe hinweisen, die den Lesevorgang unterstützen kann.

Option 1: dtypes

"dtypes" ist ein ziemlich leistungsfähiger Parameter, mit dem Sie den Speicherdruck von readMethoden reduzieren können. Siehe diese und diese Antwort. Pandas versuchen standardmäßig, d-Typen der Daten abzuleiten.

Bezugnehmend auf Datenstrukturen findet für alle gespeicherten Daten eine Speicherzuordnung statt. Grundsätzlich beziehen Sie sich auf die folgenden Werte (Die folgende Tabelle zeigt Werte für die Programmiersprache C):

The maximum value of UNSIGNED CHAR = 255                                    
The minimum value of SHORT INT = -32768                                     
The maximum value of SHORT INT = 32767                                      
The minimum value of INT = -2147483648                                      
The maximum value of INT = 2147483647                                       
The minimum value of CHAR = -128                                            
The maximum value of CHAR = 127                                             
The minimum value of LONG = -9223372036854775808                            
The maximum value of LONG = 9223372036854775807

Auf dieser Seite finden Sie die Übereinstimmung zwischen den Typen NumPy und C.

Angenommen, Sie haben eine Reihe von Ganzzahlen mit Ziffern . Sie können sowohl theoretisch als auch praktisch ein Array vom Typ 16-Bit-Ganzzahl zuweisen, aber Sie würden dann mehr Speicher zuweisen, als Sie tatsächlich zum Speichern dieses Arrays benötigen. Um dies zu verhindern, können Sie die dtypeOption aktivieren read_csv. Sie möchten die Array-Elemente nicht als lange Ganzzahl speichern, wo Sie sie tatsächlich mit einer 8-Bit-Ganzzahl ( np.int8oder np.uint8) versehen können.

Beachten Sie die folgende dtype-Karte.

Quelle: https://pbpython.com/pandas_dtypes.html

Sie können dtypeParameter als Parameter für Pandas-Methoden übergeben, readwie dies unter {column: type} vorgeschrieben ist.

import numpy as np
import pandas as pd

df_dtype = {
        "column_1": int,
        "column_2": str,
        "column_3": np.int16,
        "column_4": np.uint8,
        ...
        "column_n": np.float32
}

df = pd.read_csv('path/to/file', dtype=df_dtype)

Option 2: Von Chunks gelesen

Durch das Lesen der Daten in Blöcken können Sie auf einen Teil der Daten im Speicher zugreifen, und Sie können eine Vorverarbeitung auf Ihre Daten anwenden und die verarbeiteten Daten anstelle von Rohdaten beibehalten. Es wäre viel besser, wenn Sie diese Option mit der ersten Option, dtypes , kombinieren würden .

Ich möchte auf die Pandas-Kochbuchabschnitte für diesen Prozess hinweisen, die Sie hier finden . Beachten Sie diese beiden Abschnitte dort;

Option 3: Dask

Dask ist ein Framework, das auf der Dask-Website wie folgt definiert ist :

Dask bietet erweiterte Parallelität für die Analyse und ermöglicht eine skalierbare Leistung für die Tools, die Sie lieben

Es wurde geboren, um die notwendigen Teile abzudecken, die Pandas nicht erreichen können. Dask ist ein leistungsstarkes Framework, mit dem Sie viel mehr auf Daten zugreifen können, indem Sie es auf verteilte Weise verarbeiten.

Sie können dask verwenden, um Ihre Daten als Ganzes vorzuverarbeiten. Dask kümmert sich um den Chunking-Teil. Im Gegensatz zu Pandas können Sie also einfach Ihre Verarbeitungsschritte definieren und Dask die Arbeit erledigen lassen. Dask wendet die Berechnungen nicht an, bevor sie explizit von computeund / oder gepusht werden persist(siehe die Antwort hier für den Unterschied).

Andere Hilfsmittel (Ideen)

  • ETL-Fluss für die Daten ausgelegt. Behalten Sie nur das bei, was aus den Rohdaten benötigt wird.
    • Wenden Sie zunächst ETL auf ganze Daten mit Frameworks wie Dask oder PySpark an und exportieren Sie die verarbeiteten Daten.
    • Überprüfen Sie dann, ob die verarbeiteten Daten in den gesamten Speicher passen.
  • Erwägen Sie, Ihren RAM zu erhöhen.
  • Arbeiten Sie mit diesen Daten auf einer Cloud-Plattform.

0

Zusätzlich zu den obigen Antworten ist d6tstack eine weitere gute Option für diejenigen, die CSV verarbeiten und dann nach CSV, Parkett oder SQL exportieren möchten . Sie können mehrere Dateien laden und es werden Datenschemaänderungen (hinzugefügte / entfernte Spalten) behandelt. Chunked out of Core-Unterstützung ist bereits eingebaut.

def apply(dfg):
    # do stuff
    return dfg

c = d6tstack.combine_csv.CombinerCSV([bigfile.csv], apply_after_read=apply, sep=',', chunksize=1e6)

# or
c = d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), apply_after_read=apply, chunksize=1e6)

# output to various formats, automatically chunked to reduce memory consumption
c.to_csv_combine(filename='out.csv')
c.to_parquet_combine(filename='out.pq')
c.to_psql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # fast for postgres
c.to_mysql_combine('mysql+mysqlconnector://usr:pwd@localhost/db', 'tablename') # fast for mysql
c.to_sql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # slow but flexible

0

Für den Fall, dass noch jemand nach so etwas sucht, habe ich festgestellt, dass diese neue Bibliothek namens modin helfen kann. Es verwendet verteiltes Computing, das beim Lesen helfen kann. Hier ist ein schöner Artikel , der seine Funktionalität mit Pandas vergleicht. Es verwendet im Wesentlichen die gleichen Funktionen wie Pandas.

import modin.pandas as pd
pd.read_csv(CSV_FILE_NAME)

Können Sie kommentieren, wie dieses neue Modul modinmit dem etablierten verglichen wird dask.dataframe? Siehe beispielsweise Wechsel von Pandas zu Dask, um alle lokalen CPU-Kerne zu nutzen .
jpp

0

Bevor Sie die Option chunksize verwenden möchten, können Sie einfach die Option nrows verwenden, wenn Sie sicher sein möchten, welche Prozessfunktion Sie in die von @unutbu erwähnte Chunking-for-Schleife schreiben möchten.

small_df = pd.read_csv(filename, nrows=100)

Sobald Sie sicher sind, dass der Prozessblock bereit ist, können Sie ihn in die Chunking-for-Schleife für den gesamten Datenrahmen einfügen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.