Saltar al contenido

Indice / Cierre / 10.1

Recetario consolidado

Una tabla por unidad, sin prosa. Es la página para tener abierta al lado mientras se resuelve un ejercicio, y la que conviene imprimir.

U1 · Archivos, excepciones y formatos

Biblioteca estándar
ExpresiónQué haceOjo con
with open(r, encoding="utf-8", newline="") as f:La apertura correctaLos dos argumentos van siempre
csv.DictReader(f)Filas como dictTodo llega como str
csv.DictWriter(f, fieldnames=COLS, extrasaction="ignore")Escribe por nombreFalta el writeheader()
json.load(f) / json.dump(o, f, ensure_ascii=False, indent=2)JSONSin ensure_ascii=False quedan los escapes
except ValueError as error:Atrapa la esperadaexcept Exception tapa tus propios bugs
raise Propia(...) from errorTraduce sin perder la causaSolo dentro de un except
NULOS = {"", "-", "N/A", "NaN", "null"}Los cinco tokens del corpusfloat("NaN") no falla
log.warning("%s de %s", a, b)Mensaje perezosoArgumentos por coma, no f-string
assert len(ok) + len(no) == len(entrada)Que no se pierda nadaLas dos listas se llenan por separado
Path(__file__).resolve().parentLa carpeta del programaUna ruta relativa se resuelve contra el cwd

U2 · Tablas, claves y calidad

Las tres preguntas, y el vocabulario
ExpresiónContesta
len({f["id"] for f in filas}) == len(filas)¿Sirve como clave primaria?
Counter(f["clave"] for f in filas)¿Cuál se repite y cuántas veces?
{(f["a"], f["b"]) for f in filas}Los valores de una clave compuesta
hijos - {p["id"] for p in padres}Las referencias huérfanas
{p["id"] for p in padres} - referenciadosLos padres sin hijos - que no es un error
Cardinalidad: 1:1 mantiene las filas · 1:N deja el grano del lado N · N:M multiplica
Vocabulario: ausente · no_numerico · fuera_de_rango · fecha_invalida · dominio_desconocido · duplicado · huerfano · negativo_incoherente · atipico · restriccion_violada
Capas: 1 lectura · 2 parseo · 3 validación · 4 negocio. Las dos primeras avisan solas; de las otras dos, nadie

U3 · NumPy

Arrays
ExpresiónQué haceOjo con
a.shape / a.dtypeForma y tipoLo primero, siempre
np.genfromtxt(r, delimiter=",", skip_header=1, usecols=…)CSV → arrayLos huecos llegan como NaN
a.astype(np.int64)Copia convertidaTrunca; y un NaN da el entero mínimo
a.reshape(-1, 1)Vector a columnaDevuelve una vista
a.TIntercambia los ejesNo es lo mismo que reshape
a.mean(axis=1)Un promedio por filaEl axis es el que desaparece
(a > x).sum(axis=1)Cuenta por filaSumar booleanos es contar
(a > 1) & (a < 5)Combinar máscarasParéntesis obligatorios; and falla
a[mask] = valorReemplaza en el originalsub = a[mask] devuelve una copia
np.where(cond, si, no)Reemplaza sin filtrarConserva el shape
np.nanmean(a, axis=1, keepdims=True)(n, 1) para restar por filaSin keepdims, ValueError
np.nanpercentile(a, [25, 75])Los cuartilesLa base del IQR
np.clip(a, bajo, alto)RecortaInventa valores en el límite
np.argsort(v)Las posiciones en ordenotro[np.argsort(v)] reordena otro

U4 · pandas: fundamentos

Leer, mirar, seleccionar
ExpresiónQué haceOjo con
pd.read_csv(r)CSV → DataFrame- no es un token de ausente
df.head() / df.shape / df.dtypes / df.info()Las cuatro primeras miradasEl texto es str, no object
df["c"] vs df[["a", "b"]]Series vs DataFrameUn corchete o dos
df.loc[filas, columnas]Por etiquetaEl corte incluye el final
df.iloc[filas, columnas]Por posiciónEl corte excluye el final
df[(df.a > 1) & (df.b < 2)]FiltrarParéntesis; and falla
df["c"].isin([...]) / .between(a, b)Pertenencia y rangoNaN no cumple ninguna
df["c"].str.contains(x, na=False)TextoSin na=False, el NaN propaga
df.set_index("id") / .reset_index()Índicedf.index.is_unique antes de nada
df.assign(nueva=…)Columna derivada, encadenableDevuelve una copia
df.loc[mask, "col"] = 0Asignar filtradodf[mask]["col"] = 0 no hace nada
df.sort_values(by, ascending, na_position)OrdenarTerminar en una columna única

U5 · pandas: calidad y limpieza

Diagnosticar y limpiar
ExpresiónQué haceOjo con
df.isna().sum()Ausentes por columnaDepende de na_values
df.duplicated(subset=).sum()DuplicadosDefinir por qué columnas
df["c"].value_counts(dropna=False)El dominio realdropna=False o los huecos no se ven
pd.to_numeric(s, errors="coerce")Texto → número, lo malo a NaNContar los NaN nuevos
pd.to_datetime(s, errors="coerce", dayfirst=)Texto → fechaUna fecha imposible da NaT en silencio
s.str.strip().str.lower()Normalizar textoAntes de deduplicar
read_csv(r, na_values=["-", "N/A"])Declarar los tokensEl guion no viene por defecto
df.fillna(valor) / df.dropna(subset=[...])Imputar / descartarSin subset, dropna borra de más
df.drop_duplicates(subset=, keep="last")Deduplicarkeep="first" conserva la primera del archivo, que en catalogo_sistema_a es la versión vieja
df["c"].clip(bajo, alto)RecortarInventa valores en el límite
df.copy().rename(...).assign(...)Pipeline encadenadoEmpezar por .copy()
df.pipe(funcion)Un paso con nombreFunciones puras: reciben y devuelven

U6 · pandas: combinar y resumir

Unir, agrupar, reformar
ExpresiónQué haceOjo con
pd.concat([a, b], ignore_index=True)Apila filasSin ignore_index, el índice se repite
a.merge(b, on="id", how="inner")Cruceinner pierde filas en silencio
how="left"Conserva todo lo de la izquierdaLo que no cruza queda en NaN
indicator=TrueColumna _mergeEl anti-join es _merge == "left_only"
validate="m:1"MergeError si la clave no es únicaPonerlo siempre
df.groupby("c").agg(...)Split-apply-combinedropna=True borra la categoría nula
as_index=FalseLa clave queda como columnaEvita un reset_index()
df.pivot_table(index, columns, values, aggfunc, fill_value)Largo → anchoSin aggfunc promedia por defecto
df.melt(id_vars, var_name, value_name)Ancho → largoEs el inverso del anterior
df.sort_values("fecha").groupby("u")["fecha"].diff()Hueco con el anteriorOrdenar antes, o los huecos son basura
df.set_index("fecha").resample("D").sum()Serie diariaIncluye los días sin datos; groupby no
(df["hueco"] > umbral).cumsum()Número de sesiónEl corte por hueco, en una línea

U7 · Entrada y salida

Formatos
ExpresiónQué haceOjo con
read_csv(r, sep=";", decimal=",")CSV «europeo»Sin thousands="." queda como texto
read_csv(r, usecols=, dtype=, parse_dates=)Leer solo lo necesariodtype evita inferencias sorpresa
read_csv(r, on_bad_lines="warn")Filas irregularesEl default es fallar
pd.json_normalize(datos, record_path, meta)JSON anidado → tablaLas claves que faltan quedan en NaN
pd.read_excel(r, sheet_name=None)Todas las hojas, como dictNecesita openpyxl
with pd.ExcelWriter(r) as w:Varias hojasdf.to_excel(w, sheet_name=…)
df.to_csv(r, index=False, encoding="utf-8")Escribirindex=False casi siempre
df["c"] = df["c"].round(2)RedondearColumna por columna, no float_format
pd.read_sql_query(sql, con, params=(x,))SQL → DataFrameNunca f-strings en el SQL
df.to_sql(t, con, if_exists="replace", index=False)DataFrame → SQLreplace borra la tabla

Las seis reglas de contrato

Regla
R1Orden total: terminar siempre en una columna única de la salida
R2Vocabulario cerrado de motivos de rechazo, el mismo en las 39 prácticas
R3Redondear en el DataFrame, columna por columna, no al escribir
R4Nada de nan en el archivo: el ausente escrito es campo vacío
R5El objetivo nunca se escribe a mano: lo produce el código
R6La definición de correcto es que comparar.py no imprima nada

Los cinco números de todo informe

  1. Cuántas filas entraron.
  2. Cuántas salieron, y qué porcentaje es eso.
  3. El desglose por motivo, con el vocabulario cerrado.
  4. Dónde está el detalle: el archivo de rechazos, fila por fila.
  5. El invariante: válidas + rechazadas == entrada.

Ponete a prueba

1. ¿Cuál de estos tokens no lo reconoce read_csv como ausente si no se lo pedís?Nivel 1 · Recordar
2. En una grilla (10, 24), ¿qué elimina axis=1?Nivel 1 · Recordar
3. ¿Qué diferencia hay entre df.loc[0:3] y df.iloc[0:3] en un DataFrame con índice 0,1,2,3,4?Nivel 2 · Predecir la salida
4. ¿Qué hace validate="m:1" en un merge?Nivel 2 · Predecir la salida
5. Tenés que redondear importe a 2 decimales y tasa a 4 en el mismo archivo de salida. ¿Cómo?Nivel 3 · Elegir entre dos casi correctas
6. ¿Cuál de estas cuatro asignaciones no modifica el DataFrame?Nivel 3 · Elegir entre dos casi correctas
7. Un informe agrupado por categoria suma 1.909.786 y el total del DataFrame es 11.985.252. Ninguna fila se perdió y no hubo error. ¿Qué pasó?Nivel 4 · Encontrar el defecto