# Uma análise completa de vendas

Importar, validar, relacionar e agregar dados, conservando decisões e verificações.

Página: https://resumos.rgo.pt/cadeiras/ct-iadp/analise-completa/

Vamos responder a duas perguntas: quanto faturou cada cidade e qual foi o preço médio por unidade vendida? Temos um CSV com vendas e outra tabela com preços por produto. Os dados abaixo são fictícios e pequenos para poderes verificar cada passo.

## Definir as observações

Cada linha de vendas identifica uma venda, com código único, cidade, produto e quantidade. O catálogo tem um preço por produto. Quantidade deve ser positiva e o preço deve ser conhecido antes de calcular a faturação.

| Venda | Cidade | Produto | Quantidade |
| --- | --- | --- | --- |
| 1 | Porto | A | 2 |
| 2 | Porto | B | 3 |
| 3 | Braga | A | 5 |
| 4 | Braga | C | 4 |

A custa 1,50 euros, B custa 1,20 e C custa 2,00. À mão, Porto fatura 6,60 e Braga 15,50. O total é 22,10 euros e há 14 unidades.

## Código do princípio ao fim

```python
from io import StringIO
import pandas as pd

csv_vendas = 'venda;cidade;produto;quantidade\n1;Porto;A;2\n2;Porto;B;3\n3;Braga;A;5\n4;Braga;C;4\n'
vendas = pd.read_csv(StringIO(csv_vendas), sep=";", dtype={"produto": "string"})
precos = pd.DataFrame({"produto": ["A", "B", "C"], "preco": [1.5, 1.2, 2.0]})

assert vendas["venda"].is_unique
assert vendas[["cidade", "produto", "quantidade"]].notna().all().all()
assert (vendas["quantidade"] > 0).all()
assert precos["produto"].is_unique

dados = vendas.merge(precos, on="produto", how="left", validate="many_to_one")
assert len(dados) == len(vendas)
assert dados["preco"].notna().all()
dados["total"] = dados["quantidade"] * dados["preco"]

relatorio = dados.groupby("cidade", as_index=False).agg(
    unidades=("quantidade", "sum"),
    faturacao=("total", "sum"),
    vendas=("venda", "size"),
)
relatorio["preco_medio"] = relatorio["faturacao"] / relatorio["unidades"]
assert abs(relatorio["faturacao"].sum() - dados["total"].sum()) < 1e-10
print(relatorio.round(4).to_string(index=False))
print("Faturação total:", round(dados["total"].sum(), 2))
print("Unidades:", dados["quantidade"].sum())
print("Preço médio global:", round(dados["total"].sum() / dados["quantidade"].sum(), 4))
```

O preço médio por unidade é 1,32 euros no Porto e aproximadamente 1,7222 em Braga. O global é `22,10 / 14`, aproximadamente 1,5786 euros. Não calculamos a média das médias das cidades, porque venderam quantidades diferentes.

## Por que cada verificação existe

A chave de venda única impede contar o mesmo identificador duas vezes. As colunas essenciais não podem estar ausentes. A quantidade positiva faz parte do contrato deste exemplo; noutro conjunto, quantidades negativas podem representar devoluções e precisam de outra regra.

A chave de produto única evita multiplicar linhas na junção. `how="left"` conserva as vendas, permitindo detetar produtos sem preço. Só calculamos totais depois de confirmar que todos têm correspondência.

A contagem de linhas deve manter-se na junção muitos-para-um. A soma da faturação dos grupos deve coincidir com a soma antes de agrupar. Estas verificações não provam que os preços reais estão corretos, mas detetam perdas e duplicações no processamento.

## Acrescentar uma situação incompleta

Substitui C por D numa das vendas e volta a executar. A verificação dos preços falha antes de produzir o relatório. Para continuar, é preciso consultar o catálogo ou definir uma política para vendas sem preço. Não substituas o preço em falta por zero só para obter uma tabela final.

Duplica a linha de A no catálogo e executa novamente. `validate="many_to_one"` rejeita a junção. Sem essa verificação, as vendas de A seriam duplicadas e a soma ficaria errada.

## Preparar uma resolução de avaliação

Lê o pedido e identifica a unidade de análise. Escreve a chave que identifica cada linha, as colunas de que precisas e o resultado esperado. Depois escolhe seleção, transformação, junção ou agregação conforme a pergunta.

Antes de executar, prevê pelo menos uma célula e o número de linhas do resultado. Depois de executar, confere esse caso e um total que deva conservar-se. Na resposta, inclui o código, o valor obtido e a sua interpretação com unidade. Uma tabela correta sem explicar o que cada linha representa pode não responder à pergunta.

## Exercícios

Calcular o preço médio global

Porto faturou 6,60 euros em 5 unidades e Braga 15,50 em 9. Qual é o preço médio global por unidade? Arredonda a quatro casas decimais.

Primeira pista

A média global usa as somas da faturação e das unidades.

Mais uma pista

Divide 22,10 por 14.

Ver solução

O resultado é aproximadamente 1,5786 euros por unidade. Uma média simples das duas médias daria igual peso a cidades com quantidades diferentes.

#### Erros frequentes

Somar médias ou calcular a sua média simples sem ponderar as unidades.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/ct-iadp/analise-completa/#c%C3%B3digo-do-princ%C3%ADpio-ao-fim)

Detetar um catálogo duplicado

O catálogo passa a ter duas linhas para o mesmo produto. Explica o efeito numa junção e como a análise o deteta antes do relatório.

Primeira pista

Uma junção cria um par para cada correspondência da chave.

Mais uma pista

O catálogo deve ter uma única linha por produto.

Ver solução

A junção duplicaria as vendas desse produto e poderia aumentar a faturação. A unicidade da chave e validate=“many\_to\_one” impedem continuar com esse catálogo. Não elimines arbitrariamente um preço sem resolver a ambiguidade.

Confere a tua resposta:

*   Expliquei que cada venda desse produto encontra duas correspondências.
*   Identifiquei validate="many\_to\_one" como verificação da relação.
*   Indiquei que conferir linhas e totais ajuda a detetar multiplicação.

#### Erros frequentes

Aceitar o relatório porque o código executou ou escolher o primeiro preço sem uma regra.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/ct-iadp/analise-completa/#acrescentar-uma-situa%C3%A7%C3%A3o-incompleta)
