Skip to content

Pandas fillna(): Manejar valores faltantes en DataFrames

Updated on

Los valores faltantes son el saboteador silencioso del análisis de datos. Un solo NaN oculto en una columna crítica puede causar que una agregación devuelva NaN, que un modelo de machine learning lance un error durante el entrenamiento, o que un gráfico del dashboard muestre un espacio vacío donde debería haber una línea de tendencia. Los conjuntos de datos del mundo real casi siempre contienen vacíos -- las lecturas de sensores se pierden, los encuestados omiten preguntas, las respuestas de API devuelven campos nulos y las exportaciones CSV llegan con celdas vacías. La pregunta nunca es si encontrarás datos faltantes, sino cómo los manejarás.

El método fillna() de pandas es la herramienta principal para reemplazar valores faltantes con algo significativo. Esta guía cubre cada parámetro, demuestra estrategias comunes de relleno (escalar, diccionario, forward fill, backward fill, media/mediana/moda), compara fillna() con dropna() e interpolate(), y muestra cómo encadenar estas operaciones en un pipeline de datos limpio. Cada ejemplo de código está listo para copiar con la salida esperada.

📚

Detectar valores faltantes antes de rellenar

Antes de rellenar cualquier cosa, necesitas saber dónde están los vacíos. Pandas proporciona tres funciones de detección:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'name': ['Alice', 'Bob', None, 'Diana', 'Eve'],
    'age': [28, np.nan, 35, np.nan, 42],
    'salary': [55000, 62000, np.nan, 48000, np.nan]
})
 
# isna() devuelve True para valores faltantes (alias: isnull())
print(df.isna())

Salida:

    name    age  salary
0  False  False   False
1  False   True   False
2   True  False    True
3  False   True   False
4  False  False    True

Resumen rápido de conteos faltantes

# Contar valores faltantes por columna
print(df.isna().sum())

Salida:

name      1
age       2
salary    2
dtype: int64

notna() para la verificación inversa

# notna() devuelve True para valores no faltantes
print(df.notna().sum())

Salida:

name      4
age       3
salary    3
dtype: int64
FunciónDevuelve True cuandoAlias
isna()El valor es NaN, None o NaTisnull()
notna()El valor no es faltantenotnull()

Estas funciones funcionan tanto con DataFrames como con Series individuales. Úsalas para auditar tus datos antes de decidir una estrategia de relleno.

fillna() básico con un valor escalar

El uso más simple de fillna() reemplaza cada NaN en el DataFrame con un único valor:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'product': ['Widget', 'Gadget', 'Gizmo'],
    'price': [19.99, np.nan, 29.99],
    'stock': [100, 50, np.nan]
})
 
print("Antes:")
print(df)
 
df_filled = df.fillna(0)
 
print("\nDespués de fillna(0):")
print(df_filled)

Salida:

Antes:
  product  price  stock
0  Widget  19.99  100.0
1  Gadget    NaN   50.0
2   Gizmo  29.99    NaN

Después de fillna(0):
  product  price  stock
0  Widget  19.99  100.0
1  Gadget   0.00   50.0
2   Gizmo  29.99    0.0

Esto funciona, pero rellenar una columna de precios con 0 es engañoso -- sugiere que el producto es gratuito. Para columnas de texto, podrías rellenar con "Desconocido". La clave es elegir un valor de relleno que tenga sentido semántico para cada columna.

Firma completa del método

DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None)
ParámetroTipoPredeterminadoDescripción
valueescalar, dict, Series o DataFrameNoneEl valor para rellenar las entradas faltantes
method'ffill', 'bfill' o NoneNoneMétodo de propagación para rellenar vacíos
axis0 o 1NoneRellenar a lo largo de filas (0) o columnas (1)
inplaceboolFalseSi True, modifica el DataFrame en su lugar
limitintNoneNúmero máximo de NaN consecutivos a rellenar

fillna() con un diccionario: Diferentes valores por columna

En la mayoría de los conjuntos de datos reales, cada columna representa un tipo diferente de medición, y un único valor de relleno no tiene sentido en todas partes. Pasa un diccionario a fillna() para especificar valores de relleno por columna:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'name': ['Alice', None, 'Charlie', 'Diana'],
    'age': [28, 34, np.nan, 45],
    'department': ['Engineering', 'Sales', None, 'Marketing'],
    'salary': [75000, np.nan, 68000, np.nan]
})
 
fill_values = {
    'name': 'Unknown',
    'age': df['age'].median(),
    'department': 'Unassigned',
    'salary': df['salary'].mean()
}
 
df_filled = df.fillna(fill_values)
print(df_filled)

Salida:

      name   age   department   salary
0    Alice  28.0  Engineering  75000.0
1  Unknown  34.0        Sales  71500.0
2  Charlie  34.0   Unassigned  68000.0
3    Diana  45.0    Marketing  71500.0

Este es el enfoque recomendado para pipelines de datos en producción porque te da control explícito sobre lo que recibe cada columna.

Forward Fill (ffill) y Backward Fill (bfill)

Los datos de series temporales y los conjuntos de datos ordenados a menudo se benefician del relleno basado en propagación. El forward fill lleva el último valor conocido hacia adelante; el backward fill toma el siguiente valor conocido hacia atrás.

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'date': pd.date_range('2026-01-01', periods=7, freq='D'),
    'temperature': [22.1, np.nan, np.nan, 24.5, np.nan, 26.0, np.nan]
})
 
print("Original:")
print(df)
 
print("\nForward fill (ffill):")
print(df.fillna(method='ffill'))
 
print("\nBackward fill (bfill):")
print(df.fillna(method='bfill'))

Salida:

Original:
        date  temperature
0 2026-01-01         22.1
1 2026-01-02          NaN
2 2026-01-03          NaN
3 2026-01-04         24.5
4 2026-01-05          NaN
5 2026-01-06         26.0
6 2026-01-07          NaN

Forward fill (ffill):
        date  temperature
0 2026-01-01         22.1
1 2026-01-02         22.1
2 2026-01-03         22.1
3 2026-01-04         24.5
4 2026-01-05         24.5
5 2026-01-06         26.0
6 2026-01-07         26.0

Backward fill (bfill):
        date  temperature
0 2026-01-01         22.1
1 2026-01-02         24.5
2 2026-01-03         24.5
3 2026-01-04         24.5
4 2026-01-05         26.0
5 2026-01-06         26.0
6 2026-01-07          NaN

Observa que el backward fill deja la última fila como NaN porque no hay un valor posterior del cual extraer. Puedes combinar ambos métodos para cerrar todos los vacíos:

df_filled = df.fillna(method='ffill').fillna(method='bfill')
print(df_filled)

A partir de pandas 2.1, también puedes usar directamente los métodos independientes df.ffill() y df.bfill(), que son atajos para fillna(method='ffill') y fillna(method='bfill').

Limitar la propagación con limit

Cuando un sensor se interrumpe durante días, el forward fill indefinido puede ocultar vacíos reales en los datos. El parámetro limit limita cuántos NaN consecutivos se rellenan:

import pandas as pd
import numpy as np
 
s = pd.Series([1.0, np.nan, np.nan, np.nan, 5.0])
 
print("limit=1:")
print(s.fillna(method='ffill', limit=1))
 
print("\nlimit=2:")
print(s.fillna(method='ffill', limit=2))

Salida:

limit=1:
0    1.0
1    1.0
2    NaN
3    NaN
4    5.0
dtype: float64

limit=2:
0    1.0
1    1.0
2    1.0
3    NaN
4    5.0
dtype: float64

Esto es crucial para datos de series temporales donde deseas rellenar pequeños vacíos pero marcar interrupciones más largas para revisión manual.

fillna() con media, mediana y moda

La imputación estadística reemplaza los valores faltantes con una estadística resumida calculada a partir de los valores no faltantes en esa columna. Esta es la estrategia más común para características numéricas antes de alimentar datos a un modelo:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'math_score': [85, np.nan, 92, 78, np.nan, 88],
    'reading_score': [np.nan, 76, 81, np.nan, 90, 85],
    'grade': ['A', 'B', 'A', np.nan, 'B', np.nan]
})
 
# Rellenar columnas numéricas con su media de columna
df['math_score'] = df['math_score'].fillna(df['math_score'].mean())
df['reading_score'] = df['reading_score'].fillna(df['reading_score'].median())
 
# Rellenar columna categórica con moda (valor más frecuente)
df['grade'] = df['grade'].fillna(df['grade'].mode()[0])
 
print(df)

Salida:

   math_score  reading_score grade
0       85.00          83.00     A
1       85.75          76.00     B
2       92.00          81.00     A
3       78.00          83.00     A
4       85.75          90.00     B
5       88.00          85.00     A
EstrategiaMejor paraNotas
mean()Datos numéricos con distribuciones aproximadamente simétricasSensible a valores atípicos
median()Datos numéricos con distribuciones sesgadas o valores atípicosMás robusto que la media
mode()Datos categóricos o valores numéricos discretosDevuelve el valor más común; mode()[0] toma el primero si hay empate

Para pipelines de machine learning, considera usar sklearn.impute.SimpleImputer, que se integra con los pipelines de scikit-learn y maneja correctamente la imputación en la división train/test.

interpolate() para datos numéricos

Cuando los datos siguen una tendencia (precios de acciones, lecturas de sensores, métricas de crecimiento), interpolate() estima los valores faltantes basándose en los puntos de datos circundantes en lugar de usar un relleno plano:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'day': range(1, 8),
    'revenue': [1000, np.nan, np.nan, 1600, np.nan, 2000, np.nan]
})
 
df['fillna_ffill'] = df['revenue'].fillna(method='ffill')
df['interpolated'] = df['revenue'].interpolate(method='linear')
 
print(df)

Salida:

   day  revenue  fillna_ffill  interpolated
0    1   1000.0        1000.0        1000.0
1    2      NaN        1000.0        1200.0
2    3      NaN        1000.0        1400.0
3    4   1600.0        1600.0        1600.0
4    5      NaN        1600.0        1800.0
5    6   2000.0        2000.0        2000.0
6    7      NaN        2000.0        2000.0

Observa cómo interpolate() produce una progresión lineal suave (1000, 1200, 1400, 1600, 1800, 2000) mientras que ffill crea mesetas planas. Pandas soporta múltiples métodos de interpolación:

MétodoDescripción
'linear'Predeterminado. Traza una línea recta entre puntos conocidos.
'time'Interpolación lineal ponderada por índice temporal.
'index'Usa los valores numéricos reales del índice.
'polynomial'Ajusta un polinomio del orden especificado.
'spline'Ajusta un spline del orden especificado para curvas suaves.

Usa interpolate() cuando los datos tienen un orden natural y una tendencia. Usa fillna() cuando tienes un valor de reemplazo conocido o necesitas relleno basado en propagación.

El parámetro inplace

Como la mayoría de los métodos de pandas, fillna() devuelve un nuevo DataFrame por defecto. Establecer inplace=True modifica el original:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({'a': [1, np.nan, 3], 'b': [np.nan, 5, 6]})
 
# Método 1: asignación (recomendado)
df_new = df.fillna(0)
print(f"Original sin cambios: {df.isna().sum().sum()} NaNs")
print(f"Nueva copia: {df_new.isna().sum().sum()} NaNs")
 
# Método 2: inplace (modifica el original)
df.fillna(0, inplace=True)
print(f"Después de inplace: {df.isna().sum().sum()} NaNs")

Salida:

Original sin cambios: 2 NaNs
Nueva copia: 0 NaNs
Después de inplace: 0 NaNs

La mejor práctica moderna en pandas favorece la asignación sobre inplace=True porque la asignación funciona naturalmente en cadenas de métodos y hace explícito el flujo de datos.

Comparación: fillna() vs dropna() vs interpolate()

Elegir la estrategia correcta para datos faltantes depende de tu conjunto de datos, el patrón de datos faltantes y tu caso de uso posterior. Aquí hay una comparación lado a lado:

Aspectofillna()dropna()interpolate()
Qué haceReemplaza NaN con un valor especificadoElimina filas o columnas que contienen NaNEstima NaN a partir de valores circundantes
Conteo de filasPreservadoReducidoPreservado
Mejor paraValores de reemplazo conocidos, datos categóricos, imputación estadísticaPequeño porcentaje de filas faltantes, o cuando la imputación distorsionaría el análisisDatos numéricos ordenados/series temporales con tendencia natural
RiesgoIntroduce sesgo si el valor de relleno se elige malPierde datos; puede sesgar resultados si los datos no faltan aleatoriamenteAsume un patrón subyacente suave que puede no existir
Caso de uso típicoRellenar respuestas de encuestas faltantes con "Sin respuesta", rellenar precios con media de columnaEliminar filas sin variable objetivo antes del entrenamiento del modeloRellenar vacíos en precios de acciones diarios o lecturas de temperatura
Maneja datos categóricosSí (eliminando)No (solo numérico)
Compatible con encadenamiento

Regla general para la decisión:

  1. Si menos del 5% de las filas faltan y los datos faltan completamente al azar, dropna() es seguro.
  2. Si tienes un valor predeterminado significativo o puedes calcular una estadística razonable, usa fillna().
  3. Si los datos están ordenados y son numéricos con una tendencia, usa interpolate().

fillna() en columnas específicas

No siempre querrás rellenar todo el DataFrame. Aplica fillna() a columnas individuales o un subconjunto:

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'city': ['NYC', None, 'LA', None, 'Chicago'],
    'temperature': [32.1, np.nan, 75.3, np.nan, 28.5],
    'humidity': [45, 60, np.nan, np.nan, 55]
})
 
# Rellenar solo la columna de ciudad
df['city'] = df['city'].fillna('Unknown')
 
# Rellenar solo la columna de temperatura con su media
df['temperature'] = df['temperature'].fillna(df['temperature'].mean())
 
# Dejar los NaN de humedad intactos por ahora
print(df)

Salida:

      city  temperature  humidity
0      NYC    32.100000      45.0
1  Unknown    45.300000      60.0
2       LA    75.300000       NaN
3  Unknown    45.300000       NaN
4  Chicago    28.500000      55.0

Este enfoque selectivo es importante cuando diferentes columnas requieren diferente tratamiento -- o cuando algunos valores faltantes son intencionales (por ejemplo, la humedad podría no aplicar a mediciones interiores).

Encadenar fillna() con otras operaciones

El encadenamiento de métodos de pandas te permite construir pipelines de datos legibles. fillna() encaja naturalmente en estas cadenas:

import pandas as pd
import numpy as np
 
raw = pd.DataFrame({
    'customer_id': [101, 102, 101, 103, 102, 104],
    'purchase': [25.0, np.nan, 30.0, np.nan, 15.0, np.nan],
    'channel': ['web', 'store', None, 'web', None, 'store']
})
 
result = (
    raw
    .fillna({'purchase': 0, 'channel': 'unknown'})
    .drop_duplicates(subset=['customer_id'], keep='first')
    .sort_values('customer_id')
    .reset_index(drop=True)
)
 
print(result)

Salida:

   customer_id  purchase  channel
0          101      25.0      web
1          102       0.0    store
2          103       0.0      web
3          104       0.0    store

Este pipeline rellena valores faltantes, elimina duplicados por ID de cliente, ordena y reinicia el índice en una sola expresión legible.

Pipeline del mundo real: Limpieza de datos de ventas

Aquí hay una cadena más realista que combina múltiples pasos de limpieza:

import pandas as pd
import numpy as np
 
sales = pd.DataFrame({
    'date': ['2026-01-01', '2026-01-02', '2026-01-03', '2026-01-04', '2026-01-05'],
    'product': ['Widget', None, 'Widget', 'Gadget', None],
    'units': [10, np.nan, 15, np.nan, 8],
    'unit_price': [9.99, 9.99, np.nan, 14.99, np.nan],
    'region': ['East', 'East', None, 'West', 'West']
})
 
clean = (
    sales
    .assign(date=lambda d: pd.to_datetime(d['date']))
    .fillna({
        'product': 'Unknown',
        'region': 'Unassigned',
        'units': sales['units'].median(),
        'unit_price': sales['unit_price'].median()
    })
    .assign(total=lambda d: d['units'] * d['unit_price'])
    .sort_values('date')
    .reset_index(drop=True)
)
 
print(clean)

Salida:

        date  product  units  unit_price      region   total
0 2026-01-01   Widget   10.0        9.99        East   99.90
1 2026-01-02  Unknown   10.0        9.99        East   99.90
2 2026-01-03   Widget   15.0        9.99  Unassigned  149.85
3 2026-01-04   Gadget   10.0       14.99        West  149.90
4 2026-01-05  Unknown    8.0        9.99        West   79.92

Las llamadas a assign() crean o transforman columnas, fillna() maneja los vacíos, y la cadena fluye de arriba a abajo en orden lógico.

Visualizar patrones de datos faltantes con PyGWalker

Antes de elegir una estrategia de relleno, ayuda ver dónde se concentran los valores faltantes. ¿Están dispersos aleatoriamente, agrupados en ciertas columnas o correlacionados con períodos de tiempo específicos? La inspección visual a menudo revela patrones que las estadísticas resumidas pasan por alto.

PyGWalker (opens in a new tab) es una biblioteca Python de código abierto que convierte cualquier DataFrame de pandas en una interfaz de visualización interactiva tipo Tableau directamente en Jupyter Notebook. Puedes arrastrar columnas a los ejes, cambiar tipos de gráficos y filtrar datos con clics en lugar de escribir código repetitivo de matplotlib.

import pandas as pd
import pygwalker as pyg
 
# Carga tus datos y marca patrones de valores faltantes
df = pd.read_csv('your_data.csv')
 
# Agregar una columna que cuente valores faltantes por fila
df['missing_count'] = df.isna().sum(axis=1)
 
# Iniciar explorador interactivo
walker = pyg.walk(df)

Dentro de la interfaz de PyGWalker, puedes crear gráficos de barras mostrando el conteo de valores faltantes por columna, mapas de calor revelando qué filas tienen más vacíos, y gráficos de dispersión para verificar si los datos faltantes se correlacionan con otras variables. Este tipo de auditoría visual a menudo cambia qué estrategia de relleno eliges.

Instala PyGWalker con pip install pygwalker o pruébalo en Google Colab (opens in a new tab).

FAQ

¿Cuál es la diferencia entre fillna() y dropna()?

fillna() reemplaza los valores faltantes con un valor que especificas, manteniendo todas las filas intactas. dropna() elimina filas enteras (o columnas) que contienen valores faltantes. Usa fillna() cuando tengas un valor de reemplazo razonable y quieras preservar tu conteo de filas. Usa dropna() cuando las filas faltantes sean pocas y la imputación introduciría un sesgo inaceptable.

¿Puedo rellenar valores NaN con la media de una columna?

Sí. Usa df['column'] = df['column'].fillna(df['column'].mean()). Esto calcula la media a partir de los valores no faltantes y rellena cada NaN en esa columna con el resultado. Para datos sesgados, median() suele ser una mejor opción porque se ve menos afectada por valores atípicos extremos.

¿Qué hace el parámetro limit en fillna()?

El parámetro limit limita el número máximo de valores NaN consecutivos que se rellenan. Por ejemplo, df.fillna(method='ffill', limit=2) rellenará hacia adelante como máximo 2 vacíos consecutivos. Cualquier secuencia más larga de valores faltantes solo se rellenará parcialmente, dejando los vacíos restantes como NaN. Esto es útil para datos de series temporales donde deseas rellenar vacíos cortos pero marcar interrupciones prolongadas.

¿Cómo relleno NaN con diferentes valores para diferentes columnas?

Pasa un diccionario a fillna() donde las claves son nombres de columnas y los valores son los valores de relleno: df.fillna({'age': 0, 'name': 'Unknown', 'salary': df['salary'].median()}). Cada columna recibe su propio valor de relleno, y las columnas no listadas en el diccionario permanecen sin cambios.

¿fillna() cambia el DataFrame original?

No, por defecto fillna() devuelve un nuevo DataFrame y el original permanece sin cambios. Para modificar el original, usa asignación (df = df.fillna(0)) o pasa inplace=True. El enfoque de asignación se recomienda porque funciona con el encadenamiento de métodos y hace explícito el flujo de datos.

Conclusión

Los valores faltantes son inevitables en datos del mundo real. El método fillna() de pandas te da control preciso sobre cómo manejarlos:

  • Usa fillna escalar para reemplazos simples y uniformes en todo el DataFrame.
  • Usa fillna con diccionario para aplicar diferentes estrategias de relleno por columna -- el patrón más común en código de producción.
  • Usa forward fill (ffill) y backward fill (bfill) para datos ordenados y series temporales donde propagar valores conocidos tiene sentido.
  • Usa media, mediana o moda para imputación estadística de columnas numéricas y categóricas.
  • Usa interpolate() cuando los datos siguen una tendencia natural y deseas valores estimados suaves en lugar de rellenos planos.
  • Usa el parámetro limit para evitar que los métodos basados en propagación rellenen vacíos excesivamente largos.
  • Prefiere asignación sobre inplace=True para un código más limpio y legible.
  • Siempre detecta y audita los valores faltantes con isna() y notna() antes de elegir una estrategia de relleno.

Una vez que tus valores faltantes estén manejados, herramientas como PyGWalker (opens in a new tab) te permiten explorar interactivamente los datos limpios sin escribir código de gráficos -- ayudándote a verificar que tu lógica de relleno produjo resultados sensatos y pasar directamente al análisis.

📚