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 TrueResumen rápido de conteos faltantes
# Contar valores faltantes por columna
print(df.isna().sum())Salida:
name 1
age 2
salary 2
dtype: int64notna() 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ón | Devuelve True cuando | Alias |
|---|---|---|
isna() | El valor es NaN, None o NaT | isnull() |
notna() | El valor no es faltante | notnull() |
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.0Esto 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ámetro | Tipo | Predeterminado | Descripción |
|---|---|---|---|
value | escalar, dict, Series o DataFrame | None | El valor para rellenar las entradas faltantes |
method | 'ffill', 'bfill' o None | None | Método de propagación para rellenar vacíos |
axis | 0 o 1 | None | Rellenar a lo largo de filas (0) o columnas (1) |
inplace | bool | False | Si True, modifica el DataFrame en su lugar |
limit | int | None | Nú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.0Este 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 NaNObserva 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: float64Esto 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| Estrategia | Mejor para | Notas |
|---|---|---|
mean() | Datos numéricos con distribuciones aproximadamente simétricas | Sensible a valores atípicos |
median() | Datos numéricos con distribuciones sesgadas o valores atípicos | Más robusto que la media |
mode() | Datos categóricos o valores numéricos discretos | Devuelve 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.0Observa 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étodo | Descripció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 NaNsLa 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:
| Aspecto | fillna() | dropna() | interpolate() |
|---|---|---|---|
| Qué hace | Reemplaza NaN con un valor especificado | Elimina filas o columnas que contienen NaN | Estima NaN a partir de valores circundantes |
| Conteo de filas | Preservado | Reducido | Preservado |
| Mejor para | Valores de reemplazo conocidos, datos categóricos, imputación estadística | Pequeño porcentaje de filas faltantes, o cuando la imputación distorsionaría el análisis | Datos numéricos ordenados/series temporales con tendencia natural |
| Riesgo | Introduce sesgo si el valor de relleno se elige mal | Pierde datos; puede sesgar resultados si los datos no faltan aleatoriamente | Asume un patrón subyacente suave que puede no existir |
| Caso de uso típico | Rellenar respuestas de encuestas faltantes con "Sin respuesta", rellenar precios con media de columna | Eliminar filas sin variable objetivo antes del entrenamiento del modelo | Rellenar vacíos en precios de acciones diarios o lecturas de temperatura |
| Maneja datos categóricos | Sí | Sí (eliminando) | No (solo numérico) |
| Compatible con encadenamiento | Sí | Sí | Sí |
Regla general para la decisión:
- Si menos del 5% de las filas faltan y los datos faltan completamente al azar,
dropna()es seguro. - Si tienes un valor predeterminado significativo o puedes calcular una estadística razonable, usa
fillna(). - 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.0Este 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 storeEste 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.92Las 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 pygwalkero 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()ynotna()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.