Excel e documentos de trabalho

Crie um resumo mensal com groupby do pandas e verifique os totais

Resuma dados CSV em nível de pedido por mês com pandas e depois reconcilie as contagens, unidades e valores agrupados com a fonte. Um pequeno exemplo sintético facilita a verificação manual de todos os resultados esperados.

Ver o sumário

A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. English

Para quem éEste guia é voltado a pessoas que precisam de um resumo CSV mensal repetível e querem verificações explícitas de que a agregação não perdeu registros.

Preparação
  • Python 3.12 e um comando de terminal que inicie essa versão.
  • pandas instalado com python -m pip install pandas.
  • Uma pasta de trabalho em que o script possa criar uma nova pasta dentro de outputs.
  • O CSV de origem deve conter as colunas order_id, order_date, units e amount.

01Defina as medidas mensais

Este exemplo produz uma linha por calendar month com três medidas: order count, total units e total amount. A fonte sintética usa uma linha por order, portanto contar linhas equivale a contar orders neste dataset específico.

A etapa importante é a reconciliation. Depois do groupby, os valores mensais são somados novamente e comparados com os source totals. Um summary pode parecer razoável mesmo quando rows foram acidentalmente filtradas, duplicadas ou descartadas.

02Crie o CSV sintético

O dataset a seguir é sintético e foi criado especificamente para este artigo. Salve-o como monthly_orders.csv. Ele contém sete orders entre janeiro, fevereiro e março 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

Os source totals são simples o suficiente para serem verificados manualmente. Há 7 orders. Units totalizam 18: 2 + 3 + 1 + 4 + 2 + 5 + 1. Amount totaliza 430: 40 + 60 + 25 + 100 + 50 + 125 + 30.

MedidaTotal da fonte
Orders7
Units18
Amount430

03Calcule manualmente as linhas mensais esperadas

Janeiro contém O001 e O002. Fevereiro contém O003, O004 e O005. Março contém O006 e O007. Somar cada group fornece a tabela mensal esperada.

monthordersunitsamount
2026-0125100
2026-0237175
2026-0326155

Os monthly totals reconciliam com a fonte: 2 + 3 + 2 = 7 orders, 5 + 7 + 6 = 18 units e 100 + 175 + 155 = 430 amount.

04Crie e verifique o resumo com pandas

Salve o script a seguir como monthly_summary_pandas.py. Ele valida as required columns, rejeita order IDs ausentes ou duplicados, interpreta datas com um único formato explícito, converte numeric columns, agrupa por month, reconcilia os totals e grava o resultado somente depois que essas verificações passam.

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

05Compare a saída esperada

A saída deve conter três data rows ordenadas por month. O conteúdo esperado do CSV é mostrado abaixo.

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

A saída esperada do console abaixo foi derivada manualmente a partir dos dados sintéticos e do código. Não é um execution log capturado.

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

06Reconcilie o resultado e reconheça erros comuns

  1. Conte as source rows: 7.
  2. Some os monthly orders: 2 + 3 + 2 = 7.
  3. Some as monthly units: 5 + 7 + 6 = 18.
  4. Some o monthly amount: 100 + 175 + 155 = 430.
  5. Confirme que a saída contém exatamente 2026-01, 2026-02 e 2026-03.
  6. Execute o script novamente sem alterar OUTPUT_DIR. Ele deve parar com FileExistsError em vez de substituir o resultado existente.
SintomaO que verificar
ModuleNotFoundError for pandasInstale pandas no mesmo Python environment com python -m pip install pandas.
Missing required columnsVerifique o CSV header antes de alterar o expected schema.
Date parsing errorConfirme que todo order_date usa YYYY-MM-DD.
Numeric conversion errorInspecione units e amount para text, currency symbols ou separators que exijam uma cleanup rule explícita.
Duplicate order_id foundDetermine se o ID repetido é um accidental duplicate ou representa um data model diferente.
FileExistsErrorRevise o resultado anterior e use uma nova output folder em vez de sobrescrevê-lo.

Grand totals correspondentes não provam que todos os months estão corretos, porque dois erros mensais podem se compensar. Para reports importantes, inspecione também algumas source rows conhecidas e month-level subtotals.

07Entenda os limites

O exemplo pressupõe uma linha por order e amounts inteiros. Se um order ocupar várias line-item rows, use uma rule como order_id.nunique() em vez de row count. Monetary data reais também podem exigir fixed-point integer units ou processamento baseado em Decimal em vez de ordinary floating-point arithmetic.

Month é derivado diretamente de uma date sem time-zone handling. Se dados reais contiverem timestamps de várias zones, defina a business time zone antes do grouping. Refunds, cancellations, filters e missing records também precisam de business rules explícitas; arithmetic reconciliation sozinha não consegue determinar se essas rows pertencem ao report.

Registro de execução e verificação

ChatGPT Python tool · Python 3.13.5 · pandas 2.2.3

  • A execução principal imprimiu exatamente: 'Source rows: 7.', 'Months: 3.', 'Verified totals: units=18, amount=430.' e 'Output: outputs/monthly_summary_result/monthly_summary.csv'.
  • O conteúdo do CSV gerado correspondeu exatamente ao artigo: 2026-01,2,5,100; 2026-02,3,7,175; 2026-03,2,6,155.
  • As contagens mensais de pedidos foram conciliadas com 7: 2 + 3 + 2.
  • Os units mensais foram conciliados com 18: 5 + 7 + 6.
  • Os amount mensais foram conciliados com 430: 100 + 175 + 155.
  • A saída continha exatamente 3 linhas de dados ordenadas por month.
  • O hash SHA-256 de monthly_orders.csv permaneceu inalterado após o processamento, confirmando que o arquivo de origem não foi modificado.
  • Uma segunda execução com outputs/monthly_summary_result já existente gerou FileExistsError antes de sobrescrever o resultado anterior.
Limites da verificação
  • Não foram realizados testes de estresse separados para rejeição de IDs duplicados, valores obrigatórios ausentes, datas inválidas, units ou amount não numéricos, dados monetários em ponto flutuante, fusos horários, reembolsos, cancelamentos, modelos de pedido por item de linha, falhas de permissão, falhas de gravação parcial nem conjuntos de dados muito grandes.
  • O ambiente Python emitiu no stderr um aviso não relacionado de aquecimento da ferramenta de planilhas artifact-tool, mas o script do artigo foi concluído com sucesso com exit code 0 e produziu a saída esperada.

Fontes de referência

As explicações e os exemplos são de elaboração própria. Os comportamentos e conceitos relacionados podem ser consultados nas fontes oficiais abaixo.