Lección 7 de 29Módulo 2: Pandas - Manipulación de Datos

07. Carga y Exploración de Datos

Lee CSV, Excel, JSON y explora tus datos con head, info, describe

25 minutos

Aprende a cargar datos desde múltiples fuentes (CSV, Excel, JSON, SQL) y explorar conjuntos de datos comoreales como un analista profesional.

Cargar datos desde archivos

CSV (Comma-Separated Values)

El formato más común en análisis de datos:

import pandas as pd

# Cargar CSV básico
df = pd.read_csv('ventas.csv')

# Opciones comunes
df = pd.read_csv(
    'ventas.csv',
    sep=',',              # Separador (default: coma)
    encoding='utf-8',     # Encoding (utf-8 o latin1)
    decimal='.',          # Separador decimal
    thousands=',',        # Separador de miles
    parse_dates=['fecha'], # Parsear columnas como fechas
    index_col='id'        # Usar columna como índice
)

# Ver primeras filas
print(df.head())

Excel

# Cargar archivo Excel (requiere openpyxl o xlrd)
df = pd.read_excel('ventas.xlsx')

# Especificar hoja
df = pd.read_excel('ventas.xlsx', sheet_name='Enero')

# Cargar múltiples hojas
dict_dfs = pd.read_excel('ventas.xlsx', sheet_name=None)
df_enero = dict_dfs['Enero']
df_febrero = dict_dfs['Febrero']

# Opciones avanzadas
df = pd.read_excel(
    'ventas.xlsx',
    sheet_name='Datos',
    skiprows=2,           # Saltar primeras 2 filas
    usecols='A:E',        # Solo columnas A a E
    na_values=['N/A', '-'] # Valores a tratar como NaN
)

JSON

# JSON simple (una lista de objetos)
df = pd.read_json('data.json')

# JSON anidado
df = pd.read_json('data.json', orient='records')

# Desde string JSON
import json
json_string = '[{"nombre": "Juan", "edad": 28}, {"nombre": "María", "edad": 32}]'
df = pd.read_json(json_string)

# Normalizar JSON anidado
from pandas import json_normalize
df = json_normalize(json_data)

SQL

import pandas as pd
from sqlalchemy import create_engine

# Conectar a base de datos
engine = create_engine('postgresql://user:password@localhost/database')

# Cargar tabla completa
df = pd.read_sql_table('ventas', engine)

# Query personalizada
query = """
    SELECT *
    FROM ventas
    WHERE fecha >= '2024-01-01'
    AND monto > 1000
"""
df = pd.read_sql_query(query, engine)

# Forma más simple (SQLite)
import sqlite3
conn = sqlite3.connect('datos.db')
df = pd.read_sql_query("SELECT * FROM ventas", conn)
conn.close()

Ejemplo práctico: Cargar dataset de ventas

# Crear dataset de ejemplo y guardarlo
import pandas as pd
import numpy as np

# Generar datos de ejemplo
np.random.seed(42)
fechas = pd.date_range('2024-01-01', periods=100, freq='D')

ventas_data = {
    'fecha': fechas,
    'producto': np.random.choice(['Laptop', 'Mouse', 'Teclado', 'Monitor'], 100),
    'categoria': np.random.choice(['Electrónica', 'Accesorios'], 100),
    'cantidad': np.random.randint(1, 10, 100),
    'precio_unitario': np.random.choice([25, 60, 300, 800], 100),
    'region': np.random.choice(['Norte', 'Sur', 'Este', 'Oeste'], 100),
    'vendedor': np.random.choice(['Ana', 'Carlos', 'Luis', 'María'], 100)
}

df_ventas = pd.DataFrame(ventas_data)
df_ventas['total'] = df_ventas['cantidad'] * df_ventas['precio_unitario']

# Guardar a CSV
df_ventas.to_csv('ventas_2024.csv', index=False)
print("✅ Dataset creado y guardado en ventas_2024.csv")

# Cargar el CSV
df = pd.read_csv('ventas_2024.csv', parse_dates=['fecha'])
print(f"\n📊 Dataset cargado: {df.shape[0]} filas, {df.shape[1]} columnas")

Exploración inicial de datos

El flujo EDA (Exploratory Data Analysis)

# 1. Ver primeras y últimas filas
print("Primeras 5 filas:")
print(df.head())

print("\nÚltimas 5 filas:")
print(df.tail())

# 2. Información general del DataFrame
print("\nInformación del DataFrame:")
print(df.info())

# 3. Estadísticas descriptivas
print("\nEstadísticas descriptivas:")
print(df.describe())

# 4. Dimensiones
print(f"\nFilas: {df.shape[0]}")
print(f"Columnas: {df.shape[1]}")

# 5. Nombres de columnas
print(f"\nColumnas: {list(df.columns)}")

# 6. Tipos de datos
print("\nTipos de datos:")
print(df.dtypes)

Inspeccionar valores únicos

# Valores únicos por columna
print(f"Productos únicos: {df['producto'].nunique()}")
print(f"Regiones únicas: {df['region'].nunique()}")

# Ver todos los valores únicos
print("\nProductos:")
print(df['producto'].unique())

# Contar frecuencia de cada valor
print("\nDistribución de productos:")
print(df['producto'].value_counts())

# Porcentajes
print("\nDistribución porcentual:")
print(df['producto'].value_counts(normalize=True) * 100)

Detectar valores faltantes

# Contar valores nulos por columna
print("Valores nulos:")
print(df.isnull().sum())

# Porcentaje de valores nulos
print("\nPorcentaje de valores nulos:")
print((df.isnull().sum() / len(df)) * 100)

# Verificar si hay algún nulo
print(f"\n¿Hay valores nulos?: {df.isnull().any().any()}")

# Ver filas con valores nulos
filas_con_nulos = df[df.isnull().any(axis=1)]
print(f"\nFilas con valores nulos: {len(filas_con_nulos)}")

Análisis exploratorio completo

def analizar_dataset(df, nombre="Dataset"):
    """
    Realiza análisis exploratorio completo de un DataFrame.
    """
    print("="*70)
    print(f"ANÁLISIS EXPLORATORIO: {nombre}")
    print("="*70)

    # 1. Dimensiones
    print(f"\n📏 DIMENSIONES:")
    print(f"  Filas: {df.shape[0]:,}")
    print(f"  Columnas: {df.shape[1]}")
    print(f"  Total celdas: {df.shape[0] * df.shape[1]:,}")

    # 2. Tipos de datos
    print(f"\n📊 TIPOS DE DATOS:")
    print(df.dtypes.value_counts())

    # 3. Valores nulos
    print(f"\n❓ VALORES FALTANTES:")
    nulos = df.isnull().sum()
    if nulos.sum() == 0:
        print("  ✅ No hay valores nulos")
    else:
        print(nulos[nulos > 0])

    # 4. Duplicados
    print(f"\n🔄 FILAS DUPLICADAS:")
    duplicados = df.duplicated().sum()
    if duplicados == 0:
        print("  ✅ No hay filas duplicadas")
    else:
        print(f"  ⚠️ {duplicados} filas duplicadas ({duplicados/len(df)*100:.1f}%)")

    # 5. Columnas numéricas
    print(f"\n🔢 COLUMNAS NUMÉRICAS:")
    numericas = df.select_dtypes(include=['int64', 'float64']).columns
    if len(numericas) > 0:
        print(df[numericas].describe().round(2))
    else:
        print("  No hay columnas numéricas")

    # 6. Columnas categóricas
    print(f"\n📝 COLUMNAS CATEGÓRICAS:")
    categoricas = df.select_dtypes(include=['object']).columns
    for col in categoricas:
        unicos = df[col].nunique()
        print(f"  {col}: {unicos} valores únicos")
        if unicos <= 10:
            print(f"    {list(df[col].unique())}")

    # 7. Muestra de datos
    print(f"\n👀 MUESTRA DE DATOS:")
    print(df.head(3))

# Usar la función
analizar_dataset(df, "Ventas 2024")

Estadísticas por grupos

# Agrupar por producto y calcular estadísticas
print("Ventas por producto:")
por_producto = df.groupby('producto')['total'].agg([
    ('cantidad_transacciones', 'count'),
    ('total_ventas', 'sum'),
    ('promedio', 'mean'),
    ('mediana', 'median'),
    ('maximo', 'max'),
    ('minimo', 'min')
]).round(2)

print(por_producto)

# Agrupar por múltiples columnas
print("\nVentas por región y producto:")
por_region_producto = df.groupby(['region', 'producto'])['total'].sum()
print(por_region_producto)

# Crear tabla pivot
print("\nTabla pivot: Región vs Producto:")
pivot = df.pivot_table(
    values='total',
    index='region',
    columns='producto',
    aggfunc='sum',
    fill_value=0
)
print(pivot)

Filtrado básico durante exploración

# Filtrar datos para análisis específico
ventas_altas = df[df['total'] > 1000]
print(f"Ventas mayores a $1,000: {len(ventas_altas)}")

# Múltiples condiciones
ventas_laptops_norte = df[
    (df['producto'] == 'Laptop') &
    (df['region'] == 'Norte')
]
print(f"\nVentas de Laptops en región Norte: {len(ventas_laptops_norte)}")

# Filtrar por fechas
enero = df[df['fecha'].dt.month == 1]
print(f"\nVentas de enero: {len(enero)}")

# Top 10 ventas
top_ventas = df.nlargest(10, 'total')
print("\nTop 10 ventas:")
print(top_ventas[['fecha', 'producto', 'total']])

Proyecto práctico: Análisis de e-commerce

# Crear dataset de e-commerce
np.random.seed(42)

ecommerce_data = {
    'order_id': range(1, 501),
    'fecha': pd.date_range('2024-01-01', periods=500, freq='H'),
    'cliente_id': np.random.randint(1000, 2000, 500),
    'producto': np.random.choice([
        'Laptop Pro', 'Laptop Basic', 'Mouse Wireless',
        'Mouse Gaming', 'Teclado Mecánico', 'Teclado Estándar',
        'Monitor 24"', 'Monitor 27"', 'Webcam HD'
    ], 500),
    'categoria': np.random.choice([
        'Computadoras', 'Accesorios', 'Periféricos'
    ], 500),
    'cantidad': np.random.randint(1, 5, 500),
    'precio_unitario': np.random.choice([
        25, 45, 60, 80, 300, 400, 800, 1200
    ], 500),
    'descuento_porcentaje': np.random.choice([0, 5, 10, 15, 20], 500),
    'metodo_pago': np.random.choice([
        'Tarjeta Crédito', 'Tarjeta Débito', 'PayPal', 'Transferencia'
    ], 500),
    'envio': np.random.choice(['Estándar', 'Express', 'Premium'], 500)
}

df_ecom = pd.DataFrame(ecommerce_data)

# Calcular columnas derivadas
df_ecom['subtotal'] = df_ecom['cantidad'] * df_ecom['precio_unitario']
df_ecom['descuento_monto'] = df_ecom['subtotal'] * (df_ecom['descuento_porcentaje'] / 100)
df_ecom['total'] = df_ecom['subtotal'] - df_ecom['descuento_monto']

print("="*70)
print("ANÁLISIS DE E-COMMERCE - Q1 2024")
print("="*70)

# 1. Resumen general
print("\n📊 RESUMEN GENERAL:")
print(f"Total órdenes: {len(df_ecom):,}")
print(f"Total clientes únicos: {df_ecom['cliente_id'].nunique():,}")
print(f"Ingresos totales: ${df_ecom['total'].sum():,.2f}")
print(f"Ticket promedio: ${df_ecom['total'].mean():,.2f}")
print(f"Orden más alta: ${df_ecom['total'].max():,.2f}")

# 2. Por categoría
print("\n📦 ANÁLISIS POR CATEGORÍA:")
por_categoria = df_ecom.groupby('categoria').agg({
    'order_id': 'count',
    'total': ['sum', 'mean']
}).round(2)
por_categoria.columns = ['Órdenes', 'Ingresos Totales', 'Ticket Promedio']
print(por_categoria)

# 3. Por método de pago
print("\n💳 MÉTODOS DE PAGO:")
metodos = df_ecom.groupby('metodo_pago').agg({
    'order_id': 'count',
    'total': 'sum'
})
metodos['porcentaje'] = (metodos['order_id'] / len(df_ecom) * 100).round(1)
print(metodos)

# 4. Descuentos aplicados
print("\n🏷️ ANÁLISIS DE DESCUENTOS:")
con_descuento = df_ecom[df_ecom['descuento_porcentaje'] > 0]
print(f"Órdenes con descuento: {len(con_descuento)} ({len(con_descuento)/len(df_ecom)*100:.1f}%)")
print(f"Descuento promedio: {df_ecom[df_ecom['descuento_porcentaje']>0]['descuento_porcentaje'].mean():.1f}%")
print(f"Monto total descontado: ${df_ecom['descuento_monto'].sum():,.2f}")

# 5. Top productos
print("\n🏆 TOP 5 PRODUCTOS:")
top_productos = df_ecom.groupby('producto').agg({
    'order_id': 'count',
    'total': 'sum'
}).sort_values('total', ascending=False).head(5)
top_productos.columns = ['Ventas', 'Ingresos']
print(top_productos)

# 6. Tendencia temporal
print("\n📈 VENTAS POR DÍA:")
df_ecom['dia'] = df_ecom['fecha'].dt.date
ventas_diarias = df_ecom.groupby('dia')['total'].sum()
print(f"Promedio diario: ${ventas_diarias.mean():,.2f}")
print(f"Mejor día: {ventas_diarias.idxmax()} (${ventas_diarias.max():,.2f})")
print(f"Peor día: {ventas_diarias.idxmin()} (${ventas_diarias.min():,.2f})")

# 7. Análisis de envíos
print("\n📦 TIPOS DE ENVÍO:")
envios = df_ecom['envio'].value_counts()
print(envios)
print(f"\nCosto promedio por tipo de envío:")
costo_envio = {'Estándar': 0, 'Express': 10, 'Premium': 25}
df_ecom['costo_envio'] = df_ecom['envio'].map(costo_envio)
print(df_ecom.groupby('envio')['costo_envio'].mean())

Guardar datos procesados

# Guardar a CSV
df_ecom.to_csv('ecommerce_procesado.csv', index=False)

# Guardar a Excel (múltiples hojas)
with pd.ExcelWriter('reporte_completo.xlsx') as writer:
    df_ecom.to_excel(writer, sheet_name='Datos Completos', index=False)
    por_categoria.to_excel(writer, sheet_name='Por Categoría')
    top_productos.to_excel(writer, sheet_name='Top Productos')

print("✅ Datos guardados correctamente")

# Guardar solo columnas específicas
columnas_importantes = ['order_id', 'fecha', 'producto', 'total']
df_ecom[columnas_importantes].to_csv('resumen_ventas.csv', index=False)

Buenas prácticas

1. Siempre explorar antes de analizar

# ❌ Mal: analizar sin explorar
df = pd.read_csv('datos.csv')
resultado = df['ventas'].mean()  # ¿Hay nulos? ¿Outliers?

# ✅ Bien: explorar primero
df = pd.read_csv('datos.csv')
print(df.info())
print(df['ventas'].describe())
print(df['ventas'].isnull().sum())
# Ahora sí, analizar
resultado = df['ventas'].mean()

2. Documentar hallazgos

# Crear diccionario con hallazgos clave
hallazgos = {
    'total_registros': len(df),
    'fecha_inicio': df['fecha'].min(),
    'fecha_fin': df['fecha'].max(),
    'ingresos_totales': df['total'].sum(),
    'ticket_promedio': df['total'].mean(),
    'productos_unicos': df['producto'].nunique()
}

# Guardar hallazgos
import json
with open('hallazgos_exploracion.json', 'w') as f:
    json.dump(hallazgos, f, indent=2, default=str)

3. Crear funciones reutilizables

def cargar_y_explorar(ruta_archivo):
    """Carga archivo y muestra resumen exploratorio"""
    df = pd.read_csv(ruta_archivo)

    print(f"✅ Archivo cargado: {ruta_archivo}")
    print(f"   Filas: {len(df):,}")
    print(f"   Columnas: {len(df.columns)}")
    print(f"\n{df.head()}")
    print(f"\n{df.info()}")

    return df

# Usar la función
df = cargar_y_explorar('ventas.csv')

Resumen de la lección

✅ Pandas puede cargar datos desde CSV, Excel, JSON, SQL y más ✅ read_csv() y read_excel() son las funciones más comunes ✅ La exploración inicial siempre incluye: head(), info(), describe() ✅ Detectar valores nulos y duplicados es fundamental ✅ Agrupar datos con groupby() revela patrones importantes ✅ Siempre guardar datos procesados para análisis posteriores


🎯 Próxima lección: 08. Filtrado y Selección de Datos

En la siguiente lección dominarás loc, iloc y filtros avanzados para seleccionar exactamente los datos que necesitas. 🚀

¿Completaste esta lección?

Marca esta lección como completada. Tu progreso se guardará en tu navegador.