119 votes

Remplacer les valeurs invalides par None dans un DataFrame Pandas

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

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

Voici donc un exemple :

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

qui renvoie un résultat positif.

Mais,

df.replace('-', None)

qui renvoie le 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é ?

Comme je veux verser ce cadre de données dans une base de données MySQL, je ne peux pas mettre NaN dans n'importe quel élément de mon cadre de données et je veux plutôt mettre None . Sûrement, vous pouvez d'abord changer '-' a NaN et ensuite convertir NaN a None mais je veux savoir pourquoi le dataframe se comporte d'une manière si terrible.

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

141voto

Andy Hayden Points 38010

En fait, dans les versions ultérieures de pandas, cela donne une 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 des NaNs plutôt que des 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

36voto

Michael Dorner Points 730

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 remplacements plus importants, il est toujours évident et clair de savoir ce qui est remplacé par quoi - ce qui est beaucoup plus difficile pour les longues listes, à mon avis.

18voto

user2966041 Points 437

where est probablement ce que vous recherchez. Alors

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

De la docs panda :

where [renvoie] un objet de même forme que self et dont les entrées correspondantes sont issues de self lorsque cond est True et sinon sont issues de other).

12voto

coldspeed Points 111053

Avant de continuer avec ce post, il est important de comprendre la différence entre NaN et None . L'un est un type flottant, l'autre un type objet. Pandas est mieux adapté pour travailler avec des types scalaires car de nombreuses méthodes de 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 celle d'Andy ) est de s'en tenir à NaN.

Mais pour répondre à votre question...

pandas >= 0.18 : Utilisation de na_values=['-'] argument avec read_csv

Si vous avez chargé ces données à partir de CSV/Excel, j'ai de bonnes nouvelles pour vous. Vous pouvez éliminer ce problème à la racine, pendant le chargement des données, au lieu de devoir écrire un correctif avec du code dans une étape ultérieure.

La plupart des pd.read_* fonctions (telles que read_csv y read_excel ) accepter 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 - en NaNs, faire,

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 v0.24+, vous pouvez préserver le type entier même si votre colonne contient des NaNs (oui, c'est comme avoir le gâteau et 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 dtype n'est pas un type int conventionnel... mais plutôt un type Nullable Integer Type. Il existe d'autres options.


Manipulation de données numériques : pd.to_numeric con errors='coerce

Si vous traitez des données numériques, une solution plus rapide est d'utiliser pd.to_numeric avec le errors='coerce' qui convertit les valeurs invalides (valeurs qui ne peuvent pas être converties 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 le dtype integer (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 de réaffecter le résultat après.

Vous trouverez de plus amples informations dans cette réponse .

8voto

A-B-B Points 797

Avec Pandas version 1.0.0, j'utiliserais DataFrame.replace o Series.replace :

df.replace(old_val, pd.NA, inplace=True)

C'est mieux pour deux raisons :

  1. Il utilise pd.NA au lieu de None o np.nan .
  2. En option, il fonctionne en place qui pourrait être plus efficace en termes de mémoire selon l'implémentation interne.

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