Seleccion, filtrado y transformacion
Una vez cargado el DataFrame, el trabajo diario es proyectar columnas, filtrar filas y crear campos derivados. Hazlo con operaciones vectorizadas: son mas claras y mucho mas rapidas que bucles.
Seleccion de columnas
import pandas as pd
orders = pd.DataFrame({
"order_id": [101, 102, 103, 104, 105],
"country": ["ES", "PT", "ES", "FR", "ES"],
"channel": ["web", "app", "web", "store", "app"],
"amount": [120.5, 80.0, 310.0, 45.0, 15.0],
"status": ["paid", "paid", "refunded", "paid", "paid"],
})
# Una columna -> Series
amounts = orders["amount"]
# Varias columnas -> DataFrame
view = orders[["order_id", "amount", "status"]]
# Exclusion
meta = orders.drop(columns=["channel"])loc e iloc
locselecciona por etiqueta de indice y nombre de columna.ilocselecciona por posicion entera.
orders_idx = orders.set_index("order_id")
orders_idx.loc[101] # una fila
orders_idx.loc[[101, 103], ["amount", "status"]]
orders_idx.loc[101:103] # rango de etiquetas (incluye ambos extremos)
orders.iloc[0] # primera fila
orders.iloc[0:3, 0:2] # filas 0..2, columnas 0..1Para asignar, usa loc de forma explicita:
orders = orders.copy()
orders.loc[orders["amount"] < 20, "status"] = "review"Evita el patron encadenado df[df.a > 0]["b"] = ... (SettingWithCopy).
Filtros booleanos
mask = (orders["status"] == "paid") & (orders["amount"] >= 50)
paid_big = orders.loc[mask]
# isin / between
iberia = orders[orders["country"].isin(["ES", "PT"])]
mid = orders[orders["amount"].between(50, 200)]
# Negacion
not_refund = orders[orders["status"] != "refunded"]
# o: orders[~orders["status"].eq("refunded")]Operadores: usa &, |, ~ con parentesis. and / or de Python no funcionan sobre Series.
query (opcional)
orders.query("status == 'paid' and amount >= 50")
orders.query("country in ['ES', 'PT']")Util en notebooks; en scripts largos muchos equipos prefieren mascaras explicitas.
Ordenar y rankear
orders.sort_values(["country", "amount"], ascending=[True, False])
orders.assign(rank_amount=orders["amount"].rank(ascending=False, method="dense"))Transformaciones de columnas
Asignacion vectorizada
orders = orders.copy()
orders["amount_vat"] = orders["amount"] * 1.21
orders["is_es"] = orders["country"] == "ES"assign (encadenable, no muta in-place)
clean = (
orders
.assign(
amount_vat=lambda d: d["amount"] * 1.21,
channel=lambda d: d["channel"].str.lower(),
)
.loc[lambda d: d["status"] == "paid"]
)map, replace, where
region = {"ES": "south", "PT": "south", "FR": "central"}
orders["region"] = orders["country"].map(region)
orders["status_norm"] = orders["status"].replace({"refunded": "refund"})
# where: mantener valor si True, si no NaN u otro valor
orders["amount_paid"] = orders["amount"].where(orders["status"] == "paid", 0.0)apply: usalo con criterio
apply es flexible y lento. Reservealo para logica que no cabe en vectorizacion:
# Preferible
orders["label"] = pd.cut(orders["amount"], bins=[0, 50, 150, 10_000], labels=["L", "M", "H"])
# Solo si hace falta
def tag(row):
if row["status"] == "refunded":
return "bad"
if row["amount"] > 200:
return "vip"
return "ok"
orders["tag"] = orders.apply(tag, axis=1) # mas lentoStrings
orders["channel"] = orders["channel"].astype("string").str.strip().str.lower()
orders["country"].str.len()
orders["channel"].str.contains("web", case=False, na=False)
orders["order_code"] = "ORD-" + orders["order_id"].astype("string")Metodos .str requieren dtype string/object; valores nulos se propagan (controla con na=).
Renombrar y reordenar
orders = orders.rename(columns={"amount": "amount_eur"})
orders = orders[["order_id", "country", "amount_eur", "status", "channel"]]Explode y columnas listas
baskets = pd.DataFrame({
"order_id": [1, 2],
"skus": [["A", "B"], ["A"]],
})
baskets.explode("skus", ignore_index=True)Errores habituales
- Mezclar
and/orcon Series en lugar de&/|. - Asignacion encadenada y
SettingWithCopyWarningignorado. - Usar
apply(axis=1)para sumas, ratios o maps simples. - Filtrar y olvidar que el indice queda "agujereado" (usar
reset_index(drop=True)si molesta). - Comparar floats con
==exacto cuando hay redondeo; usa rangos onp.isclosesi aplica.
Buenas practicas
- Filtra con
loc[mask, columns]cuando seleccionas y proyectas a la vez. - Encadena con
.assign/.loc/.pipepara pipelines legibles. - Normaliza strings (
strip,lower) antes de filtrar o agrupar. - Prefiere
isin,between,str.containsa bucles. - Tras transformar, valida con
value_counts,describey conteo de filas.
Ejercicios
- Filtra pedidos
paiddeESoPTcon amount entre 50 y 300. - Crea
amount_vatyregionconassigny un dict de mapeo. - Sustituye status
refundedporrefundconreplace. - Ordena por
amountdescendente y anade un ranking denso. - Reescribe un
applyinnecesario usandonp.whereopd.cut.
Siguiente paso
Con columnas derivadas y filtros claros, el capitulo 5 se centra en nulos, duplicados, tipos sucios y validaciones.
