Volver al curso

Data Science con Python: Análisis de Datos para Tu Carrera

leccion
10 / 23
intermediate
8 horas
Pandas y Manipulacion de Datos

Limpieza de Datos y Manejo de Valores Faltantes

Lectura
55 min~4 min lectura

Limpieza de Datos y Manejo de Valores Faltantes

La Realidad de los Datos

Hay un dicho en data science: "El 80% del tiempo lo pasas limpiando datos". Es cierto. Los datos reales vienen con errores, valores faltantes, duplicados, formatos inconsistentes y outliers. Saber limpiar datos es la habilidad que mas te va a servir en el trabajo.


Valores Faltantes (NaN)
import pandas as pd
import numpy as np

# Simular un dataset con datos faltantes
df = pd.DataFrame({
    'nombre': ['Ana', 'Carlos', None, 'Luis', 'Sofia', 'Pedro'],
    'edad': [28, np.nan, 25, 42, 31, np.nan],
    'salario': [65000, 72000, 85000, np.nan, 71000, 55000],
    'departamento': ['Marketing', 'Ventas', 'IT', 'Ventas', None, 'IT'],
    'evaluacion': [4.5, 3.8, np.nan, 3.2, 4.1, np.nan]
})

print("Dataset con valores faltantes:")
print(df)

# Detectar valores faltantes
print(f"\nValores faltantes por columna:")
print(df.isnull().sum())

print(f"\nPorcentaje de valores faltantes:")
print((df.isnull().sum() / len(df) * 100).round(1))

# Verificar si hay algun NaN en cada fila
print(f"\nFilas con algun valor faltante: {df.isnull().any(axis=1).sum()}")

Estrategias para valores faltantes

# 1. ELIMINAR filas con NaN
df_sin_nan = df.dropna()  # Eliminar todas las filas con algun NaN
df_sin_nan_col = df.dropna(subset=['nombre', 'salario'])  # Solo si NaN en estas columnas

# 2. RELLENAR con un valor
df['departamento'] = df['departamento'].fillna('Sin asignar')

# 3. RELLENAR con estadisticas
df['edad'] = df['edad'].fillna(df['edad'].median())
df['salario'] = df['salario'].fillna(df['salario'].mean())
df['evaluacion'] = df['evaluacion'].fillna(df['evaluacion'].median())

# 4. RELLENAR hacia adelante o atras (util en series temporales)
# df['valor'] = df['valor'].fillna(method='ffill')  # Forward fill
# df['valor'] = df['valor'].fillna(method='bfill')  # Backward fill

# 5. INTERPOLAR (para datos numericos continuos)
# df['temperatura'] = df['temperatura'].interpolate()

print("\nDespues de limpiar:")
print(df)
print(f"\nValores faltantes restantes: {df.isnull().sum().sum()}")

Duplicados
# Crear datos con duplicados
df_dup = pd.DataFrame({
    'id': [1, 2, 3, 2, 4, 3],
    'nombre': ['Ana', 'Carlos', 'Maria', 'Carlos', 'Luis', 'Maria'],
    'monto': [100, 200, 150, 200, 300, 150]
})

# Detectar duplicados
print(f"Filas duplicadas: {df_dup.duplicated().sum()}")
print("\nFilas duplicadas:")
print(df_dup[df_dup.duplicated(keep=False)])  # Mostrar todas las copias

# Eliminar duplicados
df_limpio = df_dup.drop_duplicates()
df_limpio_id = df_dup.drop_duplicates(subset=['id'], keep='first')  # Por columna especifica

print(f"\nDespues de eliminar duplicados: {len(df_limpio)} filas")

Limpieza de Strings
# Datos sucios de texto
df_sucio = pd.DataFrame({
    'nombre': ['  Ana Garcia  ', 'carlos LOPEZ', 'MARIA rodriguez', 'luis Fernandez'],
    'email': ['[email protected]', '[email protected]', '[email protected]', '[email protected]'],
    'ciudad': ['Buenos Aires', 'buenos aires', 'BUENOS AIRES', 'Bs As']
})

# Limpiar strings
df_sucio['nombre'] = df_sucio['nombre'].str.strip().str.title()
df_sucio['email'] = df_sucio['email'].str.lower().str.strip()
df_sucio['ciudad'] = df_sucio['ciudad'].str.strip().str.title()

# Reemplazar valores inconsistentes
df_sucio['ciudad'] = df_sucio['ciudad'].replace({
    'Bs As': 'Buenos Aires',
    'Bsas': 'Buenos Aires',
    'CABA': 'Buenos Aires'
})

print(df_sucio)

Tipos de Datos
# Verificar y corregir tipos
df_tipos = pd.DataFrame({
    'fecha': ['2025-01-15', '2025-02-20', '2025-03-10'],
    'monto': ['1,500.00', '2,300.50', '980.75'],
    'cantidad': ['10', '5', '20'],
    'activo': ['True', 'False', 'True']
})

print("Tipos originales:")
print(df_tipos.dtypes)  # Todo es 'object' (string)

# Convertir tipos
df_tipos['fecha'] = pd.to_datetime(df_tipos['fecha'])
df_tipos['monto'] = df_tipos['monto'].str.replace(',', '').astype(float)
df_tipos['cantidad'] = df_tipos['cantidad'].astype(int)
df_tipos['activo'] = df_tipos['activo'].map({'True': True, 'False': False})

print("\nTipos corregidos:")
print(df_tipos.dtypes)
print(df_tipos)

Outliers
np.random.seed(42)
salarios = pd.Series(np.random.normal(60000, 15000, 100))
salarios.iloc[10] = 500000   # Outlier alto
salarios.iloc[20] = -5000    # Outlier bajo

# Detectar con IQR
Q1 = salarios.quantile(0.25)
Q3 = salarios.quantile(0.75)
IQR = Q3 - Q1
limite_inferior = Q1 - 1.5 * IQR
limite_superior = Q3 + 1.5 * IQR

outliers = salarios[(salarios < limite_inferior) | (salarios > limite_superior)]
print(f"Outliers detectados: {len(outliers)}")
print(outliers)

# Tratar outliers: clip (limitar al rango)
salarios_limpiados = salarios.clip(lower=limite_inferior, upper=limite_superior)

print(f"\nAntes: Media={salarios.mean():,.0f}, Mediana={salarios.median():,.0f}")
print(f"Despues: Media={salarios_limpiados.mean():,.0f}, Mediana={salarios_limpiados.median():,.0f}")

Pipeline de Limpieza Completo
def limpiar_dataset(df):
    """Pipeline completo de limpieza de datos."""
    df = df.copy()
    
    print(f"Forma original: {df.shape}")
    
    # 1. Eliminar duplicados
    antes = len(df)
    df = df.drop_duplicates()
    print(f"Duplicados eliminados: {antes - len(df)}")
    
    # 2. Limpiar strings
    for col in df.select_dtypes(include='object'):
        df[col] = df[col].str.strip().str.title()
    
    # 3. Manejar valores faltantes
    for col in df.select_dtypes(include='number'):
        null_pct = df[col].isnull().mean()
        if null_pct > 0.5:
            df = df.drop(columns=[col])
            print(f"Columna '{col}' eliminada ({null_pct:.0%} faltantes)")
        elif null_pct > 0:
            df[col] = df[col].fillna(df[col].median())
            print(f"Columna '{col}' rellenada con mediana")
    
    # 4. Resumen
    print(f"Forma final: {df.shape}")
    print(f"Valores faltantes restantes: {df.isnull().sum().sum()}")
    
    return df

Video Recomendado

Limpieza de Datos con Pandas - Guia Completa


Ejercicio Practico

Crea un notebook 10_limpieza.ipynb:

  1. Crea un dataset "sucio" con valores faltantes, duplicados, strings inconsistentes y outliers
  2. Detecta y reporta problemas de calidad
  3. Implementa un pipeline de limpieza completo
  4. Compara estadisticas antes y despues de la limpieza
  5. Guarda el dataset limpio como CSV

💡 Concepto Clave

Revisemos los puntos más importantes de esta lección antes de continuar.

Resumen

  • isnull() / isna(): Detectar valores faltantes
  • dropna(): Eliminar filas/columnas con NaN
  • fillna(): Rellenar NaN con valores
  • drop_duplicates(): Eliminar filas duplicadas
  • str.strip().str.title(): Limpiar strings
  • astype() / to_datetime(): Corregir tipos de datos
  • clip(): Manejar outliers limitando al rango valido
🧠 Pon a prueba tu conocimiento
¿Cuál es el aspecto más importante que aprendiste en esta lección?
  • Comprendo el concepto principal y puedo explicarlo con mis palabras
  • Entiendo cómo aplicarlo en mi situación específica
  • Necesito repasar algunas partes antes de continuar
  • Quiero ver más ejemplos prácticos del tema
✅ ¡Excelente! Continúa con la siguiente lección para profundizar más.