Pandas & NumPy Academy · Aula

Classificação e percentil dentro dos grupos

Calcule classificações dentro de cada grupo usando groupby().rank() e crie faixas de percentis com pd.qcut para comparações relativas.

Aula 4 de 413 etapas

Classificação e percentil dentro dos grupos é uma aula grátis de Pandas & NumPy Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Pandas & NumPy Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Pandas & NumPy Academy inclui 4 aulas no total.

Por que calcular a posição dentro dos grupos?

Os valores brutos costumam ser menos significativos do que as posições relativas. Um representante de vendas com receita mensal de US$ 50.000 é um profissional de alto desempenho se a média for US$ 30.000, mas terá baixo desempenho se a média for US$ 80.000. Ao calcular posições dentro dos grupos (por exemplo, a posição em cada região ou em cada trimestre), você obtém uma comparação normalizada que considera as diferentes referências entre os grupos. Pandas facilita o cálculo de posições dentro dos grupos com groupby().rank().

import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'rep': ['Alice', 'Bob', 'Carol', 'Dave', 'Eve',
            'Frank', 'Grace', 'Hank', 'Iris', 'Jake'],
    'region': ['East']*5 + ['West']*5,
    'sales': np.random.randint(30000, 100000, 10)
})
print(df.sort_values('region'))

Series.rank() — Cálculo básico de posições

Series.rank() atribui uma posição a cada valor: a posição 1 é a menor e a posição n é a maior. O parâmetro ascending=False inverte a direção, fazendo com que a posição 1 seja a maior. O resultado é uma Series de ponto flutuante (não inteira), pois, por padrão, os empates são resolvidos calculando-se a média das posições empatadas. Para uma coluna com 5 valores, as posições vão de 1.0 a 5.0 — ou, no caso de empates, alguns valores podem compartilhar uma posição, como 2.5.

import pandas as pd

s = pd.Series([80, 45, 90, 45, 70])

print('Values:', s.values)
print('Rank (ascending=True, default):', s.rank().values)
print('Rank (ascending=False — best=1):', s.rank(ascending=False).values)
# Note: two 45s share ranks 1 and 2 → both get 1.5

Métodos para desempatar em rank()

O parâmetro method controla o que acontece com os valores empatados. Opções: 'average' (padrão — os valores empatados compartilham a média de suas posições), 'min' (todos os valores empatados recebem a menor posição), 'max' (todos recebem a maior posição), 'first' (as posições seguem a ordem de aparição — sem empates) e 'dense' (sem lacunas nas posições — 1, 2, 3, 3, 4 se torna 1, 2, 3, 3, 4 em vez de 1, 2, 3, 3, 5). O método 'dense' é mais útil para classificações no estilo de percentil.

import pandas as pd

s = pd.Series([80, 45, 90, 45, 70])

result = pd.DataFrame({
    'value': s,
    'average': s.rank(method='average'),
    'min': s.rank(method='min'),
    'max': s.rank(method='max'),
    'first': s.rank(method='first'),
    'dense': s.rank(method='dense')
})
print(result)

Calculando posições dentro dos grupos com groupby().rank()

groupby('group_col')['value_col'].rank() calcula as posições independentemente dentro de cada grupo. A posição é reiniciada no início de cada grupo — portanto, o profissional de melhor desempenho na região Leste recebe a posição 1 no Leste, e o melhor profissional na região Oeste também recebe a posição 1 no Oeste. É isso que torna o cálculo de posições com groupby tão útil para comparações justas entre grupos.

import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'rep': list('ABCDEABCDE'),
    'region': ['East']*5 + ['West']*5,
    'sales': np.random.randint(30000, 100000, 10)
})

# Rank within each region (best = rank 1)
df['rank_in_region'] = df.groupby('region')['sales'].rank(ascending=False, method='min')
df_sorted = df.sort_values(['region', 'rank_in_region'])
print(df_sorted.to_string(index=False))

Classificação por percentil com rank(pct=True)

Definir pct=True em rank() retorna a posição do percentil: um valor entre 0 e 1 que representa a fração dos valores do grupo que é menor ou igual ao valor atual. Uma posição de percentil de 0.8 significa que o valor está no percentil 80 — é maior do que 80% de todos os valores. Essa normalização torna os valores de grupos de tamanhos diferentes diretamente comparáveis, sem que seja necessário conhecer o tamanho real dos grupos.

import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({
    'name': list('ABCDEFGHIJ'),
    'region': ['East']*5 + ['West']*5,
    'score': np.random.randint(50, 100, 10)
})

# Percentile rank within region
df['pct_rank'] = df.groupby('region')['score'].rank(pct=True)
df['percentile'] = (df['pct_rank'] * 100).round(0).astype(int)
print(df.sort_values(['region', 'percentile'], ascending=[True, False]).to_string(index=False))

pd.qcut: agrupamento baseado em quantis

pd.qcut(series, q) divide os valores em q intervalos com a mesma frequência, de modo que cada intervalo contenha aproximadamente o mesmo número de observações. Ao contrário de pd.cut, que usa intervalos de mesma largura, pd.qcut adapta os limites dos intervalos à distribuição dos dados. Isso é perfeito para criar quartis (q=4), quintis (q=5) ou decis (q=10) para níveis de desempenho.

import pandas as pd
import numpy as np

np.random.seed(42)
sales = pd.Series(np.random.exponential(scale=50000, size=100))

# Divide into quartiles
quartiles = pd.qcut(sales, q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])

result = pd.DataFrame({'sales': sales, 'quartile': quartiles})
print('Quartile counts (should be ~25 each):')
print(result['quartile'].value_counts().sort_index())
print('\nMean sales per quartile:')
print(result.groupby('quartile')['sales'].mean().round(0))

pd.cut vs pd.qcut

pd.cut(series, bins=n) cria intervalos de mesma largura (mesmo intervalo, quantidades diferentes). pd.qcut(series, q=n) cria intervalos de mesma frequência (mesma quantidade, intervalos diferentes). Em dados assimétricos, pd.cut resulta em intervalos quase vazios nas extremidades, enquanto pd.qcut distribui as observações uniformemente. Escolha pd.cut quando os limites dos intervalos tiverem um significado empresarial natural (faixas de preço, grupos de idade); escolha pd.qcut para níveis de desempenho nos quais você queira grupos do mesmo tamanho.

import pandas as pd
import numpy as np

np.random.seed(0)
# Right-skewed data
data = pd.Series(np.random.exponential(1, 100))

cut_result = pd.cut(data, bins=5).value_counts().sort_index()
qcut_result = pd.qcut(data, q=5).value_counts().sort_index()

print('Equal-width bins (pd.cut) — uneven counts:')
print(cut_result.to_string())
print('\nEqual-frequency bins (pd.qcut) — even counts:')
print(qcut_result.to_string())

Criando rótulos de decis com pd.qcut

pd.qcut(series, q=10, labels=range(1,11)) atribui cada observação ao seu decil (de 1 a 10). Essa é uma técnica padrão em análise de marketing (decis de valor do cliente), pontuação de crédito (decis de risco) e testes AB (decis de tráfego para análise de coortes). O parâmetro labels pode ser qualquer lista com o mesmo tamanho de q — use strings como ['Bottom 10%', ..., 'Top 10%'] para obter resultados mais fáceis de interpretar.

import pandas as pd
import numpy as np

np.random.seed(7)
customer_value = pd.Series(np.random.exponential(500, 1000))

# Assign to deciles 1-10
decile_labels = [f'D{i}' for i in range(1, 11)]
customer_decile = pd.qcut(
    customer_value,
    q=10,
    labels=decile_labels
)

result = pd.DataFrame({'value': customer_value, 'decile': customer_decile})
print('Mean customer value per decile:')
print(result.groupby('decile')['value'].mean().round(0))

Intervalos de percentil dentro dos grupos

Combine groupby com pd.qcut, usando transform, para criar intervalos de percentil dentro de cada grupo. Isso é útil quando você quer classificar clientes dentro de cada região (em vez de globalmente) — um cliente no decil global inferior pode estar no topo do decil de sua região. Use groupby('group')['value'].transform(lambda x: pd.qcut(x, q=4, labels=['Q1','Q2','Q3','Q4'])).

import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'customer': range(20),
    'region': ['North']*10 + ['South']*10,
    'spend': np.random.randint(100, 1000, 20)
})

# Quartile within each region
def quartile_label(x):
    return pd.qcut(x, q=4, labels=['Q1','Q2','Q3','Q4'])

df['region_quartile'] = df.groupby('region')['spend'].transform(quartile_label)
print(df.sort_values(['region', 'region_quartile']).to_string(index=False))

Os N melhores dentro dos grupos

Uma pergunta comum nos negócios é: "quem são os 3 profissionais de melhor desempenho em cada região?" Use groupby().rank() e, em seguida, filtre pela posição: df[df['rank_col'] <= 3]. Isso funciona bem com grupos de qualquer tamanho e trata os empates de forma adequada, mantendo mais de 3 linhas quando há posições empatadas no limite. Como alternativa, use groupby().nlargest(n), que retorna diretamente os n maiores valores por grupo sem adicionar uma coluna de posição.

import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({
    'rep': [f'Rep_{i}' for i in range(15)],
    'region': ['East']*5 + ['West']*5 + ['North']*5,
    'revenue': np.random.randint(40000, 120000, 15)
})

df['region_rank'] = df.groupby('region')['revenue'].rank(ascending=False, method='min')

print('Top 2 per region:')
top2 = df[df['region_rank'] <= 2].sort_values(['region', 'region_rank'])
print(top2[['rep', 'region', 'revenue', 'region_rank']].to_string(index=False))

Combinando rank e transform para normalização

Você pode usar groupby().rank(pct=True) combinado com transform para adicionar uma coluna de posição por percentil ao DataFrame original. Essa coluna normalizada costuma ser mais útil como característica de um modelo do que o valor bruto — ela não depende da escala e permite comparações entre grupos. Em aprendizado de máquina, as posições por percentil são uma alternativa simples à padronização (pontuação z) e são mais robustas a valores discrepantes.

import pandas as pd
import numpy as np

np.random.seed(1)
df = pd.DataFrame({
    'product': np.random.choice(['A', 'B', 'C'], 30),
    'score': np.random.randint(50, 100, 30)
})

# Percentile rank within each product group
df['global_pct'] = df['score'].rank(pct=True).round(3)
df['group_pct'] = df.groupby('product')['score'].rank(pct=True).round(3)

print(df.sort_values(['product', 'score']).head(12).to_string(index=False))

Verificação rápida

Teste sua compreensão das operações de posição e percentil apresentadas nesta lição.

Recapitulação da lição

Nesta lição, você aprendeu que Series.rank() calcula posições numéricas com regras configuráveis para desempate, groupby().rank() reinicia as posições dentro de cada grupo para permitir comparações justas entre grupos, pct=True converte posições em percentis (de 0 a 1) e pd.qcut cria intervalos de mesma frequência para atribuir níveis de quartil, decil e percentil. A seguir, exploraremos dicas de desempenho do Pandas — análise de desempenho, como evitar loops lentos e tipos de dados eficientes.

Grátis para começar

Aprenda Python com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
30
Aulas
120

Perguntas Frequentes

A aula “Classificação e percentil dentro dos grupos” é grátis?

Sim — o texto completo de “Classificação e percentil dentro dos grupos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Pandas & NumPy Academy, atualize para CoddyKit PRO. O curso de Pandas & NumPy Academy inclui 4 aulas no total.

O que vou aprender em “Classificação e percentil dentro dos grupos”?

Calcule classificações dentro de cada grupo usando groupby().rank() e crie faixas de percentis com pd.qcut para comparações relativas. Você pratica Pandas & NumPy Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Pandas & NumPy Academy?

Nenhuma experiência prévia é necessária. Pandas & NumPy Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Classificação e percentil dentro dos grupos”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Pandas & NumPy Academy?

Sim. Cada aula de Pandas & NumPy Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Janelas móveis
  2. Janelas expansivas
  3. Média móvel ponderada exponencialmente
  4. Classificação e percentil dentro dos grupos
← Voltar para Pandas & NumPy Academy