Cómo pasar otra columna completa como argumento a pandas fillna ()

94

Me gustaría completar los valores faltantes en una columna con valores de otra columna, usando el fillnamétodo.

(Leí que recorrer cada fila sería una muy mala práctica y que sería mejor hacer todo de una vez, pero no pude averiguar cómo hacerlo fillna).

Datos antes:

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

Datos después:

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

Respuestas:

173

Puede proporcionar esta columna a fillna(ver documentos ), usará esos valores en índices coincidentes para completar:

In [17]: df['Cat1'].fillna(df['Cat2'])
Out[17]:
0    cat
1    dog
2    cat
3    ant
Name: Cat1, dtype: object
joris
fuente
7
¡Agradable! No sabía que fillnalleva una serie.
Ami Tavory
1
¡Gracias! Pensé que la serie tenía que ser del tamaño exacto del número de valores NA.
xav
También funciona para marcos de datos para filas multicolumnar. Esa característica de fillna es muy útil.
Wertikal
19

Podrías hacerlo

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

La construcción general en el RHS utiliza el patrón ternario del pandaslibro de cocina (que vale la pena leer en cualquier caso). Es una versión vectorial de a? b: c.

Ami Tavory
fuente
No es la solución que usé para este problema, ¡pero un patrón muy interesante! ¡Gracias!
xav
¿Hay alguna forma de usar esto para varias columnas? por ejemplo, si este df tuviera cat1, cat2, cat3, cat4, cat5 y digamos que cat5 estaba vacío. ¿Habría una manera de llenar cat5 con valores de cat1 si cat1 está vacío, luego cat2, si cat2 está vacío, luego cat3, etc.?
user8322222
@ user8322222 Definitivamente llego tarde, pero si alguien tiene esta pregunta, puede hacer np.where anidado, tal como lo haría en Excel cell = np.where (cond, val_true, np.where (cond, val_true, val_false), ).
Kaisar
Quiere mencionar que esto solo está redefiniendo los pandas incorporados pd.DataFrame.fillna(). Y sospecho que el comportamiento del caso de esquina puede diferir, por ejemplo, para longitudes de series no coincidentes de diferentes marcos de datos: dfA ['Cat1'], dfB ['Cat2']
smci
8

Simplemente use el valueparámetro en lugar 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
fuente
¡Gracias por la respuesta! ¿Qué cambia usar valor en lugar del método descrito por joris?
xav
@xav valuees el primer parámetro, por lo que joris está haciendo exactamente lo mismo. Como dijo, consulte los documentos .
chrisaycock
Sí, la cadena de documentos es un poco engañosa, ya que methodse enumera allí primero.
joris
7

pandas.DataFrame.combine_first también funciona.

( Atención: dado que "Las columnas de índice de resultados serán la unión de los índices y columnas respectivos", debe verificar que el índice y las columnas coincidan ) .

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 con otras respuestas:

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

No utilicé este método a continuación:

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 generará una excepción:

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

lo que significa que np.isnan se puede aplicar a matrices NumPy de dtype nativo (como np.float64), pero genera TypeError cuando se aplica a matrices de objetos .

Entonces reviso el 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
fuente
0

Aquí hay un enfoque más general (el método fillna es probablemente mejor)

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)
gorrión
fuente
0

Sé que esta es una pregunta antigua, pero recientemente tuve la necesidad de hacer algo similar. Pude usar lo siguiente:

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)

Cuyos rendimientos:

  Cat1
0  cat
1  dog
2  cat
3  ant

¡Espero que esto sea útil para alguien!

Jeff Coldplume
fuente