web-dev-qa-db-fra.com

Remplacer les valeurs invalides par None dans Pandas DataFrame

Existe-t-il une méthode pour remplacer les valeurs par None dans Pandas en Python?

Vous pouvez utiliser df.replace('pre', 'post') et remplacer une valeur par une autre, mais cela ne peut pas être fait si vous souhaitez remplacer par la valeur None. Si vous essayez, vous obtenez un résultat étrange.

Alors, voici un exemple:

df = DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df.replace('-', 0)

ce qui retourne un résultat réussi.

Mais,

df.replace('-', None)

qui retourne un résultat suivant:

0
0   - // this isn't replaced
1   3
2   2
3   5
4   1
5  -5
6  -1
7  -1 // this is changed to `-1`...
8   9

Pourquoi un résultat aussi étrange est-il renvoyé?

Puisque je veux verser ce cadre de données dans la base de données MySQL, je ne peux pas mettre de valeurs NaN dans aucun élément de mon cadre de données, mais plutôt mettre None. Vous pouvez sûrement d'abord changer '-' to NaN puis convertissez NaN en None, mais je veux savoir pourquoi le cadre de données agit de manière si terrible.

Testé sur pandas 0.12.0 dev Python 2.7 et OS X 10.8. Python est une version préinstallée sur OS X et moi avons installé pandas en utilisant le script SciPy Superpack, pour votre information.

64
Blaszard

En fait, dans les versions ultérieures de pandas, cela donnera une erreur TypeError:

df.replace('-', None)
TypeError: If "to_replace" and "value" are both None then regex must be a mapping

Vous pouvez le faire en passant soit une liste, soit un dictionnaire:

In [11]: df.replace('-', df.replace(['-'], [None]) # or .replace('-', {0: None})
Out[11]:
      0
0  None
1     3
2     2
3     5
4     1
5    -5
6    -1
7  None
8     9

Mais je recommande d'utiliser NaNs plutôt que None:

In [12]: df.replace('-', np.nan)
Out[12]:
     0
0  NaN
1    3
2    2
3    5
4    1
5   -5
6   -1
7  NaN
8    9
88
Andy Hayden

where est probablement ce que vous recherchez. Alors

data=data.where(data=='-', None) 

De la panda docs :

where [renvoie] un objet de même forme que self et dont les entrées correspondantes proviennent de self où cond est True et sinon, elles proviennent d'autres.

14
user2966041

Je préfère la solution utilisant replace avec un dict en raison de sa simplicité et de son élégance:

df.replace({'-': None})

Vous pouvez également avoir plus de remplaçants:

df.replace({'-': None, 'None': None})

Et même pour les gros remplaçants, ce qui est remplacé par ce qui est bien plus difficile pour de longues listes, à mon avis, est toujours évident et clair.

11
Michael Dorner

Avant de continuer avec ce post, il est important de comprendre la différence entre NaN et None . L'un est un type float, l'autre est un type d'objet. Pandas est plus approprié pour travailler avec des types scalaires car de nombreuses méthodes sur ces types peuvent être vectorisées. Pandas essaie de gérer None et NaN de manière cohérente, mais NumPy ne le peut pas. .

Ma suggestion ( et Andy ) est de rester avec NaN.

(v0.24 +) Une meilleure solution pour les données CSV/Excel: na_values=['-']

Si vous avez chargé ces données à partir de CSV/Excel, j'ai de bonnes nouvelles pour vous. Vous pouvez annuler cela à la racine lors du chargement des données au lieu de devoir écrire un correctif avec du code à l'étape suivante.

La plupart pd.read_* _ fonctions (telles que read_csv et read_Excel ) accepte un na_values attribut.

file.csv

A,B
-,1
3,-
2,-
5,3
1,-2
-5,4
-1,-1
-,0
9,0

Maintenant, pour convertir le - caractères dans NaNs,

import pandas as pd
df = pd.read_csv('file.csv', na_values=['-'])
df

     A    B
0  NaN  1.0
1  3.0  NaN
2  2.0  NaN
3  5.0  3.0
4  1.0 -2.0
5 -5.0  4.0
6 -1.0 -1.0
7  NaN  0.0
8  9.0  0.0

Et similaire pour d'autres fonctions/formats de fichiers.

P.S .: Sur la version 0.24 +, vous pouvez conserver le type d’entier même si votre colonne contient des NaN (oui, parlez d’avoir le gâteau et de le manger aussi). Vous pouvez spécifier dtype='Int32'

df = pd.read_csv('file.csv', na_values=['-'], dtype='Int32')
df

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

df.dtypes

A    Int32
B    Int32
dtype: object

Le type n'est pas un type int conventionnel ... mais plutôt un Type entier Nullable. Il existe d'autres options.


Traitement des données numériques: pd.to_numeric avec errors='coerce

Si vous utilisez des données numériques, une solution plus rapide consiste à utiliser pd.to_numeric avec le errors='coerce' argument qui convertit les valeurs non valides (valeurs impossibles à convertir en numérique) en NaN.

pd.to_numeric(df['A'], errors='coerce')

0    NaN
1    3.0
2    2.0
3    5.0
4    1.0
5   -5.0
6   -1.0
7    NaN
8    9.0
Name: A, dtype: float64

Pour conserver un type entier (nullable), utilisez

pd.to_numeric(df['A'], errors='coerce').astype('Int32')

0    NaN
1      3
2      2
3      5
4      1
5     -5
6     -1
7    NaN
8      9
Name: A, dtype: Int32 

Pour contraindre plusieurs colonnes, utilisez apply:

df[['A', 'B']].apply(pd.to_numeric, errors='coerce').astype('Int32')

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

... et assigne le résultat après.

Plus d'informations peuvent être trouvées dans cette réponse .

3
cs95
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df = df.where(df!='-', None)
2
Shravan kp

La définition de valeurs nulles peut être faite avec np.nan:

import numpy as np
df.replace('-', np.nan)

L'avantage est que df.last_valid_index() les reconnaît comme non valides.

0
Freek Wiekmeijer