Comment remplacer NaNs par des valeurs précédentes dans pandas DataFrame?

141

Supposons que j'ai un DataFrame avec des NaNs:

>>> import pandas as pd
>>> df = pd.DataFrame([[1, 2, 3], [4, None, None], [None, None, 9]])
>>> df
    0   1   2
0   1   2   3
1   4 NaN NaN
2 NaN NaN   9

Ce que je dois faire est de remplacer chaque NaNpar la première non- NaNvaleur dans la même colonne au-dessus. On suppose que la première ligne ne contiendra jamais de fichier NaN. Donc, pour l'exemple précédent, le résultat serait

   0  1  2
0  1  2  3
1  4  2  3
2  4  2  9

Je peux simplement parcourir l'ensemble de DataFrame colonne par colonne, élément par élément et définir les valeurs directement, mais existe-t-il un moyen simple (de manière optimale sans boucle) d'y parvenir?

Zegkljan
la source

Réponses:

214

Vous pouvez utiliser la fillnaméthode sur le DataFrame et spécifier la méthode comme ffill(remplissage avant):

>>> df = pd.DataFrame([[1, 2, 3], [4, None, None], [None, None, 9]])
>>> df.fillna(method='ffill')
   0  1  2
0  1  2  3
1  4  2  3
2  4  2  9

Cette méthode...

propage la [s] dernière observation valide vers la prochaine observation valide

Pour aller dans le sens inverse, il y a aussi une bfillméthode.

Cette méthode ne modifie pas le DataFrame en place - vous devrez relier le DataFrame retourné à une variable ou spécifier inplace=True:

df.fillna(method='ffill', inplace=True)
Alex Riley
la source
Que se passe-t-il si la cellule vide se trouve dans l'index des noms de colonne (c'est-à-dire que quelques colonnes n'ont pas de noms mais contiennent des données. Existe-t-il un moyen d'utiliser bfill ou ffill pour remplir la cellule d'index de colonne vide avec la cellule dans le ligne immédiatement en dessous? Par exemple: df = pd.DataFrame ({'col1': [2, 4, 8], 'col2': [2, 0, 0], '': [10, 2, 1]} , index = ['falcon', 'dog', 'spider' ']) Comment pourrais-je utiliser bfill ou ffill pour changer le nom de la troisième colonne en 10 (qui est la valeur de la ligne immédiatement sous le nom de la troisième colonne vide ? Merci!
GbG Il y a
31

La réponse acceptée est parfaite. J'ai eu une situation connexe mais légèrement différente où je devais remplir en avant mais seulement au sein des groupes. Si quelqu'un a le même besoin, sachez que fillna fonctionne sur un objet DataFrameGroupBy.

>>> example = pd.DataFrame({'number':[0,1,2,nan,4,nan,6,7,8,9],'name':list('aaabbbcccc')})
>>> example
  name  number
0    a     0.0
1    a     1.0
2    a     2.0
3    b     NaN
4    b     4.0
5    b     NaN
6    c     6.0
7    c     7.0
8    c     8.0
9    c     9.0
>>> example.groupby('name')['number'].fillna(method='ffill') # fill in row 5 but not row 3
0    0.0
1    1.0
2    2.0
3    NaN
4    4.0
5    4.0
6    6.0
7    7.0
8    8.0
9    9.0
Name: number, dtype: float64
ErnestScripteur
la source
exactement ce que je cherchais, ty
Tony
18

Vous pouvez utiliser pandas.DataFrame.fillnaavec l' method='ffill'option. 'ffill'signifie «remplissage en avant» et propage la dernière observation valide vers l'avant. L'alternative est celle 'bfill'qui fonctionne de la même manière, mais à l'envers.

import pandas as pd

df = pd.DataFrame([[1, 2, 3], [4, None, None], [None, None, 9]])
df = df.fillna(method='ffill')

print(df)
#   0  1  2
#0  1  2  3
#1  4  2  3
#2  4  2  9

Il existe également une fonction de synonyme direct pour cela pandas.DataFrame.ffill, pour simplifier les choses.

Ffisegydd
la source
14

Une chose que j'ai remarquée en essayant cette solution est que si vous avez N / A au début ou à la fin du tableau, ffill et bfill ne fonctionnent pas tout à fait. Vous avez besoin des deux.

In [224]: df = pd.DataFrame([None, 1, 2, 3, None, 4, 5, 6, None])

In [225]: df.ffill()
Out[225]:
     0
0  NaN
1  1.0
...
7  6.0
8  6.0

In [226]: df.bfill()
Out[226]:
     0
0  1.0
1  1.0
...
7  6.0
8  NaN

In [227]: df.bfill().ffill()
Out[227]:
     0
0  1.0
1  1.0
...
7  6.0
8  6.0
jjs
la source
Brillant. J'avais exactement besoin de cela pour mon problème. Remplissage avant et après. Merci beaucoup.
Prometheus
Génial. J'ai besoin de cette solution. Merci
Junkrat
6

ffill a maintenant sa propre méthode pd.DataFrame.ffill

df.ffill()

     0    1    2
0  1.0  2.0  3.0
1  4.0  2.0  3.0
2  4.0  2.0  9.0
piRSquared
la source
5

Une seule version de colonne

  • Remplissez NAN avec la dernière valeur valide
df[column_name].fillna(method='ffill', inplace=True)
  • Remplissez NAN avec la prochaine valeur valide
df[column_name].fillna(method='backfill', inplace=True)
SpiralDev
la source
5

Juste d'accord avec la ffillméthode, mais une information supplémentaire est que vous pouvez limiter le remplissage avant avec l'argument mot-clé limit.

>>> import pandas as pd    
>>> df = pd.DataFrame([[1, 2, 3], [None, None, 6], [None, None, 9]])

>>> df
     0    1   2
0  1.0  2.0   3
1  NaN  NaN   6
2  NaN  NaN   9

>>> df[1].fillna(method='ffill', inplace=True)
>>> df
     0    1    2
0  1.0  2.0    3
1  NaN  2.0    6
2  NaN  2.0    9

Maintenant avec l' limitargument mot-clé

>>> df[0].fillna(method='ffill', limit=1, inplace=True)

>>> df
     0    1  2
0  1.0  2.0  3
1  1.0  2.0  6
2  NaN  2.0  9
Suvo
la source
1

Dans mon cas, nous avons des séries chronologiques de différents appareils, mais certains appareils ne pouvaient pas envoyer de valeur pendant une certaine période. Nous devons donc créer des valeurs NA pour chaque appareil et période de temps et ensuite faire fillna.

df = pd.DataFrame([["device1", 1, 'first val of device1'], ["device2", 2, 'first val of device2'], ["device3", 3, 'first val of device3']])
df.pivot(index=1, columns=0, values=2).fillna(method='ffill').unstack().reset_index(name='value')

Résultat:

        0   1   value
0   device1     1   first val of device1
1   device1     2   first val of device1
2   device1     3   first val of device1
3   device2     1   None
4   device2     2   first val of device2
5   device2     3   first val of device2
6   device3     1   None
7   device3     2   None
8   device3     3   first val of device3
Hodza
la source
0

Vous pouvez utiliser fillnapour supprimer ou remplacer les valeurs NaN.

NaN Supprimer

import pandas as pd

df = pd.DataFrame([[1, 2, 3], [4, None, None], [None, None, 9]])

df.fillna(method='ffill')
     0    1    2
0  1.0  2.0  3.0
1  4.0  2.0  3.0
2  4.0  2.0  9.0

Remplacer NaN

df.fillna(0) # 0 means What Value you want to replace 
     0    1    2
0  1.0  2.0  3.0
1  4.0  0.0  0.0
2  0.0  0.0  9.0

Référence pandas.DataFrame.fillna

Md Jewele Islam
la source