# manejo-de-datos-en-pandas
Manejo de datos en Pandas
Volver a la portada del cursoPreparación de datos para proyecto
Sección 6 · Proyecto Final
Preparación de datos para el proyecto
En las lecciones anteriores cada paso de limpieza se hizo en su propia celda: quitar duplicados, normalizar ciudades, convertir precios y fechas, rellenar faltantes. Funciona, pero tiene un problema: si mañana llega el archivo de otra sucursal, hay que volver a ejecutar quince celdas en el orden correcto. Para el proyecto final la limpieza se empaqueta en una función reutilizable, escrita como una cadena de métodos y vigilada con assert. Esta lectura arma esa función paso a paso; el notebook analisis-pedidos la aplica completa.
1. El checklist de limpieza
Antes de escribir código conviene listar las decisiones. Para el CSV de pedidos de PyShop son estas:
| Problema | Decisión | Herramienta |
|---|---|---|
| filas repetidas | quitarlas | drop_duplicates() |
| 16 formas de escribir 5 ciudades | normalizar | .str.strip().str.lower() + map |
precio como texto con «$» y comas | convertir a número | .str.replace + pd.to_numeric |
| fechas imposibles | convertir y descartar la fila | pd.to_datetime(errors="coerce") + dropna(subset=...) |
| precio vacío | mediana del mismo producto | groupby(...).transform("median") |
| cantidad vacía | mediana | fillna |
| calificación vacía | dejarla vacía | nada: es un faltante estructural |
La última fila es tan importante como las otras: los pedidos no entregados no se califican, y rellenar ese vacío inventaría opiniones.
2. Encadenamiento de métodos
Casi todos los métodos de pandas devuelven un DataFrame nuevo, así que se pueden encadenar: el resultado de uno es la entrada del siguiente. Entre paréntesis, la cadena se escribe un método por línea y se lee de arriba abajo como una receta.
La pieza clave es assign, que crea o reemplaza columnas y devuelve el DataFrame resultante. Si el valor es una lambda, pandas la llama con el DataFrame en ese punto de la cadena. Por eso lambda d: d["ciudad"]... trabaja sobre los datos ya sin duplicados, aunque el nombre original df nunca exista.
def a_numero(precio):
sin_simbolos = precio.str.replace(r"[$,]", "", regex=True)
return pd.to_numeric(sin_simbolos)
def limpiar(texto):
return (
pd.read_csv(io.StringIO(texto))
.drop_duplicates()
.assign(
ciudad=lambda d: d["ciudad"].str.strip()
.str.lower().map(MAPA_CIUDADES),
precio_unitario=lambda d: a_numero(d["precio_unitario"]),
fecha=lambda d: pd.to_datetime(d["fecha"], errors="coerce"),
)
.assign(
precio_unitario=lambda d: d["precio_unitario"].fillna(
d.groupby("producto")["precio_unitario"]
.transform("median")),
cantidad=lambda d: d["cantidad"].fillna(d["cantidad"].median()),
)
.dropna(subset=["fecha"])
.assign(total=lambda d: d["cantidad"] * d["precio_unitario"])
)
El orden importa. Hay dos assign porque la mediana del precio solo se puede calcular cuando el precio ya es número. El total va al final, cuando cantidad y precio ya no tienen huecos. Con el CSV del curso, limpiar devuelve un DataFrame de 991 filas y 11 columnas, y la única columna con nulos es la calificación (369).
3. Validar con assert
Una función de limpieza hace supuestos: que solo hay diez variantes de ciudad, que el precio solo trae «$» y comas. Si el archivo del próximo año rompe un supuesto, conviene que el error aparezca en la limpieza y no en una gráfica equivocada. assert condicion, "mensaje" detiene la ejecución si la condición es falsa.
def validar(df):
assert df["pedido_id"].is_unique, "hay pedidos repetidos"
assert df["ciudad"].notna().all(), "hay ciudades sin mapear"
assert df["ciudad"].nunique() == 5
columnas = ["cantidad", "precio_unitario", "fecha", "total"]
assert df[columnas].notna().all().all()
return True
El segundo assert vigila al map: una ciudad que no está en el diccionario se convierte en NaN sin avisar.
4. Guardar el resultado
to_csv("pedidos_limpios.csv", index=False) guarda el archivo sin la columna del índice. Al volver a leerlo, la fecha llega como texto a menos que se indique parse_dates=["fecha"]. Una buena práctica es releer el archivo y comprobar que la forma y el ingreso coinciden: con el CSV del curso, 991 filas y un ingreso sin cancelados de 1,350,460 MXN.
Trampas comunes
- Usar
dfdentro de la cadena.assign(x=df["a"] * 2)toma el DataFrame original, con duplicados; usalambda d: d["a"] * 2. - Rellenar antes de convertir. La mediana de una columna de texto no existe.
- Calcular el total antes de rellenar. Cada
NaNse propaga al total. - Cadenas de veinte métodos. Si un paso necesita explicación, sácalo a una función con nombre, como
a_numero. - Validar a ojo. «Se ve bien» no detecta una ciudad nueva; un
assertsí.
Antes de empezar el análisis del proyecto, ten listas tres funciones: una que mida la calidad, limpiar y validar. El resto del trabajo se construye sobre ellas.