0Pricing
Pandas & NumPy Academy · Leçon

Rangs et percentiles au sein des groupes

Calculez les rangs au sein des groupes avec groupby().rank() et créez des classes de percentiles avec pd.qcut pour effectuer des comparaisons relatives.

Rangs et percentiles au sein des groupes est une leçon Pandas & NumPy Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Pandas & NumPy Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Pandas & NumPy Academy comprend 4 leçons au total.

Pourquoi classer les valeurs au sein des groupes ?

Les valeurs brutes sont souvent moins parlantes que les classements relatifs. Un commercial qui génère 50 000 $ de chiffre d’affaires mensuel est très performant si la moyenne est de 30 000 $, mais peu performant si la moyenne est de 80 000 $. En calculant les rangs au sein des groupes (par exemple, le rang dans chaque région ou dans chaque trimestre), vous obtenez une comparaison normalisée qui tient compte des niveaux de référence différents entre les groupes. Pandas facilite le classement au sein des groupes avec 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() — Classement de base

Series.rank() attribue un rang à chaque valeur : le rang 1 correspond à la plus petite valeur et le rang n à la plus grande. Le paramètre ascending=False inverse la direction : le rang 1 correspond alors à la plus grande valeur. Le résultat est une Series flottante, et non entière, car les égalités sont par défaut résolues en faisant la moyenne des rangs concernés. Pour une colonne contenant 5 valeurs, les rangs vont de 1.0 à 5.0 ; en cas d’égalité, certaines valeurs peuvent partager un rang comme 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éthodes de résolution des égalités dans rank()

Le paramètre method contrôle le traitement des valeurs égales. Options : 'average' (par défaut — les valeurs égales partagent la moyenne de leurs rangs), 'min' (toutes les valeurs égales reçoivent le rang le plus bas), 'max' (elles reçoivent toutes le rang le plus élevé), 'first' (les rangs suivent l’ordre d’apparition — aucune égalité), et 'dense' (les rangs ne comportent aucun saut — 1, 2, 3, 3, 4 devient 1, 2, 3, 3, 4 au lieu de 1, 2, 3, 3, 5). La méthode 'dense' est particulièrement utile pour les classements de type percentile.

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)

Classer les valeurs au sein des groupes avec groupby().rank()

groupby('group_col')['value_col'].rank() calcule les rangs indépendamment au sein de chaque groupe. Le classement recommence au début de chaque groupe : le meilleur élément de la région Est reçoit donc le rang 1 dans l’Est, et le meilleur élément de la région Ouest reçoit également le rang 1 dans l’Ouest. C’est ce qui rend le classement avec groupby si utile pour comparer équitablement plusieurs groupes.

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))

Classement en percentile avec rank(pct=True)

Définir pct=True dans rank() renvoie le rang en percentile : une valeur comprise entre 0 et 1 qui représente la fraction des valeurs du groupe inférieures ou égales à la valeur actuelle. Un rang en percentile de 0.8 signifie que la valeur se situe au 80e percentile, c’est-à-dire qu’elle est supérieure à 80 % de toutes les valeurs. Cette normalisation permet de comparer directement des valeurs provenant de groupes de tailles différentes, sans connaître la taille réelle des groupes.

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 : regroupement fondé sur les quantiles

pd.qcut(series, q) répartit les valeurs dans q intervalles contenant chacun approximativement le même nombre d’observations. Contrairement à pd.cut, qui utilise des intervalles de même largeur, pd.qcut adapte les limites des intervalles à la distribution des données. C’est idéal pour créer des quartiles (q=4), des quintiles (q=5) ou des déciles (q=10) afin de définir des niveaux de performance.

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 ou pd.qcut

pd.cut(series, bins=n) crée des intervalles de même largeur (même étendue, effectifs différents). pd.qcut(series, q=n) crée des intervalles de même fréquence (même effectif, étendues différentes). Pour des données asymétriques, pd.cut produit des intervalles presque vides aux extrémités, tandis que pd.qcut répartit les observations uniformément. Choisissez pd.cut lorsque les limites des intervalles ont une signification naturelle pour l’activité (tranches de prix, groupes d’âge), et pd.qcut pour les niveaux de performance lorsque vous souhaitez des groupes de même taille.

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())

Créer des étiquettes de décile avec pd.qcut

pd.qcut(series, q=10, labels=range(1,11)) affecte chaque observation à son décile (de 1 à 10). Il s’agit d’une technique courante en analyse marketing (déciles de valeur client), en évaluation du risque de crédit (déciles de risque) et dans les tests AB (déciles de trafic pour l’analyse de cohortes). Le paramètre labels peut être une liste quelconque de longueur égale à celle de q ; utilisez des chaînes comme ['Bottom 10%', ..., 'Top 10%'] pour obtenir un résultat plus lisible.

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))

Intervalles de percentile au sein des groupes

Combinez groupby et pd.qcut avec transform pour créer des intervalles de percentile au sein de chaque groupe. Cette méthode est utile lorsque vous souhaitez classer les clients dans chaque région plutôt qu’à l’échelle globale : un client situé dans le décile global inférieur peut se trouver dans le décile régional supérieur. Utilisez 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))

Top-N au sein des groupes

Une question courante dans le monde professionnel est : « qui sont les 3 meilleurs éléments de chaque région ? » Utilisez groupby().rank(), puis filtrez selon le rang : df[df['rank_col'] <= 3]. Cette méthode fonctionne quelle que soit la taille du groupe et traite correctement les égalités en conservant plus de 3 lignes si plusieurs rangs sont égaux à la limite. Vous pouvez également utiliser groupby().nlargest(n), qui renvoie directement les n plus grandes valeurs de chaque groupe sans ajouter de colonne de rang.

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))

Combiner le classement et transform pour normaliser

Vous pouvez utiliser groupby().rank(pct=True) avec transform pour ajouter une colonne de rang en percentile au DataFrame d’origine. Cette colonne normalisée est souvent plus utile comme caractéristique d’un modèle que la valeur brute : elle est indépendante de l’échelle et comparable entre les groupes. En apprentissage automatique, les rangs en percentile constituent une solution simple de remplacement de la standardisation (score z), plus robuste aux valeurs aberrantes.

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))

Vérification rapide

Vérifiez votre compréhension des opérations de classement et de percentile présentées dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris que Series.rank() calcule des rangs numériques avec une résolution configurable des égalités, que groupby().rank() recommence le classement dans chaque groupe pour permettre une comparaison équitable entre les groupes, que pct=True convertit les rangs en percentiles (de 0 à 1), et que pd.qcut crée des intervalles de fréquence égale pour attribuer des niveaux de quartile, de décile et de percentile. Nous allons maintenant étudier les conseils d’optimisation des performances de Pandas : profilage, évitement des boucles lentes et utilisation de types de données efficaces.

Questions Fréquemment Posées

La leçon « Rangs et percentiles au sein des groupes » est-elle gratuite ?

Oui — le texte complet de « Rangs et percentiles au sein des groupes » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Pandas & NumPy Academy, passe à CoddyKit PRO. Le cours Pandas & NumPy Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Rangs et percentiles au sein des groupes » ?

Calculez les rangs au sein des groupes avec groupby().rank() et créez des classes de percentiles avec pd.qcut pour effectuer des comparaisons relatives. Tu pratiques Pandas & NumPy Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Pandas & NumPy Academy ?

Aucune expérience préalable n'est requise. Pandas & NumPy Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Rangs et percentiles au sein des groupes » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Pandas & NumPy Academy ?

Oui. Chaque leçon Pandas & NumPy Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Fenêtres glissantes
  2. Fenêtres cumulées
  3. Moyenne mobile pondérée exponentiellement
  4. Rangs et percentiles au sein des groupes
← Retour à Pandas & NumPy Academy