100 votes

Grand DataFrame persistant dans pandas

En tant qu'utilisateur de longue date de SAS, j'envisage de passer à python et à pandas.

Cependant, lors de l'exécution de certains tests aujourd'hui, j'ai été surpris de constater que python manquait de mémoire lorsqu'il essayait de pandas.read_csv() un fichier csv de 128mb. Il comportait environ 200 000 lignes et 200 colonnes de données essentiellement numériques.

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

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

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

0 votes

Je ne suis pas familier avec pandas, mais vous pourriez envisager d'itérer dans le fichier. pandas.pydata.org/pandas-docs/stable/

89voto

fickludd Points 507

Wes a bien sûr raison ! J'interviens simplement 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`

6 votes

Je pense que tu peux juste faire df = concate(tp, ignore_index=True) ?

0 votes

@smci J'ai essayé rapidement avec les mêmes données répétées x4 (550 Mo) ou x8 (1,1 Go). Il est intéressant de noter qu'avec ou sans [x pour x dans tp], la x4 s'est bien déroulée, et la x8 s'est écrasée dans une MemoryError.

0 votes

@fickludd Utilisez-vous ipython ? Le cadre de données persiste-t-il dans la mémoire plutôt que d'être collecté ?

84voto

Wes McKinney Points 17545

En principe, il ne devrait pas manquer de mémoire, mais il y a actuellement des problèmes de mémoire avec read_csv sur les gros fichiers, en raison de problèmes internes complexes de Python (c'est vague, mais c'est connu depuis longtemps) : http://github.com/pydata/pandas/issues/407 ).

Pour l'instant, il n'y a pas de solution parfaite (voici une solution fastidieuse : vous pourriez transcrire le fichier ligne par ligne dans un tableau NumPy préalloué ou un fichier mappé en mémoire ). np.mmap ), mais c'est un sujet sur lequel je vais travailler dans un avenir proche. Une autre solution consiste à lire le fichier en plus petits morceaux (utiliser la fonction iterator=True, chunksize=1000 ) puis concaténer ensuite avec pd.concat . Le problème se pose lorsque l'on met en mémoire l'ensemble du fichier texte en une seule fois.

1 votes

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 j'ai de l'espace sur le disque dur. Est-ce la même chose avec les DataFrames ? J'ai l'impression qu'ils sont limités par la RAM et non par l'espace sur le disque dur. Désolé pour cette question de débutant et merci pour votre aide. J'apprécie votre livre.

4 votes

Exact, vous êtes limité par la RAM. SAS offre en effet une bien meilleure prise en charge du traitement des données volumineuses " hors cœur ".

5 votes

@WesMcKinney Ces solutions de contournement ne devraient plus être nécessaires, grâce au nouveau chargeur csv que vous avez installé dans la 0.10, n'est-ce pas ?

44voto

Il s'agit d'un ancien fil de discussion, mais je voulais simplement déposer ma solution de contournement ici. J'ai d'abord essayé le chunksize (même avec des valeurs assez petites comme 10000), mais cela n'a pas aidé beaucoup ; j'avais encore des problèmes techniques avec la taille de la mémoire (mon CSV était ~ 7.5 Gb).

Pour l'instant, je lis simplement des morceaux de fichiers CSV dans une approche for-loop et je les ajoute, 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()

4 votes

Super utile de voir un cas d'utilisation réaliste pour la fonctionnalité de lecture par morceaux. Merci.

6 votes

Juste une petite remarque, sur ce vieux sujet : pandas.read_csv renvoie directement (du moins sur la version que j'utilise actuellement) un itérateur si vous fournissez simplement le paramètre iterator=True et chunksize=chunksize . Par conséquent, il suffit de faire un for boucle sur le pd.read_csv au lieu de le réinstancier à chaque fois. Cependant, cela ne coûte que l'overhead de l'appel, il n'y a peut-être pas d'impact significatif.

1 votes

Salut, Joel. Merci pour la note ! Le site iterator=True et chunksize Les paramètres existaient déjà à l'époque si je me souviens bien. Peut-être qu'il y avait un bogue dans une version antérieure qui a causé l'explosion de la mémoire - je vais essayer à nouveau la prochaine fois que je lirai un grand DataFrame dans Pandas (j'utilise principalement Blaze maintenant pour de telles tâches).

6voto

W-B Points 94428

Voici mon processus 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, il est préférable d'optimiser la taille des morceaux.

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

Une fois que toutes les données sont dans la base, vous pouvez interroger celles dont vous avez besoin dans la base.

4voto

user8108173 Points 41

Si vous voulez charger d'énormes fichiers csv, dask pourrait être une bonne option. Il imite l'API de pandas, ce qui le rend très similaire à pandas.

lien vers dask sur github

0 votes

Merci, depuis que j'ai posté ceci, j'ai utilisé dask et le format parquet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X