Pandas read_csv low_memory et dtype options

320

En appelant

df = pd.read_csv('somefile.csv')

Je reçois:

/Users/josh/anaconda/envs/py27/lib/python2.7/site-packages/pandas/io/parsers.py:1130: DtypeWarning: les colonnes (4, 5, 7, 16) ont des types mixtes. Spécifiez l'option dtype lors de l'importation ou définissez low_memory = False.

Pourquoi l' dtypeoption est-elle liée à low_memory, et pourquoi serait-il Falseutile de résoudre ce problème?

Josh
la source
2
J'ai une question à propos de cet avertissement. L'index des colonnes mentionnées est-il basé sur 0? Par exemple, la colonne 4 qui a un type mixte, est celle df [:, 4] ou df [:, 3]
maziar
@maziar lors de la lecture d'un csv, par défaut un nouvel index basé sur 0 est créé et utilisé.
firelynx

Réponses:

433

L'option low_memory déconseillée

L' low_memoryoption n'est pas correctement déconseillée, mais elle devrait l'être, car elle ne fait rien de différent [ source ]

La raison pour laquelle vous obtenez cet low_memoryavertissement est que deviner les dtypes pour chaque colonne est très gourmand en mémoire. Pandas essaie de déterminer le type de fichier à définir en analysant les données de chaque colonne.

Dtype Guessing (très mauvais)

Les pandas ne peuvent déterminer le type de dt d'une colonne qu'une fois que le fichier entier est lu. Cela signifie que rien ne peut vraiment être analysé avant la lecture de l'ensemble du fichier, sauf si vous risquez d'avoir à modifier le type de dt de cette colonne lorsque vous lisez la dernière valeur.

Prenons l'exemple d'un fichier qui a une colonne appelée user_id. Il contient 10 millions de lignes où user_id est toujours des nombres. Étant donné que les pandas ne peuvent pas savoir qu'il ne s'agit que de chiffres, ils le conserveront probablement comme les chaînes d'origine jusqu'à ce qu'il ait lu l'intégralité du fichier.

Spécification de dtypes (devrait toujours être fait)

ajouter

dtype={'user_id': int}

à l' pd.read_csv()appel fera savoir aux pandas quand il commencera à lire le fichier, qu'il ne s'agit que d'entiers.

Il convient également de noter que si la dernière ligne du fichier avait "foobar"écrit dans la user_idcolonne, le chargement se bloquerait si le type ci-dessus était spécifié.

Exemple de données cassées qui se cassent lorsque les dtypes sont définis

import pandas as pd
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO


csvdata = """user_id,username
1,Alice
3,Bob
foobar,Caesar"""
sio = StringIO(csvdata)
pd.read_csv(sio, dtype={"user_id": int, "username": "string"})

ValueError: invalid literal for long() with base 10: 'foobar'

Les dtypes sont généralement une chose numpy, en savoir plus à leur sujet ici: http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html

Quels dtypes existent?

Nous avons accès à dtypes numpy: float, int, bool, timedelta64 [ns] et datetime64 [ns]. Notez que les dtypes numpy date / time ne sont pas sensibles au fuseau horaire.

Pandas étend cet ensemble de dtypes avec les siens:

'datetime64 [ns,]' qui est un horodatage prenant en compte le fuseau horaire.

'catégorie' qui est essentiellement une énumération (chaînes représentées par des clés entières pour enregistrer

'period []' À ne pas confondre avec un timedelta, ces objets sont en fait ancrés à des périodes de temps spécifiques

'Sparse', 'Sparse [int]', 'Sparse [float]' est pour les données clairsemées ou 'Les données qui ont beaucoup de trous' Au lieu d'enregistrer le NaN ou Aucun dans la trame de données, il omet les objets, économisant de l'espace .

«Intervalle» est un sujet qui lui est propre, mais son utilisation principale est pour l'indexation. Voir plus ici

'Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64' sont tous des entiers spécifiques au pandas qui peuvent être annulés, contrairement à la variante numpy.

'chaîne' est un type spécifique pour travailler avec des données de chaîne et donne accès à l' .strattribut de la série.

«booléen» est comme le «booléen» numpy mais il prend également en charge les données manquantes.

Lisez la référence complète ici:

Pandas dtype reference

Gotchas, mises en garde, notes

Le réglage dtype=objectmettra en sourdine l'avertissement ci-dessus, mais ne le rendra pas plus efficace en mémoire, il ne fera qu'effectuer un processus efficace.

Le réglage dtype=unicodene fera rien, car pour numpy, a unicodeest représenté par object.

Utilisation de convertisseurs

@sparrow souligne correctement l'utilisation de convertisseurs pour éviter que les pandas explosent lorsqu'ils se rencontrent 'foobar'dans une colonne spécifiée comme int. Je voudrais ajouter que les convertisseurs sont vraiment lourds et inefficaces à utiliser dans les pandas et devraient être utilisés en dernier recours. En effet, le processus read_csv est un processus unique.

Les fichiers CSV peuvent être traités ligne par ligne et peuvent ainsi être traités par plusieurs convertisseurs en parallèle plus efficacement en coupant simplement le fichier en segments et en exécutant plusieurs processus, ce que les pandas ne prennent pas en charge. Mais c'est une autre histoire.

firelynx
la source
6
Donc, étant donné que la définition d'un dtype=objectn'est pas plus efficace en mémoire, y a-t-il une raison de gâcher cela en plus de se débarrasser de l'erreur?
zthomas.nc
6
@ zthomas.nc oui, Pandas n'a pas besoin de se préoccuper de tester ce qui est dans la colonne. Sauvegarde théorique de la mémoire pendant le chargement (mais aucune après le chargement) et sauvegarde théorique de certains cycles de processeur (ce que vous ne remarquerez pas car les E / S disque seront le goulot d'étranglement.)
firelynx
5
"Il convient également de noter que si la dernière ligne du fichier avait" foobar "écrit dans la colonne user_id, le chargement se bloquerait si le type ci-dessus était spécifié." existe-t-il une option de «contrainte» qui pourrait être utilisée pour jeter cette ligne au lieu de planter?
moineau
5
@sparrow il peut y en avoir, mais la dernière fois que je l'ai utilisé, il y avait des bugs. Il peut être corrigé dans la dernière version de pandas. error_bad_lines=False, warn_bad_lines=Truedevrait faire l'affaire. La documentation indique qu'elle n'est valable qu'avec l'analyseur C. Il indique également que l'analyseur par défaut est None, ce qui rend difficile de savoir lequel est celui par défaut.
firelynx
5
@nealmcb Vous pouvez lire la trame de données avec nrows=100comme argument, puis faire df.dtypespour voir les dtypes que vous obtenez. Cependant, lors de la lecture de l'ensemble de la trame de données avec ces dtypes, assurez-vous de le faire try/exceptafin de détecter les suppositions de dtype défectueuses. Les données sont sales, vous savez.
firelynx
50

Essayer:

dashboard_df = pd.read_csv(p_file, sep=',', error_bad_lines=False, index_col=False, dtype='unicode')

Selon la documentation des pandas:

dtype: Tapez le nom ou le dict de la colonne -> type

Quant à low_memory, il est vrai par défaut et n'est pas encore documenté. Je ne pense pas que ce soit pertinent. Le message d'erreur est générique, vous ne devriez donc pas avoir besoin de jouer avec low_memory de toute façon. J'espère que cela vous aide et faites-moi savoir si vous avez d'autres problèmes

hd1
la source
1
Ajout dtype=unicodeproduit: NameError: name 'unicode' is not defined. Mais mettre des unicodeguillemets (comme dans 'unicode') semble fonctionner!
sedeh
5
@sedeh Vous pouvez spécifier des dtypes en tant que types python ou en tant que numpy.dtype('unicode'). Lorsque vous donnez à l'option dtype une chaîne, il essaiera de la transtyper via l' numpy.dtype()usine par défaut. Spécifier 'unicode'ne fera rien du tout, les Unicodes sont juste transférés vers objects. Vous obtiendrezdtype='object'
firelynx
44
df = pd.read_csv('somefile.csv', low_memory=False)

Cela devrait résoudre le problème. J'ai eu exactement la même erreur lors de la lecture de 1,8 M de lignes à partir d'un CSV.

Neal
la source
51
Cela réduit l'erreur au silence, mais ne change rien d'autre.
firelynx
2
J'ai le même problème lors de l'exécution d'un fichier de données de 1,5 Go
Sitz Blogz
18

Comme mentionné précédemment par firelynx si dtype est explicitement spécifié et qu'il existe des données mixtes qui ne sont pas compatibles avec ce dtype, le chargement se bloquera. J'ai utilisé un convertisseur comme celui-ci comme solution de contournement pour modifier les valeurs avec un type de données incompatible afin que les données puissent toujours être chargées.

def conv(val):
    if not val:
        return 0    
    try:
        return np.float64(val)
    except:        
        return np.float64(0)

df = pd.read_csv(csv_file,converters={'COL_A':conv,'COL_B':conv})
moineau
la source
2

J'ai eu un problème similaire avec un fichier de ~ 400 Mo. Le réglage a low_memory=Falsefait l'affaire pour moi. Faites d'abord les choses simples, je vérifierais que votre dataframe n'est pas plus grand que la mémoire de votre système, redémarrez, videz la RAM avant de continuer. Si vous rencontrez toujours des erreurs, cela vaut la peine de vous assurer que votre .csvfichier est correct, jetez un coup d'œil dans Excel et assurez-vous qu'il n'y a pas de corruption évidente. Des données originales brisées peuvent faire des ravages ...

Dr Nigel
la source
1

J'étais confronté à un problème similaire lors du traitement d'un énorme fichier csv (6 millions de lignes). J'ai eu trois problèmes: 1. le fichier contenait des caractères étranges (corrigé à l'aide de l'encodage) 2. le type de données n'était pas spécifié (corrigé à l'aide de la propriété dtype) 3. En utilisant ce qui précède, je rencontrais toujours un problème lié au format_fichier qui ne pouvait pas être défini en fonction du nom de fichier (corrigé à l'aide de try .. sauf ..)

df = pd.read_csv(csv_file,sep=';', encoding = 'ISO-8859-1',
                 names=['permission','owner_name','group_name','size','ctime','mtime','atime','filename','full_filename'],
                 dtype={'permission':str,'owner_name':str,'group_name':str,'size':str,'ctime':object,'mtime':object,'atime':object,'filename':str,'full_filename':str,'first_date':object,'last_date':object})

try:
    df['file_format'] = [Path(f).suffix[1:] for f in df.filename.tolist()]
except:
    df['file_format'] = ''
Wim Folkerts
la source
-1

Cela a fonctionné pour moi low_memory = Falselors de l'importation d'un DataFrame. C'est tout le changement qui a fonctionné pour moi:

df = pd.read_csv('export4_16.csv',low_memory=False)
Rajat Saxena
la source