Excel y documentos de trabajo

Crear un resumen mensual con groupby de pandas y comprobar los totales

Resume datos CSV a nivel de pedido por mes con pandas y después concilia los recuentos, unidades e importes agrupados con la fuente. Un pequeño ejemplo sintético permite verificar manualmente todos los resultados esperados.

Ver el índice

La traducción se ha realizado con IA. Comprueba el código, las unidades y los valores junto con el original. La revisión por hablantes nativos de cada idioma aún no se ha completado. English

Para quién esEsta guía está dirigida a personas que necesitan un resumen CSV mensual repetible y quieren comprobaciones explícitas de que la agregación no perdió registros.

Preparación
  • Python 3.12 y un comando de terminal que inicie esa versión.
  • pandas instalado con python -m pip install pandas.
  • Una carpeta de trabajo donde el script pueda crear una nueva carpeta dentro de outputs.
  • El CSV de origen debe contener las columnas order_id, order_date, units y amount.

01Definir las medidas mensuales

Este ejemplo genera una fila por calendar month con tres medidas: order count, total units y total amount. La fuente sintética utiliza una fila por order, por lo que contar filas equivale a contar orders en este dataset concreto.

El paso importante es la reconciliation. Después de groupby, los valores mensuales se vuelven a sumar y se comparan con los source totals. Un summary puede parecer razonable incluso cuando algunas rows se filtraron, duplicaron o eliminaron accidentalmente.

02Crear el CSV sintético

El dataset siguiente es sintético y fue creado específicamente para este artículo. Guárdalo como monthly_orders.csv. Contiene siete orders entre enero, febrero y marzo de 2026.

csv
order_id,order_date,team,units,amount
O001,2026-01-05,North,2,40
O002,2026-01-20,South,3,60
O003,2026-02-02,North,1,25
O004,2026-02-18,South,4,100
O005,2026-02-28,North,2,50
O006,2026-03-03,South,5,125
O007,2026-03-15,North,1,30

Los source totals son lo bastante sencillos como para comprobarlos manualmente. Hay 7 orders. Units suman 18: 2 + 3 + 1 + 4 + 2 + 5 + 1. Amount suma 430: 40 + 60 + 25 + 100 + 50 + 125 + 30.

MedidaTotal de origen
Orders7
Units18
Amount430

03Calcular manualmente las filas mensuales esperadas

Enero contiene O001 y O002. Febrero contiene O003, O004 y O005. Marzo contiene O006 y O007. Al sumar cada group se obtiene la tabla mensual esperada.

monthordersunitsamount
2026-0125100
2026-0237175
2026-0326155

Los monthly totals concilian con la fuente: 2 + 3 + 2 = 7 orders, 5 + 7 + 6 = 18 units y 100 + 175 + 155 = 430 amount.

04Crear y verificar el resumen con pandas

Guarda el script siguiente como monthly_summary_pandas.py. Valida las required columns, rechaza order IDs ausentes o duplicados, interpreta las fechas con un único formato explícito, convierte las numeric columns, agrupa por month, concilia los totals y escribe el resultado únicamente después de superar esas comprobaciones.

python
from pathlib import Path
import pandas as pd

SOURCE = Path("monthly_orders.csv")
OUTPUT_DIR = Path("outputs") / "monthly_summary_result"
OUTPUT = OUTPUT_DIR / "monthly_summary.csv"
REQUIRED = ["order_id", "order_date", "units", "amount"]


def main() -> None:
    if not SOURCE.is_file():
        raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
    if OUTPUT_DIR.exists():
        raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")

    data = pd.read_csv(SOURCE, dtype={"order_id": "string"})

    missing = [name for name in REQUIRED if name not in data.columns]
    if missing:
        raise ValueError(f"Missing required columns: {missing}")
    if data.empty:
        raise ValueError("Source CSV contains no data rows.")
    if data[REQUIRED].isna().any().any():
        raise ValueError("A required field contains a missing value.")
    if data["order_id"].str.strip().eq("").any():
        raise ValueError("order_id contains a blank value.")
    if data["order_id"].duplicated().any():
        raise ValueError("Duplicate order_id found.")

    data["order_date"] = pd.to_datetime(
        data["order_date"], format="%Y-%m-%d", errors="raise"
    )
    data["units"] = pd.to_numeric(data["units"], errors="raise")
    data["amount"] = pd.to_numeric(data["amount"], errors="raise")
    data["month"] = data["order_date"].dt.to_period("M").astype(str)

    summary = (
        data.groupby("month", as_index=False, sort=True)
        .agg(
            orders=("order_id", "count"),
            units=("units", "sum"),
            amount=("amount", "sum"),
        )
    )

    source_totals = (len(data), data["units"].sum(), data["amount"].sum())
    summary_totals = (
        summary["orders"].sum(),
        summary["units"].sum(),
        summary["amount"].sum(),
    )
    if summary_totals != source_totals:
        raise RuntimeError("Monthly totals do not reconcile with the source.")

    OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_DIR.mkdir()
    summary.to_csv(OUTPUT, index=False)

    check = pd.read_csv(OUTPUT)
    if check.to_dict("records") != summary.to_dict("records"):
        raise RuntimeError("Saved CSV does not match the calculated summary.")

    print(f"Source rows: {len(data)}.")
    print(f"Months: {len(summary)}.")
    print(f"Verified totals: units={source_totals[1]}, amount={source_totals[2]}.")
    print(f"Output: {OUTPUT.as_posix()}")


if __name__ == "__main__":
    main()
text
python monthly_summary_pandas.py

05Comparar la salida esperada

La salida debería contener tres data rows ordenadas por month. El contenido esperado del CSV se muestra a continuación.

csv
month,orders,units,amount
2026-01,2,5,100
2026-02,3,7,175
2026-03,2,6,155

La salida de consola esperada que aparece a continuación se obtuvo manualmente a partir de los datos sintéticos y del código. No es un execution log capturado.

text
Source rows: 7.
Months: 3.
Verified totals: units=18, amount=430.
Output: outputs/monthly_summary_result/monthly_summary.csv

06Conciliar el resultado y reconocer errores comunes

  1. Cuenta las source rows: 7.
  2. Suma los monthly orders: 2 + 3 + 2 = 7.
  3. Suma las monthly units: 5 + 7 + 6 = 18.
  4. Suma el monthly amount: 100 + 175 + 155 = 430.
  5. Confirma que la salida contenga exactamente 2026-01, 2026-02 y 2026-03.
  6. Ejecuta el script de nuevo sin cambiar OUTPUT_DIR. Debería detenerse con FileExistsError en lugar de sustituir el resultado existente.
SíntomaQué comprobar
ModuleNotFoundError for pandasInstala pandas en el mismo Python environment con python -m pip install pandas.
Missing required columnsComprueba el CSV header antes de cambiar el expected schema.
Date parsing errorConfirma que cada order_date utilice YYYY-MM-DD.
Numeric conversion errorInspecciona units y amount para detectar text, currency symbols o separators que requieran una cleanup rule explícita.
Duplicate order_id foundDetermina si el ID repetido es un accidental duplicate o representa un data model diferente.
FileExistsErrorRevisa el resultado anterior y utiliza una output folder nueva en lugar de sobrescribirlo.

Que coincidan los grand totals no demuestra que todos los months sean correctos, porque dos errores mensuales pueden compensarse entre sí. Para reports importantes, inspecciona también varias source rows conocidas y month-level subtotals.

07Comprender los límites

El ejemplo supone una fila por order e importes enteros. Si un order ocupa varias line-item rows, utiliza una rule como order_id.nunique() en lugar de row count. Los monetary data reales también pueden necesitar fixed-point integer units o procesamiento basado en Decimal en lugar de ordinary floating-point arithmetic.

Month se deriva directamente de una date sin time-zone handling. Si los datos reales contienen timestamps de varias zones, define la business time zone antes de agrupar. Refunds, cancellations, filters y missing records también necesitan business rules explícitas; la arithmetic reconciliation por sí sola no puede determinar si esas rows deben incluirse en el report.

Registro de ejecución y verificación

2026-09-20 · ejemplo revisado manualmente · objetivo: Python 3.12 · se requiere pandas · sin ejecución

  • Se contaron manualmente 7 source records entre enero, febrero y marzo de 2026.
  • Se calcularon manualmente source totals de 18 units y 430 amount.
  • Se calculó manualmente enero como 2 orders, 5 units, 100 amount; febrero como 3 orders, 7 units, 175 amount; y marzo como 2 orders, 6 units, 155 amount.
  • Se conciliaron manualmente los monthly totals de nuevo con 7 orders, 18 units y 430 amount.
  • Se revisó el script para required-column checks, duplicate IDs, explicit date parsing, numeric conversion, groupby aggregation, reconciliation, saved-file comparison y output collision protection.
  • Se obtuvieron manualmente el CSV esperado y la salida de consola.
Límites de la verificación
  • El código no fue ejecutado por el autor de esta respuesta; aquí no se probaron el comportamiento de pandas ni las filesystem writes.
  • No se probaron Decimal currency, time zones, refunds, cancellations, line-item order data ni very large files.
  • Las URL de la documentación oficial se proporcionaron a partir de ubicaciones de documentación conocidas, pero no se comprobaron en línea.

Criterios de redacción y verificación de todo el sitio

Fuentes de referencia

Las explicaciones y los ejemplos son de elaboración propia. Puedes consultar los comportamientos y conceptos relacionados en las siguientes fuentes oficiales.