# Agrupar e agregar

Separar por chaves, calcular agregações, pivot_table e distinguir agg de transform.

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

Queremos o número total de unidades por cidade. Cada venda tem uma quantidade, mas o relatório precisa de uma linha por cidade. Agrupar identifica as vendas da mesma cidade; agregar reduz essas vendas a um resultado.

## Separar, calcular e reunir

```python
import pandas as pd
vendas = pd.DataFrame({
    "cidade": ["Porto", "Porto", "Braga", "Braga"],
    "produto": ["pão", "leite", "pão", "arroz"],
    "quantidade": [2, 3, 5, 4],
    "preco": [1.5, 1.2, 1.5, 2.0],
})
vendas["total"] = vendas["quantidade"] * vendas["preco"]
resumo = vendas.groupby("cidade", as_index=False).agg(
    unidades=("quantidade", "sum"),
    faturacao=("total", "sum"),
    vendas=("produto", "size"),
)
print(resumo.round(2).to_string(index=False))
```

Porto vende 5 unidades em duas vendas e fatura 6,60 euros. Braga vende 9 unidades em duas vendas e fatura 15,50 euros. A soma da faturação dos grupos é 22,10 euros, igual à da tabela original.

![As quatro vendas separam-se por cidade; a agregação soma dois valores no Porto e dois em Braga, criando dois resultados.](https://resumos.rgo.pt/cadeiras/ct-iadp/agrupamentos/figura-1.svg)

`as_index=False` conserva cidade como uma coluna. Sem esse argumento, cidade torna-se o índice do resultado. A agregação com nomes diz de que coluna vem cada cálculo e como se chama no relatório.

## size, count e múltiplas chaves

`size` conta linhas do grupo. `count` conta valores não nulos de uma coluna. Se uma das vendas não tem produto conhecido, `size` continua a contar a venda e `count` nessa coluna não a conta.

`groupby(["cidade", "produto"])` cria um grupo por par. Cada chave adicional refina os grupos. Por defeito, linhas com chaves em falta podem ser excluídas; usa `dropna=False` quando devem formar um grupo explícito.

`agg(["sum", "mean"])` calcula várias estatísticas. Não agregues a coluna de preço com soma se a pergunta for faturação. Primeiro calcula quantidade vezes preço por venda e só depois soma esses totais.

## transform conserva as linhas

`agg` produz um resultado por grupo. `transform` devolve um valor associado a cada linha original. Para calcular a proporção de unidades de uma venda dentro da cidade:

```
total_cidade = vendas.groupby("cidade")["quantidade"].transform("sum")
vendas["proporcao"] = vendas["quantidade"] / total_cidade
```

No Porto, as proporções são `2/5` e `3/5`. Em Braga, `5/9` e `4/9`. A soma é um em cada cidade, se o total do grupo for não nulo e todas as vendas forem consideradas.

## Tabela dinâmica com pivot\_table

```
tabela = vendas.pivot_table(
    index="cidade",
    columns="produto",
    values="quantidade",
    aggfunc="sum",
    fill_value=0,
)
```

Cada célula soma as quantidades de uma combinação cidade-produto. `pivot_table` admite combinações repetidas porque a agregação decide como as reunir. A função por defeito é média, por isso escreve `aggfunc="sum"` quando o enunciado pede totais.

`fill_value=0` transforma uma combinação sem vendas em zero depois da agregação. Isto exige interpretar a ausência como nenhuma venda, não como uma quantidade desconhecida. `margins=True` acrescenta totais, mas só deves somá-los outra vez se quiseres contar os dados duas vezes.

## Rever a agregação

## Exercícios

Contar observações incompletas

Um grupo tem três linhas, mas uma célula da coluna produto está em falta. Quanto vale count() nessa coluna?

Primeira pista

count conta valores conhecidos da coluna escolhida.

Mais uma pista

Há três linhas e dois produtos conhecidos.

Ver solução

count vale 2. size valeria 3, porque conta linhas independentemente dessa ausência.

#### Erros frequentes

Dizer que as duas funções são sempre equivalentes.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/ct-iadp/agrupamentos/#size-count-e-m%C3%BAltiplas-chaves)

Conservar a forma inicial

Queres acrescentar a cada venda a quantidade total da sua cidade. Qual é a escolha?

Primeira pista

O resultado deve ter uma posição por venda.

Mais uma pista

transform conserva as linhas; agg reduz aos grupos.

Ver solução

Usa transform e atribui o resultado a uma coluna nova. Depois confere que as vendas da mesma cidade recebem o mesmo total.

*   **groupby("cidade")\["quantidade"\].transform("sum").** Devolve um valor para cada linha original, alinhado pelo índice.
*   **groupby("cidade")\["quantidade"\].sum() atribuído diretamente.** Tem uma linha por cidade; os rótulos podem não alinhar com o índice das vendas.
*   **quantidade.sum().** Calcula o total global e perde a distinção de cidades.

#### Erros frequentes

Usar uma agregação reduzida como se tivesse o índice da tabela inicial.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/ct-iadp/agrupamentos/#transform-conserva-as-linhas)
