1. Faro

Nivel 7 · ¿Por qué no encajan los objetivos?

La solicitud

De Megan Collins, Directora de Ventas

“Un compañero unió los recuentos mensuales de pedidos con mi hoja de objetivos y todos los meses salieron vacíos. Los objetivos están ahí, seguro. ¿Qué falló?”

Tu objetivo

Elige la explicación del join vacío.

El código es `monthly = orders.set_index("order_date").resample("ME").size().rename("n_orders").reset_index()`, y luego `targets.merge(monthly, left_on="month", right_on="order_date", how="left")`. La columna `month` de los objetivos contiene fechas como 2025-01-01, 2025-02-01. ¿Por qué n_orders es NaN en todas las filas?

Elige una respuesta y luego compruébala.

La lección

Una etiqueta es una fecha

resample agrupa filas en periodos, pero el resultado sigue indexado por fechas, una por periodo. Qué fecha representa “enero de 2025” depende del alias de frecuencia:

by_start = orders.set_index("order_date").resample("MS").size()
by_end = orders.set_index("order_date").resample("ME").size()
by_start.index[0]   # Timestamp('2023-01-01')
by_end.index[0]     # Timestamp('2023-01-31')

Los recuentos son idénticos (192 pedidos en enero de 2023 de cualquiera de las dos maneras). Solo difieren las etiquetas. Para pandas, 2023-01-01 y 2023-01-31 son simplemente dos fechas distintas, nada más.

Donde muerde: los merges

La mayoría de las tablas mensuales con las que unirás (objetivos, presupuestos, exportaciones del libro mayor) usan el primer día del mes. Un merge compara los valores de las claves exactamente, así que las etiquetas de final de mes no coinciden con ninguna:

targets.merge(monthly, left_on="month", right_on="order_date", how="left")
# n_orders is NaN on every row

Un left join nunca da error cuando las claves no coinciden; rellena con NaN las columnas de la derecha. Con un join inner obtendrías una tabla vacía. Ambos son fáciles de pasar por alto cuando trabajas deprisa.

Dos soluciones

Etiqueta los meses como lo hace la otra tabla:

monthly = orders.set_index("order_date").resample("MS").size()

O quita el día de ambos lados convirtiendo las fechas en periodos mensuales. Un periodo es un mes entero en lugar de un día suyo, así que 2023-01-01 y 2023-01-31 pasan ambos a 2023-01:

targets["month"] = targets["month"].dt.to_period("M")
monthly.index = monthly.index.to_period("M")

Los periodos también son lo que quieres cuando la otra tabla guarda los meses como texto del tipo "2025-01": conviértelo con pd.PeriodIndex(..., freq="M") o convierte tus periodos en texto con .astype(str).

Comprueba las claves antes de fiarte de un join

Un hábito rápido detecta toda esta clase de errores: antes de unir, imprime las primeras claves de ambos lados, y tras un merge left, cuenta los NaN en una columna que siempre debería estar rellena. “Todas las filas vacías” casi nunca son datos reales; casi siempre son claves que no coinciden, y lo mismo vale para los datos semanales, donde "W-SUN" etiqueta una semana con su domingo y otros sistemas suelen usar el lunes.

Errores comunes

  • Mezclar "ME" y "MS" entre dos tablas que se van a unir.
  • Fiarte de un merge left porque no dio ningún error.
  • Comparar una columna datetime con texto de mes como "2025-01"; convierte antes uno de los lados.
  • Olvidar que las semanas también tienen etiquetas: "W-SUN" y "W-MON" dan las mismas semanas con distintos días finales.

Elige qué puede ejecutarse en este sitio. Puedes cambiarlo cuando quieras desde "Configurar cookies", al pie de cada página.