Pandas: Agrupamento por Agregação

Última atualização: 2026-08-26

Agregação com GroupBy é o coração da análise de dados — visualizar vendas por região, calcular preços médios por categoria, contar pedidos por mês — tudo se resume em "dividir, calcular, combinar." O groupby do Pandas implementa o clássico paradigma split-apply-combine. Nesta lição, diagramas Mermaid ajudarão você a compreender totalmente o processo, e você dominará as três operações-chave: agg / transform / filter.

⚠️ Nota: O código abaixo requer um ambiente Python local para ser executado.

1. O Que Você Vai Aprender


2. Alice Totalliza Vendas de Cafeteria por Categoria

(1) O Ponto de Dor: Loops Manuais São Tediosos

Alice deseja totalizar as vendas por categoria de bebida. Escrever loops manualmente:

PYTHON
import pandas as pd

df = pd.DataFrame({
    'drink': ['Latte', 'Americano', 'Mocha', 'Latte', 'Espresso',
              'Americano', 'Latte', 'Mocha', 'Espresso', 'Latte'],
    'category': ['Hot', 'Hot', 'Hot', 'Hot', 'Hot',
                 'Iced', 'Iced', 'Iced', 'Iced', 'Iced'],
    'sales': [320, 280, 350, 310, 150, 200, 180, 160, 90, 120],
    'quantity': [64, 56, 50, 62, 30, 40, 36, 22, 18, 24]
})

# Loop manual — tedioso e sujeito a erros
for cat in df['category'].unique():
    subset = df[df['category'] == cat]
    print(f"{cat}: sales={subset['sales'].sum()}, qty={subset['quantity'].sum()}")
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

(2) A Solução: Uma Única Linha com groupby

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: fundamentos do groupby (Dificuldade ⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'drink': ['Latte', 'Americano', 'Mocha', 'Latte', 'Espresso',
              'Americano', 'Latte', 'Mocha', 'Espresso', 'Latte'],
    'category': ['Hot', 'Hot', 'Hot', 'Hot', 'Hot',
                 'Iced', 'Iced', 'Iced', 'Iced', 'Iced'],
    'sales': [320, 280, 350, 310, 150, 200, 180, 160, 90, 120],
    'quantity': [64, 56, 50, 62, 30, 40, 36, 22, 18, 24]
})

# Uma linha substitui todo o loop!
result = df.groupby('category')['sales'].sum()
print(result)
# category
# Hot     1410
# Iced     750
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

3. O Princípio Dividir-Aplicar-Combinar

(1) Diagrama Mermaid do Processo de Agrupamento

100%
graph TB
    subgraph Split["① Dividir — Separar por categoria"]
        S1["Quente: Latte 320<br>Americano 280<br>Mocha 350<br>Latte 310<br>Espresso 150"]
        S2["Gelado: Americano 200<br>Latte 180<br>Mocha 160<br>Espresso 90<br>Latte 120"]
    end
    subgraph Apply["② Aplicar — Somar cada grupo"]
        A1["Quente → 320+280+350<br>+310+150 = 1410"]
        A2["Gelado → 200+180+160<br>+90+120 = 750"]
    end
    subgraph Combine["③ Combinar — Juntar resultados"]
        C1["categoria | vendas<br>Quente    | 1410<br>Gelado    | 750"]
    end
    Split --> Apply --> Combine
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

(2) O Objeto GroupBy é Lazy

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: Avaliação lazy com GroupBy (Dificuldade ⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'category': ['Hot', 'Hot', 'Iced', 'Iced'],
    'sales': [320, 280, 200, 180]
})

# groupby retorna um objeto GroupBy — ainda sem cálculo
grouped = df.groupby('category')
print(type(grouped))  # <class 'pandas.core.groupby.DataFrameGroupBy'>

# O cálculo acontece quando você chama uma agregação
print(grouped['sales'].sum())

# Inspecionar os grupos
print(grouped.groups)  # {'Hot': [0, 1], 'Iced': [2, 3]}
print(grouped.ngroups)  # 2

# Iterar sobre os grupos (raramente necessário)
for name, group in grouped:
    print(f"Grupo: {name}")
    print(group)
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

4. Funções de Agregação Comuns

(1) Agregações Integradas

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga alongado. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: Funções de agregação integradas (Dificuldade ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'category': ['Electronics', 'Electronics', 'Clothing', 'Clothing', 'Home', 'Home'],
    'product': ['Laptop', 'Phone', 'Shirt', 'Pants', 'Lamp', 'Chair'],
    'price': [999, 699, 45, 65, 30, 120],
    'stock': [50, 120, 200, 180, 300, 80],
    'rating': [4.7, 4.5, 4.2, 4.0, 3.8, 4.3]
})

# Agregação única por coluna
print(df.groupby('category')['price'].mean())
# Electronics    849.0
# Clothing        55.0
# Home            75.0

# Múltiplas colunas, mesma agregação
print(df.groupby('category')[['price', 'stock']].sum())

# Métodos de agregação comuns:
# .sum() .mean() .median() .min() .max()
# .count() .size() .std() .var() .first() .last()
# .nunique() .idxmax() .idxmin()
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga alongado. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

(2) size vs count

Método O Que Computa Tratamento de NaN
size Número de linhas por grupo (inclui NaN) Inclui NaN
count Número de valores não-NaN por grupo Exclui NaN

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga alongado. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: size vs count (Dificuldade ⭐)

PYTHON
import pandas as pd
import numpy as np

df = pd.DataFrame({
    'category': ['A', 'A', 'B', 'B', 'A'],
    'value': [1, np.nan, 3, 4, 5]
})

print(df.groupby('category').size())
# category
# A    3  ← 3 linhas (incluindo a linha com NaN)
# B    2

print(df.groupby('category').count())
#           value
# category
# A            2  ← 2 valores não-NaN
# B            2
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga alongado. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

5. Múltiplas Agregações com agg

(1) Múltiplas Funções de Agregação

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: Múltiplas agregações com agg (Dificuldade ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'category': ['Electronics', 'Electronics', 'Clothing', 'Clothing', 'Home', 'Home'],
    'product': ['Laptop', 'Phone', 'Shirt', 'Pants', 'Lamp', 'Chair'],
    'price': [999, 699, 45, 65, 30, 120],
    'stock': [50, 120, 200, 180, 300, 80],
    'rating': [4.7, 4.5, 4.2, 4.0, 3.8, 4.3]
})

# Multiple aggregations on one column
print(df.groupby('category')['price'].agg(['mean', 'min', 'max']))
#              mean  min   max
# category
# Clothing     55.0   45    65
# Electronics 849.0  699   999
# Home         75.0   30   120

# Different aggregations per column
print(df.groupby('category').agg({
    'price': ['mean', 'max'],
    'stock': 'sum',
    'rating': 'mean'
}))

# Named aggregations (cleaner column names)
result = df.groupby('category').agg(
    avg_price=('price', 'mean'),
    max_price=('price', 'max'),
    total_stock=('stock', 'sum'),
    avg_rating=('rating', 'mean')
)
print(result)
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

(2) Funções de Agregação Personalizadas

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: Agregação personalizada (Dificuldade ⭐⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'category': ['A', 'A', 'A', 'B', 'B', 'B'],
    'value': [10, 20, 30, 100, 200, 300]
})

# Custom function: range (max - min)
def value_range(series):
    return series.max() - series.min()

result = df.groupby('category')['value'].agg(['mean', value_range])
print(result)
#           mean  value_range
# category
# A         20.0           20
# B        200.0          200

# Lambda in agg (less readable but concise)
result2 = df.groupby('category')['value'].agg([
    ('mean', 'mean'),
    ('range', lambda x: x.max() - x.min()),
    ('cv', lambda x: x.std() / x.mean())  # coefficient of variation
])
print(result2)
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

6. Transformação Dentro do Grupo com transform

(1) transform Preserva a Forma Original

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: Padronização dentro do grupo com transform (Dificuldade ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'student': ['Alice', 'Alice', 'Bob', 'Bob', 'Charlie', 'Charlie'],
    'subject': ['Math', 'English', 'Math', 'English', 'Math', 'English'],
    'score': [85, 90, 92, 88, 78, 82]
})

# Z-score normalization per student
df['z_score'] = df.groupby('student')['score'].transform(
    lambda x: (x - x.mean()) / x.std()
)
print(df)

# Fill missing with group mean (instead of global mean)
df2 = pd.DataFrame({
    'category': ['A', 'A', 'A', 'B', 'B'],
    'value': [10, 20, None, 100, None]
})
df2['value_filled'] = df2.groupby('category')['value'].transform(
    lambda x: x.fillna(x.mean())
)
print(df2)

# Rank within group
df['rank'] = df.groupby('student')['score'].transform('rank', method='min')
print(df[['student', 'subject', 'score', 'rank']])
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

(2) Comparação entre transform e agg

Característica agg transform
Forma de retorno Uma linha por grupo Mesmo comprimento do DataFrame original
Caso de uso Relatórios de resumo Preencher a tabela original
Operações típicas soma/média/contagem Padronização/preenchimento/classificação
Transmissão (broadcasting) ✅ Transmite automaticamente para as linhas originais

7. Filtragem em Nível de Grupo com filter

(1) Manter ou Descartar Grupos por Condição

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: Filtrando grupos com filter (Dificuldade ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'category': ['Electronics', 'Electronics', 'Electronics',
                 'Books', 'Books',
                 'Clothing', 'Clothing', 'Clothing', 'Clothing'],
    'product': ['Laptop', 'Phone', 'Tablet', 'Novel', 'Textbook',
                'Shirt', 'Pants', 'Jacket', 'Socks'],
    'sales': [5000, 3000, 2000, 200, 300, 800, 600, 400, 100]
})

# Manter apenas grupos onde as vendas totais > 1000
big_categories = df.groupby('category').filter(lambda x: x['sales'].sum() > 1000)
print(big_categories)
# Electronics e Clothing mantidos; Books descartado (200+300=500 < 1000)

# Manter grupos com pelo menos 3 itens
large_groups = df.groupby('category').filter(lambda x: len(x) >= 3)
print(large_groups['category'].unique())  # ['Electronics', 'Clothing']

# Manter grupos onde qualquer produto tem vendas > 4000
has_top_seller = df.groupby('category').filter(lambda x: x['sales'].max() > 4000)
print(has_top_seller['category'].unique())  # ['Electronics']
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

8. Agrupamento Multi-Coluna e as_index

(1) Agrupando por Múltiplas Colunas

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e siga along. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.

: Agregação de groupby multi-coluna (Dificuldade ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'region': ['North', 'North', 'North', 'South', 'South', 'South'],
    'category': ['Electronics', 'Clothing', 'Electronics',
                 'Electronics', 'Clothing', 'Clothing'],
    'product': ['Laptop', 'Shirt', 'Phone', 'Tablet', 'Pants', 'Jacket'],
    'sales': [5000, 800, 3000, 2000, 600, 400]
})

# Group by multiple columns
result = df.groupby(['region', 'category'])['sales'].sum()
print(result)
# region  category
# North  Clothing       800
#        Electronics   8000
# South  Clothing      1000
#        Electronics   2000

# as_index=False → mantém as colunas de agrupamento como colunas comuns
result2 = df.groupby(['region', 'category'], as_index=False)['sales'].sum()
print(result2)
#   region    category  sales
# 0  North    Clothing    800
# 1  North  Electronics  8000
# 2  South    Clothing   1000
# 3  South  Electronics  2000

# sort=False → preserva a ordem original da primeira ocorrência
result3 = df.groupby(['region', 'category'], sort=False)['sales'].sum()
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e siga along. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.

9. Exemplo Completo: Análise Multi-Dimensional de Agrupamento para E-Commerce

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: Análise multi-dimensional de agrupamento (groupby) (Dificuldade ⭐⭐⭐)

PYTHON
import pandas as pd
import numpy as np

# ============================================
# Exemplo abrangente: análise de agrupamento
# multi-dim para e-commerce com agg/transform/filter
# ============================================

# 1. Criar dados de vendas
np.random.seed(42)
df = pd.DataFrame({
    'region': np.random.choice(['Norte', 'Sul', 'Leste', 'Oeste'], 100),
    'category': np.random.choice(['Eletrônicos', 'Vestuário', 'Casa', 'Esportes'], 100),
    'product': [f'Item_{i:03d}' for i in range(100)],
    'sales': np.round(np.random.uniform(50, 2000, 100), 2),
    'quantity': np.random.randint(1, 50, 100),
    'discount': np.random.choice([0, 0.1, 0.2, 0.3], 100)
})

# 2. agg: resumo de múltiplas métricas por categoria
summary = df.groupby('category').agg(
    total_sales=('sales', 'sum'),
    avg_sales=('sales', 'mean'),
    max_sales=('sales', 'max'),
    order_count=('sales', 'count'),
    avg_quantity=('quantity', 'mean')
).round(2)
print("=== Resumo por Categoria ===")
print(summary)

# 3. Agrupamento por múltiplas colunas
region_cat = df.groupby(['region', 'category'], as_index=False).agg(
    total_sales=('sales', 'sum'),
    avg_discount=('discount', 'mean')
).round(3)
print("\n=== Região × Categoria ===")
print(region_cat.head(8))

# 4. transform: ranking dentro do grupo
df['sales_rank_in_region'] = df.groupby('region')['sales'].transform(
    'rank', method='min', ascending=False
)
df['sales_pct_in_category'] = df.groupby('category')['sales'].transform(
    lambda x: (x / x.sum() * 100).round(1)
)
print("\n=== Top 3 no Norte ===")
print(df[df['region'] == 'Norte'].nsmallest(3, 'sales_rank_in_region')
      [['product', 'sales', 'sales_rank_in_region']])

# 5. filter: manter categorias com > 20 pedidos
big_cats = df.groupby('category').filter(lambda x: len(x) > 20)
print(f"\n=== Categorias Grandes ===")
print(big_cats['category'].value_counts())
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

❓ Perguntas Frequentes

P O que o groupby retorna?
R O groupby retorna um objeto GroupBy (lazy) — nenhuma computação acontece imediatamente. O processo de dividir-aplicar-combinar só é executado quando você chama uma função de agregação (soma/média/agg, etc.). Esse design lazy permite encadear múltiplas operações sem variáveis intermediárias.
P Qual é a diferença entre agg e apply?
R A agg aplica funções de agregação a cada coluna e retorna uma linha por grupo. A apply aplica uma função arbitrária a cada grupo e retorna resultados mais flexíveis (pode retornar um DataFrame). Use agg para agregações simples (mais rápida e explícita); use apply para cálculos complexos dentro do grupo. A agg também suporta agregação nomeada (nome=(col, func)) para nomes de colunas mais limpos.
P O transform preserva a forma?
R Sim — o transform retorna um resultado com o mesmo comprimento do DataFrame original, transmitindo o resultado de cada grupo de volta às linhas originais. Casos de uso típicos: padronização dentro do grupo (z-score), preenchimento de valores ausentes com a média do grupo e classificação dentro do grupo. Um erro é gerado se o comprimento do resultado não corresponder ao tamanho do grupo.
P O filter remove linhas ou grupos?
R O filter remove grupos inteiros — se o resultado agregado de um grupo não atender à condição, todas as linhas desse grupo são descartadas. Ele não filtra linhas individuais dentro de um grupo! Isso difere da indexação booleana: a indexação booleana filtra por condições em nível de linha, enquanto o filter opera em condições em nível de grupo.
P O que o as_index=False faz?
R Por padrão, o groupby transforma as colunas de agrupamento no Index (não colunas regulares). O as_index=False mantém as colunas de agrupamento como colunas regulares, produzindo um DataFrame limpo em vez de uma Series com MultiIndex. Quando você precisa operar nas colunas de agrupamento depois (por exemplo, em um mesclagem), o as_index=False é mais conveniente.
P Como eu leio resultados de groupby de múltiplas colunas?
R O agrupamento de múltiplas colunas produz um MultiIndex (índice hierárquico). Use result.loc[('North', 'Electronics')] para selecionar um grupo específico, ou use as_index=False para evitar o índice hierárquico completamente. O reset_index() também pode achatar um índice hierárquico em colunas regulares.
P O groupby + sort deixa as coisas mais lentas?
R sort=True (o padrão) ordena as chaves de agrupamento, adicionando uma pequena sobrecarga. Para DataFrames grandes com chaves de agrupamento de alta cardinalidade, sort=False pode acelerar as coisas. No entanto, resultados ordenados são mais legíveis. Recomendação: use sort=True para conjuntos de dados pequenos (legibilidade primeiro) e sort=False para conjuntos de dados muito grandes (desempenho primeiro).

📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Crie um DataFrame de vendas (região/categoria/vendas). Use groupby + soma/média/contagem para três agregações diferentes e compare os resultados.
  2. Intermediário (Dificuldade ⭐⭐): Crie uma tabela de notas de alunos. Use agg para calcular média/máxima/mínima para cada disciplina, e use transform para calcular as notas normalizadas por z-score para cada aluno.
  3. Desafio (Dificuldade ⭐⭐⭐): Simule 50 linhas de pedidos de e-commerce (região/categoria/vendas/quantidade/desconto). Complete o seguinte pipeline: agg resumo multi-métrica → groupby multi-coluna (região+categoria) → transform ranking intra-grupo → filtrar para manter grandes grupos → produzir um relatório abrangente.

← Previous: Data Transformation · Next: Merge and Join →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%