Remplacement des valeurs vides (espace blanc) par NaN dans les pandas

152

Je veux trouver toutes les valeurs dans un dataframe Pandas qui contiennent des espaces (n'importe quel montant arbitraire) et remplacer ces valeurs par NaNs.

Des idées sur la façon dont cela peut être amélioré?

Fondamentalement, je veux tourner ceci:

                   A    B    C
2000-01-01 -0.532681  foo    0
2000-01-02  1.490752  bar    1
2000-01-03 -1.387326  foo    2
2000-01-04  0.814772  baz     
2000-01-05 -0.222552         4
2000-01-06 -1.176781  qux     

Dans ceci:

                   A     B     C
2000-01-01 -0.532681   foo     0
2000-01-02  1.490752   bar     1
2000-01-03 -1.387326   foo     2
2000-01-04  0.814772   baz   NaN
2000-01-05 -0.222552   NaN     4
2000-01-06 -1.176781   qux   NaN

J'ai réussi à le faire avec le code ci-dessous, mais c'est moche. Ce n'est pas Pythonic et je suis sûr que ce n'est pas non plus l'utilisation la plus efficace des pandas. Je boucle sur chaque colonne et effectue un remplacement booléen par rapport à un masque de colonne généré en appliquant une fonction qui effectue une recherche regex de chaque valeur, correspondant sur un espace blanc.

for i in df.columns:
    df[i][df[i].apply(lambda i: True if re.search('^\s*$', str(i)) else False)]=None

Il pourrait être optimisé un peu en itérant uniquement dans les champs qui pourraient contenir des chaînes vides:

if df[i].dtype == np.dtype('object')

Mais ce n'est pas vraiment une amélioration

Et enfin, ce code définit les chaînes cibles sur None, ce qui fonctionne avec les fonctions de Pandas comme fillna(), mais ce serait bien pour l'exhaustivité si je pouvais réellement insérer un NaNdirectement au lieu de None.

Chris Clark
la source
2
Ce que vous voulez vraiment, c'est pouvoir utiliser replaceavec une regex ... (peut-être que cela devrait être demandé en tant que fonctionnalité).
Andy Hayden
3
J'ai créé un problème github pour cette fonctionnalité: github.com/pydata/pandas/issues/2285 . Serait reconnaissant pour les PR! :)
Chang She
Pour ceux qui veulent transformer exactement un seul caractère vide en manquant, voyez cette solution simple ci
Ted Petrou

Réponses:

200

Je pense que df.replace()fait le travail, depuis pandas 0.13 :

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

# replace field that's entirely space (or empty) with NaN
print(df.replace(r'^\s*$', np.nan, regex=True))

Produit:

                   A    B   C
2000-01-01 -0.532681  foo   0
2000-01-02  1.490752  bar   1
2000-01-03 -1.387326  foo   2
2000-01-04  0.814772  baz NaN
2000-01-05 -0.222552  NaN   4
2000-01-06 -1.176781  qux NaN

Comme Temak l'a souligné, utilisez-le df.replace(r'^\s+$', np.nan, regex=True)si vos données valides contiennent des espaces blancs.

patricksurry
la source
1
regex est un indicateur booléen. Peut-être que vous voulez dire pd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).valuesce qui donne['1', 'X', '9', 'X', None]
patricksurry
2
2 ans plus tard, j'ai changé la réponse acceptée à cela, maintenant que les pandas la soutiennent. Merci!
Chris Clark
35
REMARQUE : si vous ne voulez pas qu'un élément contenant un espace au milieu soit remplacé par NaN, utilisezdf.replace(r'^\s+$', np.nan, regex=True)
Temak
7
J'ai essayé de l'utiliser, mais j'ai découvert que r '^ \ s * $' devrait être l'expression à utiliser. sans ^ et $ il correspondra à n'importe quelle chaîne avec deux blancs consécutifs. Également changé + en * pour inclure la chaîne vide "" dans la liste des choses à convertir en NaN
Master Yogurt
1
J'essaye votre solution dans mon code, mais cela n'a aucun effet. J'essaye "énergie [" Approvisionnement en énergie "]. Replace (to_replace =" ... ", valeur = np.NaN)". Vous voulez changer la chaîne "..." en valeurs NaN, mais cela ne fait rien et renvoie la même trame de données.
Archan Joshi
50

Si vous souhaitez remplacer une chaîne vide et des enregistrements avec uniquement des espaces, la bonne réponse est !:

df = df.replace(r'^\s*$', np.nan, regex=True)

La réponse acceptée

df.replace(r'\s+', np.nan, regex=True)

Ne remplace pas une chaîne vide !, vous pouvez essayer vous-même avec l'exemple donné légèrement mis à jour:

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'fo o', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', ''],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

Notez également que «fo o» n'est pas remplacé par Nan, bien qu'il contienne un espace. Notez en outre, qu'un simple:

df.replace(r'', np.NaN)

Ne fonctionne pas non plus - essayez-le.

Philipp Schwarz
la source
33

Que diriez-vous:

d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

La applymapfonction applique une fonction à chaque cellule du dataframe.

BrenBarn
la source
Quelle belle amélioration! J'aurais dû y penser rétrospectivement, mais je me suis accroché à faire des remplacements booléens pour une raison quelconque. Une question - y a-t-il un avantage à faire la vérification de la chaîne de base par rapport à seulement str (x) .isspace ()?
Chris Clark
1
@ChrisClark: L'un ou l'autre est bien, même si je suppose que ce isinstancesera un peu plus rapide.
BrenBarn
13
La référence à "basestring" dans le code ci-dessus ne fonctionnera pas dans Python 3 .... dans ce cas, essayez d'utiliser "str" ​​à la place.
Spike Williams
4
Notez que cette solution ne remplace pas les chaînes vides ''. Pour considérer également les chaînes vides, utilisez:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x)
tuomastik
18

Je vais faire ceci:

df = df.apply(lambda x: x.str.strip()).replace('', np.nan)

ou

df = df.apply(lambda x: x.str.strip() if isinstance(x, str) else x).replace('', np.nan)

Vous pouvez supprimer tout str, puis remplacer str vide par np.nan.

Xiaorong Liao
la source
lambda x: x.str.strip () doit être lambda x: x.strip ()? suggestion mineure: ajoutez .astype (str) devant, cela résout d'autres problèmes de données pour moi. Cela fonctionne pour moi: df = df.apply ['column']. Astype (str) .apply (lambda x: x.strip ()). Replace ('', np.nan)
Wouter
La deuxième ligne de code gère à la fois les colonnes de type int / float et string. Agréable. Tks!
Kate Stohr
6

La plus simple de toutes les solutions:

df = df.replace(r'^\s+$', np.nan, regex=True)
Gil Baggio
la source
5

Si vous exportez les données du fichier CSV, cela peut être aussi simple que ceci:

df = pd.read_csv(file_csv, na_values=' ')

Cela créera la trame de données et remplacera les valeurs vides comme Na

ibrahim rupawala
la source
2
Une autre option .. utiliser skipinitialspace=Truesupprime également tout espace après le délimiteur, ce qui entraînerait la lecture de n'importe quelle longueur d'espace blanc, les chaînes vides nan. Cependant, si vous souhaitez conserver les espaces initiaux pour une raison quelconque, cette option n'est pas un bon choix.
Rajshekar Reddy
1
@RajshekarReddy pouvez-vous s'il vous plaît mettre ceci comme réponse quelque part, c'était génial!
User2321
2

Pour une solution très rapide et simple où vous vérifiez l'égalité par rapport à une valeur unique, vous pouvez utiliser la maskméthode.

df.mask(df == ' ')
Ted Petrou
la source
1

Ce sont tous proches de la bonne réponse, mais je ne dirais pas que tout résoudra le problème tout en restant plus lisible pour les autres lisant votre code. Je dirais que cette réponse est une combinaison de la réponse de BrenBarn et du commentaire de tuomasttik sous cette réponse . La réponse de isspaceBrenBarn utilise intégré, mais ne prend pas en charge la suppression des chaînes vides, comme OP l'a demandé, et j'aurais tendance à attribuer cela comme le cas d'utilisation standard du remplacement des chaînes par null.

Je l'ai réécrit avec .apply, donc vous pouvez l'appeler sur un pd.Seriesou pd.DataFrame.


Python 3:

Pour remplacer des chaînes vides ou des chaînes entièrement d'espaces:

df = df.apply(lambda x: np.nan if isinstance(x, str) and (x.isspace() or not x) else x)

Pour remplacer des chaînes entièrement d'espaces:

df = df.apply(lambda x: np.nan if isinstance(x, str) and x.isspace() else x)

Pour l'utiliser dans Python 2, vous devrez le remplacer strpar basestring.

Python 2:

Pour remplacer des chaînes vides ou des chaînes entièrement d'espaces:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and (x.isspace() or not x) else x)

Pour remplacer des chaînes entièrement d'espaces:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)
spen.smith
la source
1

Cela a fonctionné pour moi. Lorsque j'importe mon fichier csv, j'ai ajouté na_values ​​= ''. Les espaces ne sont pas inclus dans les valeurs NaN par défaut.

df = pd.read_csv (chemin du fichier, na_values ​​= '')

Sambrowne
la source
0

vous pouvez également utiliser un filtre pour le faire.

df = PD.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '])
    df[df=='']='nan'
    df=df.astype(float)
ERIC
la source
Chaque ligne de ce code (sans compter les données) est défectueuse.
Julius
0
print(df.isnull().sum()) # check numbers of null value in each column

modifiedDf=df.fillna("NaN") # Replace empty/null values with "NaN"

# modifiedDf = fd.dropna() # Remove rows with empty values

print(modifiedDf.isnull().sum()) # check numbers of null value in each column
Jayantha
la source
0

Ce n'est pas une solution élégante, mais ce qui semble fonctionner, c'est d'enregistrer sur XLSX, puis de le réimporter. Les autres solutions sur cette page n'ont pas fonctionné pour moi, je ne sais pas pourquoi.

data.to_excel(filepath, index=False)
data = pd.read_excel(filepath)
David Kong
la source