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

08. Filtrado y Selección de Datos

Domina loc, iloc, filtros condicionales y selección avanzada

20 minutos

Domina loc, iloc y filtros condicionales avanzados para seleccionar exactamente los datos que necesitas analizar.

Selección de columnas

Seleccionar una columna

import pandas as pd
import numpy as np

# Dataset de ejemplo
df = pd.DataFrame({
    'producto': ['Laptop', 'Mouse', 'Teclado', 'Monitor', 'Tablet'],
    'precio': [800, 25, 60, 300, 500],
    'stock': [15, 50, 30, 20, 10],
    'categoria': ['Computadoras', 'Accesorios', 'Accesorios', 'Monitores', 'Computadoras']
})

# Una columna (retorna Series)
precios = df['precio']
print(type(precios))  # <class 'pandas.core.series.Series'>

# Una columna (retorna DataFrame)
precios_df = df[['precio']]
print(type(precios_df))  # <class 'pandas.core.frame.DataFrame'>

Seleccionar múltiples columnas

# Múltiples columnas
subset = df[['producto', 'precio', 'stock']]
print(subset)

# Reordenar columnas
df_reordenado = df[['categoria', 'producto', 'stock', 'precio']]

# Excluir columnas
columnas_mantener = ['producto', 'precio']
df_filtrado = df[columnas_mantener]

loc: Selección por etiquetas

loc selecciona por etiquetas de índice y nombres de columnas:

# Seleccionar fila por índice
fila = df.loc[0]
print(fila)

# Seleccionar múltiples filas
filas = df.loc[0:2]  # Incluye el 2
print(filas)

# Seleccionar filas y columnas específicas
valor = df.loc[0, 'precio']  # Precio de la fila 0
print(valor)  # 800

# Múltiples filas y columnas
subset = df.loc[0:2, ['producto', 'precio']]
print(subset)

# Todas las filas, columnas específicas
productos_precios = df.loc[:, ['producto', 'precio']]

loc con índice personalizado

# DataFrame con índice personalizado
df_productos = pd.DataFrame({
    'nombre': ['Laptop Pro', 'Mouse Gamer', 'Teclado RGB'],
    'precio': [1200, 45, 80],
    'stock': [5, 20, 15]
}, index=['PROD-001', 'PROD-002', 'PROD-003'])

# Seleccionar por índice
producto = df_productos.loc['PROD-001']
print(producto)

# Rango de índices
productos = df_productos.loc['PROD-001':'PROD-003']

# Seleccionar dato específico
precio = df_productos.loc['PROD-002', 'precio']
print(f"Precio: ${precio}")  # 45

iloc: Selección por posición

iloc selecciona por posición numérica (como arrays):

# Primera fila
primera = df.iloc[0]

# Primeras 3 filas
primeras = df.iloc[0:3]  # No incluye el 3

# Última fila
ultima = df.iloc[-1]

# Seleccionar por posiciones de filas y columnas
valor = df.iloc[0, 1]  # Fila 0, Columna 1
print(valor)  # 800

# Múltiples posiciones
subset = df.iloc[0:3, 0:2]  # Primeras 3 filas, primeras 2 columnas

# Seleccionar columnas específicas por posición
columnas = df.iloc[:, [0, 2]]  # Columnas 0 y 2

Filtrado condicional

Filtros simples

# Productos con precio > 100
caros = df[df['precio'] > 100]
print(caros)

# Stock bajo (< 20)
stock_bajo = df[df['stock'] < 20]
print(stock_bajo)

# Productos de categoría específica
computadoras = df[df['categoria'] == 'Computadoras']
print(computadoras)

# Productos que NO son accesorios
no_accesorios = df[df['categoria'] != 'Accesorios']

Filtros múltiples (AND)

# Productos caros Y con poco stock
filtro = df[(df['precio'] > 100) & (df['stock'] < 20)]
print(filtro)

# Computadoras con precio > 500
computadoras_caras = df[
    (df['categoria'] == 'Computadoras') &
    (df['precio'] > 500)
]
print(computadoras_caras)

Filtros múltiples (OR)

# Productos muy caros O muy baratos
extremos = df[(df['precio'] > 500) | (df['precio'] < 50)]
print(extremos)

# Computadoras O Monitores
categorias = df[
    (df['categoria'] == 'Computadoras') |
    (df['categoria'] == 'Monitores')
]

Filtros con isin()

# Productos específicos
productos_buscar = ['Laptop', 'Monitor', 'Tablet']
filtrado = df[df['producto'].isin(productos_buscar)]
print(filtrado)

# Categorías permitidas
categorias_validas = ['Computadoras', 'Accesorios']
validos = df[df['categoria'].isin(categorias_validas)]

# NOT isin (excluir)
no_accesorios = df[~df['categoria'].isin(['Accesorios'])]

Filtros con strings

# Productos que contienen "top"
laptops = df[df['producto'].str.contains('top', case=False, na=False)]

# Productos que empiezan con "M"
empieza_m = df[df['producto'].str.startswith('M')]

# Productos que terminan con "r"
termina_r = df[df['producto'].str.endswith('r')]

# Longitud del nombre
nombres_largos = df[df['producto'].str.len() > 5]

Filtros con between()

# Precios entre 50 y 500
rango_medio = df[df['precio'].between(50, 500)]
print(rango_medio)

# Stock entre 10 y 30
stock_medio = df[df['stock'].between(10, 30)]

Proyecto práctico: Análisis de ventas

# Dataset completo de ventas
np.random.seed(42)

ventas = pd.DataFrame({
    'fecha': pd.date_range('2024-01-01', periods=200, freq='D'),
    'producto': np.random.choice([
        'Laptop Pro', 'Laptop Basic', 'Mouse Wireless',
        'Teclado Mecánico', 'Monitor 24"', 'Monitor 27"',
        'Tablet Pro', 'Tablet Basic', 'Webcam HD'
    ], 200),
    'categoria': np.random.choice([
        'Computadoras', 'Accesorios', 'Monitores', 'Tablets'
    ], 200),
    'cantidad': np.random.randint(1, 10, 200),
    'precio_unitario': np.random.choice([25, 45, 60, 80, 300, 400, 800, 1200], 200),
    'region': np.random.choice(['Norte', 'Sur', 'Este', 'Oeste'], 200),
    'vendedor': np.random.choice([
        'Ana García', 'Carlos López', 'María Rodríguez',
        'Luis Martínez', 'Carmen Fernández'
    ], 200),
    'cliente_tipo': np.random.choice(['Nuevo', 'Regular', 'VIP'], 200, p=[0.3, 0.5, 0.2])
})

ventas['total'] = ventas['cantidad'] * ventas['precio_unitario']
ventas['mes'] = ventas['fecha'].dt.month

print("="*70)
print("ANÁLISIS DE VENTAS CON FILTROS AVANZADOS")
print("="*70)

# 1. Ventas grandes (> $2000)
ventas_grandes = ventas[ventas['total'] > 2000]
print(f"\n💰 Ventas > $2,000: {len(ventas_grandes)} transacciones")
print(f"   Total: ${ventas_grandes['total'].sum():,.2f}")

# 2. Ventas de laptops en región Norte
laptops_norte = ventas[
    (ventas['producto'].str.contains('Laptop')) &
    (ventas['region'] == 'Norte')
]
print(f"\n💻 Laptops vendidas en Norte: {len(laptops_norte)}")
print(f"   Ingresos: ${laptops_norte['total'].sum():,.2f}")

# 3. Clientes VIP que compraron computadoras o tablets
vip_tech = ventas[
    (ventas['cliente_tipo'] == 'VIP') &
    (ventas['categoria'].isin(['Computadoras', 'Tablets']))
]
print(f"\n⭐ Clientes VIP (Computadoras/Tablets): {len(vip_tech)}")
print(f"   Ticket promedio: ${vip_tech['total'].mean():,.2f}")

# 4. Ventas del primer trimestre
q1 = ventas[ventas['mes'].isin([1, 2, 3])]
print(f"\n📅 Ventas Q1: {len(q1)} transacciones")
print(f"   Total Q1: ${q1['total'].sum():,.2f}")

# 5. Top vendedor en cada región
print(f"\n🏆 TOP VENDEDOR POR REGIÓN:")
for region in ventas['region'].unique():
    region_ventas = ventas[ventas['region'] == region]
    por_vendedor = region_ventas.groupby('vendedor')['total'].sum()
    top_vendedor = por_vendedor.idxmax()
    top_monto = por_vendedor.max()
    print(f"   {region}: {top_vendedor} (${top_monto:,.2f})")

# 6. Productos de precio medio (entre $100 y $500)
precio_medio = ventas[ventas['precio_unitario'].between(100, 500)]
productos_medio = precio_medio.groupby('producto')['total'].agg([
    ('ventas', 'count'),
    ('ingresos', 'sum')
]).sort_values('ingresos', ascending=False)

print(f"\n📊 PRODUCTOS DE PRECIO MEDIO ($100-$500):")
print(productos_medio.head())

# 7. Análisis de clientes nuevos
nuevos = ventas[ventas['cliente_tipo'] == 'Nuevo']
print(f"\n👶 CLIENTES NUEVOS:")
print(f"   Transacciones: {len(nuevos)}")
print(f"   Ticket promedio: ${nuevos['total'].mean():,.2f}")
print(f"   Categoría preferida: {nuevos['categoria'].value_counts().index[0]}")

# 8. Ventas problemáticas (cantidad alta pero total bajo)
problematicas = ventas[
    (ventas['cantidad'] >= 5) &
    (ventas['total'] < 300)
]
print(f"\n⚠️ Ventas con posible descuento excesivo: {len(problematicas)}")
if len(problematicas) > 0:
    print(problematicas[['fecha', 'producto', 'cantidad', 'total']].head())

query(): Sintaxis SQL-like

# Alternativa más legible para filtros complejos
caros = df.query('precio > 500')

# Múltiples condiciones
filtro = df.query('precio > 100 and stock < 20')

# Con variables externas
precio_min = 100
filtro = df.query('precio > @precio_min')

# OR condition
filtro = df.query('categoria == "Computadoras" or precio > 500')

# IN condition
categorias = ['Computadoras', 'Monitores']
filtro = df.query('categoria in @categorias')

Modificar valores con loc

# Modificar un solo valor
df.loc[0, 'precio'] = 850

# Modificar múltiples valores
df.loc[0:2, 'precio'] = df.loc[0:2, 'precio'] * 1.10

# Modificar basado en condición
df.loc[df['stock'] < 20, 'stock'] = 20  # Stock mínimo 20

# Crear columna condicional
df.loc[df['precio'] > 500, 'categoria_precio'] = 'Premium'
df.loc[df['precio'] <= 500, 'categoria_precio'] = 'Estándar'

Selección avanzada

Seleccionar por tipo de dato

# Solo columnas numéricas
numericas = df.select_dtypes(include=['int64', 'float64'])

# Solo columnas de texto
texto = df.select_dtypes(include=['object'])

# Excluir ciertos tipos
no_numericas = df.select_dtypes(exclude=['int64', 'float64'])

Seleccionar por condiciones complejas

# Productos caros Y (con poco stock O categoría Premium)
complejo = df[
    (df['precio'] > 500) &
    ((df['stock'] < 15) | (df['categoria'] == 'Computadoras'))
]

# Productos donde nombre contiene número
tiene_numero = df[df['producto'].str.contains(r'\d', regex=True)]

Sample: Muestreo aleatorio

# Muestra aleatoria de 5 filas
muestra = df.sample(n=5)

# Muestra del 20% del dataset
muestra = df.sample(frac=0.2)

# Muestra reproducible
muestra = df.sample(n=10, random_state=42)

Buenas prácticas

1. Usar paréntesis en filtros complejos

# ❌ Error común (no usar paréntesis)
# filtro = df[df['precio'] > 100 & df['stock'] < 20]  # ERROR

# ✅ Correcto
filtro = df[(df['precio'] > 100) & (df['stock'] < 20)]

2. Usar copy() para evitar SettingWithCopyWarning

# ❌ Puede causar warnings
subset = df[df['precio'] > 500]
subset['descuento'] = 0.1

# ✅ Mejor práctica
subset = df[df['precio'] > 500].copy()
subset['descuento'] = 0.1

3. Validar filtros

# Verificar que el filtro no está vacío
filtrado = df[df['precio'] > 10000]
if len(filtrado) == 0:
    print("⚠️ El filtro no retornó resultados")
else:
    print(f"✅ {len(filtrado)} registros encontrados")

Resumen de la lección

loc selecciona por etiquetas, iloc por posición numérica ✅ Filtros condicionales usan operadores: >, <, ==, !=, &, |isin() filtra por lista de valores permitidos ✅ query() ofrece sintaxis SQL-like más legible ✅ Siempre usar paréntesis en condiciones múltiples ✅ Usar copy() al crear subsets que modificarás


🎯 Próxima lección: 09. Limpieza de Datos

En la siguiente lección aprenderás a manejar valores faltantes, duplicados y datos inconsistentes. 🚀

¿Completaste esta lección?

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