Como passar outra coluna inteira como argumento para pandas fillna ()

99

Gostaria de preencher os valores ausentes em uma coluna com valores de outra coluna, usando o fillnamétodo.

(Li que repetir cada linha seria uma prática muito ruim e que seria melhor fazer tudo de uma vez, mas não consegui descobrir como fazer isso fillna.)

Dados anteriores:

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

Dados depois de:

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

Respostas:

182

Você pode fornecer esta coluna para fillna(veja os documentos ), ela usará esses valores em índices correspondentes para preencher:

In [17]: df['Cat1'].fillna(df['Cat2'])
Out[17]:
0    cat
1    dog
2    cat
3    ant
Name: Cat1, dtype: object
Joris
fonte
8
Agradável! Não sabia que fillnaleva uma série.
Ami Tavory
1
Obrigado! Achei que a série tinha que ter o tamanho exato do número de valores de NA.
xav
Ele também funciona para dataframes de linhas multicolunares. Esse recurso de fillna é muito útil.
Wertikal
Eu tenho que dizer que amo isso!
Taylrl
22

Você poderia fazer

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

A construção geral do RHS usa o padrão ternário do pandaslivro de receitas (que vale a pena ler em qualquer caso). É uma versão vetorial de a? b: c.

Ami Tavory
fonte
Não é a solução que usei para este problema, mas um padrão muito interessante! Obrigado!
xav
existe uma maneira de usar isso para várias colunas? por exemplo, se este df tivesse cat1, cat2, cat3, cat4, cat5 e digamos que cat5 estivesse vazio. haveria uma maneira de preencher cat5 com valores de cat1 se cat1 vazio, em seguida, cat2, se cat2 vazio, em seguida, cat3 etc.?
user8322222
@ user8322222 Estou definitivamente atrasado, mas se alguém estiver tendo essa dúvida, você pode fazer np.where aninhado, assim como faria no excel cell = np.where (cond, val_true, np.where (cond, val_true, val_false), )
Kaisar
Você quer mencionar que isso está apenas redefinindo os pandas embutidos pd.DataFrame.fillna(). E eu suspeito que o comportamento do caso extremo pode ser diferente, por exemplo, para comprimentos de série incompatíveis de diferentes dataframes: dfA ['Cat1'], dfB ['Cat2']
smci
9

Basta usar o valueparâmetro em vez 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
fonte
Obrigado pela resposta! O que muda para usar valor em vez do método descrito por joris?
xav
@xav valueé o primeiro parâmetro, então joris está realmente fazendo exatamente a mesma coisa. Como ele disse, veja os docs .
chrisaycock
Sim, a docstring é um pouco enganosa, pois methodestá listada primeiro.
joris
7

pandas.DataFrame.combine_first também funciona.

( Atenção: como "As colunas do índice de resultado serão a união dos respectivos índices e colunas", você deve verificar se o índice e as colunas são correspondidos. )

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

Compare com outras respostas:

%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)

Não usei este método abaixo:

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)

porque levantará uma exceção:

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')

o que significa que np.isnan pode ser aplicado a arrays NumPy de dtype nativo (como np.float64), mas aumenta TypeError quando aplicado a arrays de objetos .

Então, eu reviso o método:

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
fonte
0

Aqui está uma abordagem mais geral (o método fillna é provavelmente melhor)

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)
pardal
fonte
0

Sei que essa é uma pergunta antiga, mas recentemente precisei fazer algo semelhante. Consegui usar o seguinte:

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)

O que produz:

  Cat1
0  cat
1  dog
2  cat
3  ant

Espero que isto ajude alguém!

Jeff Coldplume
fonte