DataFrame volumineux et persistant dans les pandas

91

J'explore le passage à python et aux pandas en tant qu'utilisateur SAS de longue date.

Cependant, lors de l'exécution de certains tests aujourd'hui, j'ai été surpris que python soit à court de mémoire lors de la tentative d' pandas.read_csv()un fichier csv de 128 Mo. Il contenait environ 200 000 lignes et 200 colonnes de données principalement numériques.

Avec SAS, je peux importer un fichier csv dans un ensemble de données SAS et il peut être aussi grand que mon disque dur.

Y a-t-il quelque chose d'analogue pandas?

Je travaille régulièrement avec des fichiers volumineux et je n'ai pas accès à un réseau informatique distribué.

Zelazny7
la source
Je ne suis pas familier avec les pandas, mais vous voudrez peut-être parcourir le fichier. pandas.pydata.org/pandas-docs/stable/…
monkut

Réponses:

79

En principe, il ne devrait pas manquer de mémoire, mais il y a actuellement des problèmes de mémoire avec read_csvdes fichiers volumineux causés par des problèmes internes complexes de Python (c'est vague mais on le sait depuis longtemps: http://github.com/pydata / pandas / issues / 407 ).

Pour le moment, il n'y a pas de solution parfaite (en voici une fastidieuse: vous pouvez transcrire le fichier ligne par ligne dans un tableau NumPy pré-alloué ou un fichier mappé en mémoire-- np.mmap), mais c'est celui que je vais travailler dans un proche avenir. Une autre solution est de lire le fichier en petits morceaux (utiliser iterator=True, chunksize=1000) puis de concaténer ensuite avec pd.concat. Le problème survient lorsque vous extrayez le fichier texte entier en mémoire en un seul gros slurp.

Wes McKinney
la source
1
Disons que je peux lire le fichier et les concaténer tous ensemble dans un DataFrame. Le DataFrame doit-il résider en mémoire? Avec SAS, je peux travailler avec des ensembles de données de n'importe quelle taille tant que je dispose de l'espace disque dur. Est-ce la même chose avec DataFrames? J'ai l'impression qu'ils sont limités par la RAM et non par l'espace disque. Désolé pour la question noob et merci pour votre aide. J'apprécie votre livre.
Zelazny7
3
Oui, vous êtes limité par la RAM. SAS a en effet un bien meilleur support pour le traitement de Big Data "out-of-core".
Wes McKinney
5
@WesMcKinney Ces solutions de contournement ne devraient plus être nécessaires, à cause du nouveau chargeur csv que vous avez atterri en 0.10, non?
Gabriel Grant
79

Wes a bien sûr raison! Je suis juste en train de sonner pour fournir un exemple de code un peu plus complet. J'ai eu le même problème avec un fichier de 129 Mo, qui a été résolu par:

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
la source
6
Je pense que tu peux le faire df = concate(tp, ignore_index=True)?
Andy Hayden
@smci J'ai essayé cela rapidement avec les mêmes données répétées x4 (550 Mo) ou x8 (1,1 Go). Fait intéressant, avec ou sans [x for x in tp], le x4 s'est bien passé et x8 s'est écrasé dans une MemoryError.
fickludd
3
Je reçois cette erreur lors de son utilisation: AssertionError: first argument must be a list-like of pandas objects, you passed an object of type "TextFileReader". Une idée de ce qui se passe ici?
Prince Kumar
3
Ce bogue sera corrigé dans la version 0.14 (bientôt disponible), github.com/pydata/pandas/pull/6941 ; solution de contournement pour <0.14.0 est à fairepd.concat(list(tp), ignore_index=True)
Jeff
1
et si les valeurs sont des chaînes ou catégoriques - j'obtiens l'erreur: catégories incompatibles dans le concat catégorique
As3adTintin
41

Il s'agit d'un thread plus ancien, mais je voulais juste vider ma solution de contournement ici. J'ai d'abord essayé le chunksizeparamètre (même avec des valeurs assez petites comme 10000), mais cela n'a pas beaucoup aidé; avait encore des problèmes techniques avec la taille de la mémoire (mon CSV était ~ 7,5 Go).

Pour le moment, je viens de lire des morceaux des fichiers CSV dans une approche en boucle for et de les ajouter, par exemple, à une base de données SQLite étape par étape:

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

la source
4
Super utile pour voir un cas d'utilisation réaliste de la fonction de lecture fragmentée. Merci.
Alex Kestner
5
Juste une petite remarque, sur ce vieux sujet: pandas.read_csvrenvoie directement (du moins sur la version que j'utilise actuellement) un itérateur si vous fournissez simplement iterator=Trueet chunksize=chunksize. Par conséquent, vous feriez simplement une forboucle sur l' pd.read_csvappel, au lieu de le ré-instancier à chaque fois. Cependant, cela ne coûte que les frais généraux d'appel, il n'y a peut-être pas d'impact significatif.
Joël
1
Salut, Joel. Merci pour la note! Les paramètres iterator=Trueet chunksizeexistaient déjà à l'époque si je me souviens bien. Peut-être qu'il y avait un bogue dans une version plus ancienne qui a provoqué une explosion de la mémoire - je vais essayer de nouveau la prochaine fois que je lis un grand DataFrame dans Pandas (j'utilise principalement Blaze maintenant pour de telles tâches)
6

Voici mon flux de travail.

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

En fonction de la taille de votre fichier, vous feriez mieux d'optimiser la taille des morceaux.

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

Après avoir toutes les données dans la base de données, vous pouvez rechercher celles dont vous avez besoin à partir de la base de données.

BEN_YO
la source
3

Si vous souhaitez charger d'énormes fichiers csv, dask peut être une bonne option. Il imite l'api de pandas, donc il se sent assez similaire aux pandas

lien vers dask sur github

user8108173
la source
Merci, depuis que j'ai posté ceci, j'utilise dask et le format parquet.
Zelazny7
1

Vous pouvez utiliser Pytable plutôt que pandas df. Il est conçu pour les grands ensembles de données et le format de fichier est en hdf5. Le temps de traitement est donc relativement rapide.

Elm662
la source