Comment passer une autre colonne entière comme argument à pandas fillna ()

94

Je voudrais remplir les valeurs manquantes dans une colonne avec des valeurs d'une autre colonne, en utilisant la fillnaméthode.

(J'ai lu que parcourir chaque ligne en boucle serait une très mauvaise pratique et qu'il serait préférable de tout faire en une seule fois, mais je ne pouvais pas savoir comment le faire fillna.)

Données avant:

Day  Cat1  Cat2
1    cat   mouse
2    dog   elephant
3    cat   giraf
4    NaN   ant

Données après:

Day  Cat1  Cat2
1    cat   mouse
2    dog   elephant
3    cat   giraf
4    ant   ant
xav
la source

Réponses:

173

Vous pouvez fournir cette colonne à fillna(voir la documentation ), elle utilisera ces valeurs sur les index correspondants pour remplir:

In [17]: df['Cat1'].fillna(df['Cat2'])
Out[17]:
0    cat
1    dog
2    cat
3    ant
Name: Cat1, dtype: object
joris
la source
7
Agréable! Je ne savais pas que ça fillnaprend une série.
Ami Tavory
1
Merci! Je pensais que la série devait avoir la taille exacte du nombre de valeurs NA.
xav
Cela fonctionne également pour les dataframes pour les lignes multicolonnes. Cette fonctionnalité de fillna est très utile.
Wertikal
19

Vous pourriez faire

df.Cat1 = np.where(df.Cat1.isnull(), df.Cat2, df.Cat1)

La construction globale sur le RHS utilise le modèle ternaire du pandaslivre de cuisine (qu'il vaut la peine de lire dans tous les cas). C'est une version vectorielle de a? b: c.

Ami Tavory
la source
Pas la solution que j'ai utilisée pour ce problème mais un motif très intéressant! Merci!
xav
y a-t-il un moyen de l'utiliser pour plusieurs colonnes? par exemple si ce df avait cat1, cat2, cat3, cat4, cat5 et disons que cat5 était vide. y aurait-il un moyen de remplir cat5 avec des valeurs de cat1 si cat1 vide puis cat2, si cat2 vide alors cat3 etc.?
user8322222
@ user8322222 Je suis définitivement en retard mais si quelqu'un a cette question, vous pouvez faire np.where imbriqué, comme vous le feriez dans excel cell = np.where (cond, val_true, np.where (cond, val_true, val_false), ).
Kaisar
Vous voulez mentionner que cela ne fait que redéfinir les pandas intégrés pd.DataFrame.fillna(). Et je soupçonne que le comportement de la casse d'angle peut différer, par exemple pour les longueurs de série non concordantes de différentes dataframes: dfA ['Cat1'], dfB ['Cat2']
smci
8

Utilisez simplement le valueparamètre au lieu de method:

In [20]: df
Out[20]:
  Cat1      Cat2  Day
0  cat     mouse    1
1  dog  elephant    2
2  cat     giraf    3
3  NaN       ant    4

In [21]: df.Cat1 = df.Cat1.fillna(value=df.Cat2)

In [22]: df
Out[22]:
  Cat1      Cat2  Day
0  cat     mouse    1
1  dog  elephant    2
2  cat     giraf    3
3  ant       ant    4
chrisaycock
la source
Merci d'avoir répondu! Qu'est-ce que cela change pour utiliser la valeur plutôt que la méthode décrite par joris?
xav
@xav valueest le premier paramètre, donc joris fait exactement la même chose. Comme il l'a dit, consultez la documentation .
chrisaycock
Oui, la docstring est un peu trompeuse, comme methodindiqué en premier.
joris
7

pandas.DataFrame.combine_first fonctionne également.

( Attention: puisque "Les colonnes d'index de résultat seront l'union des index et des colonnes respectifs", vous devez vérifier que l'index et les colonnes correspondent. )

import numpy as np
import pandas as pd
df = pd.DataFrame([["1","cat","mouse"],
    ["2","dog","elephant"],
    ["3","cat","giraf"],
    ["4",np.nan,"ant"]],columns=["Day","Cat1","Cat2"])

In: df["Cat1"].combine_first(df["Cat2"])
Out: 
0    cat
1    dog
2    cat
3    ant
Name: Cat1, dtype: object

Comparez avec d'autres réponses:

%timeit df["Cat1"].combine_first(df["Cat2"])
181 µs ± 11.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit df['Cat1'].fillna(df['Cat2'])
253 µs ± 10.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit np.where(df.Cat1.isnull(), df.Cat2, df.Cat1)
88.1 µs ± 793 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Je n'ai pas utilisé cette méthode ci-dessous:

def is_missing(Cat1,Cat2):    
    if np.isnan(Cat1):        
        return Cat2
    else:
        return Cat1

df['Cat1'] = df.apply(lambda x: is_missing(x['Cat1'],x['Cat2']),axis=1)

car cela lèvera une exception:

TypeError: ("ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''", 'occurred at index 0')

ce qui signifie que np.isnan peut être appliqué aux tableaux NumPy de type dtype natif (comme np.float64), mais lève TypeError lorsqu'il est appliqué aux tableaux d' objets .

Je révise donc la méthode:

def is_missing(Cat1,Cat2):    
    if pd.isnull(Cat1):        
        return Cat2
    else:
        return Cat1

%timeit df.apply(lambda x: is_missing(x['Cat1'],x['Cat2']),axis=1)
701 µs ± 7.38 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Jeremy Z
la source
0

Voici une approche plus générale (la méthode fillna est probablement meilleure)

def is_missing(Cat1,Cat2):    
    if np.isnan(Cat1):        
        return Cat2
    else:
        return Cat1

df['Cat1'] = df.apply(lambda x: is_missing(x['Cat1'],x['Cat2']),axis=1)
moineau
la source
0

Je sais que c'est une vieille question, mais j'avais besoin de faire quelque chose de similaire récemment. J'ai pu utiliser les éléments suivants:

df = pd.DataFrame([["1","cat","mouse"],
    ["2","dog","elephant"],
    ["3","cat","giraf"],
    ["4",np.nan,"ant"]],columns=["Day","Cat1","Cat2"])

print(df)

  Day Cat1      Cat2
0   1  cat     mouse
1   2  dog  elephant
2   3  cat     giraf
3   4  NaN       ant

df1 = df.bfill(axis=1).iloc[:, 1]
df1 = df1.to_frame()
print(df1)

Ce qui donne:

  Cat1
0  cat
1  dog
2  cat
3  ant

J'espère que cela est utile à quelqu'un!

Jeff Coldplume
la source