← voltar ao comparativo
Fable 5.1 · Anthropic

Relatório de dados: planilha crua vira números conferíveis

T16 — Extrato de vendas 2026: relatório

Agente: claude · Fonte: alvos\dados\vendas-2026.csv (sha256 774cb912b05ddfb8a06fc9b03e7e2e81eaf3b5cf2f1e633a7b802e84ea54aba8, não alterado) Script que gerou todos os números: [analise.py](analise.py) → [resultados.json](resultados.json) · Gráfico: [faturamento-por-canal.png](faturamento-por-canal.png)

Base depois do tratamento: 1.545 linhas de dados no arquivo → 1.500 após deduplicar → 1.439 válidas → 1.058 vendas pagas.

Nenhum número deste relatório foi digitado à mão: todos saem do resultados.json, que o analise.py grava ao rodar.


As 8 respostas

1. Faturamento total pago em 2026

> ## R$ 1.147.167,16

Soma dos valores das 1.058 linhas válidas com status = pago.

2. Ticket médio pago

> ## R$ 1.084,28

R$ 1.147.167,16 ÷ 1.058 vendas pagas. A divisão é feita em Decimal; o arredondamento para 2 casas acontece só na exibição.

3. Canal que mais faturou

> ## Instagram — R$ 387.361,82

Ranking completo (é o que está no gráfico):

CanalFaturamento pago
InstagramR$ 387.361,82
IndicaçãoR$ 265.654,67
GoogleR$ 218.871,12
EventoR$ 147.772,70
OutboundR$ 127.506,85

O Instagram só aparece em 1º porque as 4 grafias do arquivo (Instagram , INSTAGRAM, instagram, Instagram) foram somadas como um canal só — separadas, nenhuma delas lideraria.

4. Top 3 vendedores por faturamento pago

> ## 1º Beatriz Alcantara — R$ 267.420,33 > ## 2º Caio Meireles — R$ 220.411,48 > ## 3º Danilo Rocha — R$ 207.298,40

Os outros três, para contexto: Fernanda Quirino R$ 186.932,73 · Otavio Bastos R$ 160.293,78 · Silvia Nakamura R$ 104.810,44.

5. Mês de maior faturamento pago

> ## Maio/2026 — R$ 188.486,15

Série mensal completa (só pagos): jan 85.283,10 · fev 89.588,77 · mar 123.830,87 · abr 140.795,60 · mai 188.486,15 · jun 132.610,08 · jul 131.126,15 · ago 137.212,14 · set 118.234,30. O arquivo cobre janeiro a setembro de 2026.

6. Taxa de cancelamento

> ## 8,83%

127 linhas válidas com status = cancelado ÷ 1.439 linhas válidas (todos os status), × 100 = 8,8255…% → 8,83%. O denominador é o das válidas *depois* da deduplicação e dos descartes da regra 2, como pede o enunciado — não as 1.545 linhas brutas (que dariam 8,22% e estariam erradas).

7. Linhas descartadas, por motivo

> ## 106 linhas descartadas no total

MotivoLinhas
Duplicata exata45
valor vazio30
Data impossível15
UF inválida15
Valor negativo1
Total106

1.545 − 106 = 1.439 linhas válidas. Detalhe de cada motivo na seção de tratamento, abaixo.

8. UF com maior ticket médio (mínimo 10 vendas pagas)

> ## SE (Sergipe) — R$ 1.583,62

Com 10 vendas pagas — exatamente no limite, e por isso entra. 18 UFs passaram no corte de 10 vendas pagas; 9 ficaram de fora.

Top 5 das elegíveis: SE R$ 1.583,62 (10) · AM R$ 1.396,59 (15) · GO R$ 1.381,69 (40) · PA R$ 1.321,96 (14) · MS R$ 1.277,86 (17).

O corte de 10 é o que decide a resposta: RR tem ticket médio de R$ 1.686,72 e ficaria em 1º, mas isso é uma venda só — sem o filtro, a resposta seria RR e estaria errada.


Como tratei a sujeira do arquivo

As regras foram aplicadas exatamente na ordem do PERGUNTAS.md: deduplicar → descartar (com precedência) → filtrar por status → normalizar → agrupar.

O que descartei, e por quê

#MotivoQtdO que apareceu no arquivo
1Duplicata exata4545 pares de linhas idênticas em todos os 9 campos, byte a byte. Mantive a 1ª ocorrência e descartei a cópia. Comparação feita nos campos crus, antes de qualquer normalização — como manda a regra 1 ("exatamente como aparecem no arquivo").
2valor vazio30Campo vindo em branco do sistema de origem. Sem valor não há venda a somar.
3Data impossível152026-02-30 (3×), 2026-02-31 (3×), 2026-06-31 (1×), 2026-09-31 (2×), 2026-11-31 (2×) e 2026-13-05 (4×, mês 13). Datas que não existem no calendário — testadas construindo um date real, não por regex.
4UF inválida15-- (3×), BR (6×), XX (2×), ZZ (4×). Nenhuma é uma das 27 siglas.
5Valor negativo1Uma linha com -949.45 (provável estorno lançado como venda).

Precedência: uma linha que se encaixa em mais de um caso é contada só pelo primeiro motivo na ordem valor vazio → data impossível → UF inválida → valor negativo, e a duplicata vem antes de todos porque a regra 1 roda primeiro. Sem essa ordem os 5 números mudariam mesmo com o total igual.

O que normalizei (e o que teria dado errado sem isso)

CampoAntesDepoisImpacto
canal21 grafias diferentes (caixa, acento e espaço sobrando: INSTAGRAM, Instagram, indicação, Indicacao, INDICACAO, google , OutBound…)5 canais canônicosSem isso a pergunta 3 teria 21 "canais" e nenhum líder real. Acento removido via unicodedata NFD, não por tabela de substituição.
uf76 formas cruas ( SP , sp, Sp, Rj…)27 siglas válidas + 4 inválidasDuplo papel: define o que é UF válida (pergunta 7) e o agrupamento da pergunta 8.
vendedor24 formas cruas6 vendedoresO mais perigoso do arquivo: os 6 vendedores aparecem também com espaço interno duplo (Beatriz Alcantara, Caio Meireles, Danilo Rocha, Fernanda Quirino, Otavio Bastos, Silvia Nakamura). Só tirar espaço das pontas não resolve — teria partido cada vendedor em dois e quebrado o top 3 da pergunta 4. Colapsei espaço interno também.
cliente623 formas cruas483 clientesMesmo tratamento (não muda nenhuma resposta, mas mantém o dado coerente).
statusminúsculas, sem espaçoGarante que o filtro pago / cancelado não perca linha.

Decisão de interpretação que registro explicitamente

Normalizei a UF *antes* de julgar se ela é válida. A regra 2 diz para descartar uf fora das 27 siglas e a regra 4 diz que uf vem com variação de caixa e espaço. Se a validade fosse checada no texto cru, SP , sp e Sp seriam "inválidas" e ~130 vendas legítimas iriam para o lixo — e a menção a uf na regra 4 não teria função nenhuma. Normalizar primeiro deixa como inválidas só --, BR, XX e ZZ, que são inválidas de verdade em qualquer leitura.

Formatos convertidos


Verificação

Não me contentei em rodar o script uma vez e confiar no que saiu:

  1. Duas implementações independentes, mesmo resultado. Além do analise.py (csv puro + Decimal + laço), escrevi o [conferencia.py](conferencia.py), que refaz as 8 respostas por outro caminho — pandas, float, agrupamento vetorizado — e compara item a item com o resultados.json. Saída: CONFERENCIA OK - os dois caminhos independentes batem em todos os 19 itens. Se divergir, ele sai com código 1.
  2. Fechamento aritmético: 1.545 − 106 descartes = 1.439 válidas; 1.058 pagas + 254 pendentes + 127 canceladas = 1.439. Os dois fecham.
  3. PNG conferido no arquivo, não na intenção: cabeçalho do PNG lido em binário → 1200×800 px (mínimo exigido 400×300), 38.360 bytes. Abri a imagem e confirmei que as 5 barras e os rótulos saíram legíveis.
  4. Arquivo original intacto: git status do alvos/ volta vazio; sha256 registrado no topo deste relatório.
  5. Reexecutável do zero: python analise.py roda da pasta da entrega sem editar nada — o CSV é apontado por caminho absoluto e as saídas são gravadas ao lado do script (Path(__file__).parent), não no cwd.
  6. O script recusa ambiguidade em vez de chutar: canal fora dos 5 canônicos, formato de data desconhecido ou formato de valor desconhecido levantam ValueError. Rodou sem levantar nenhum — ou seja, os 1.545 registros couberam nas regras declaradas, sem caso silenciosamente ignorado.

Ferramentas usadas

FerramentaPara quê
Claude Code (Fable 5.1, VS Code, bypass permissions)Sessão inteira
Bash / Git BashLevantamento da sujeira antes de escrever o script (contagem de grafias, formatos e casos-limite), execução e conferência
Write / EditEscrita do analise.py, conferencia.py e deste relatório — o heredoc do bash quebrou com o conteúdo do script e troquei de ferramenta em vez de insistir
Read (visão de imagem)Abri o PNG gerado para conferir que o gráfico saiu legível, em vez de só confiar que o savefig funcionou
Python 3.12.10csv, decimal, datetime, unicodedata, re, json, pathlib (biblioteca padrão)
pandas 3.0.2Só na conferência independente (já estava instalado)
matplotlib 3.11.1Gráfico. Instalei nesta sessão (python -m pip install matplotlib) — não existia na máquina; veio junto contourpy, cycler, fonttools e kiwisolver
gitConferência de que o alvo não foi tocado

Não usei MCP, subagente nem navegador neste teste: a tarefa é determinística e local, e qualquer uma dessas rotas só adicionaria intermediário entre o CSV e o número.

Arquivos entregues

ArquivoO que é
RELATORIO.mdEste documento — as 8 respostas e o tratamento
analise.pyScript que produz todos os números e o gráfico. python analise.py roda do zero
faturamento-por-canal.pngGráfico gerado por código dentro do analise.py (1200×800 px)
resultados.jsonSaída estruturada do script — a origem rastreável de cada número acima
conferencia.pyConferência independente (extra, não pedido): refaz tudo em pandas e compara