Pandas & NumPy Academy · Lección

Rangos y percentiles dentro de grupos

Calcule rangos dentro de cada grupo mediante groupby().rank() y cree intervalos percentiles con pd.qcut para realizar comparaciones relativas.

Lección 4 de 413 pasos

Rangos y percentiles dentro de grupos es una lección gratuita de Pandas & NumPy Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Pandas & NumPy Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

¿Por qué calcular rangos dentro de grupos?

Los valores sin procesar suelen ser menos significativos que las posiciones relativas. Un representante de ventas con 50.000 $ de ingresos mensuales es un empleado de alto rendimiento si el promedio es de 30.000 $, pero de bajo rendimiento si el promedio es de 80.000 $. Al calcular rangos dentro de grupos (por ejemplo, el rango dentro de cada región o de cada trimestre), obtiene una comparación normalizada que tiene en cuenta las distintas bases de referencia entre grupos. Pandas facilita el cálculo de rangos dentro de grupos mediante 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 rangos

Series.rank() asigna un rango a cada valor: el rango 1 corresponde al menor valor y el rango n, al mayor. El parámetro ascending=False invierte la dirección, de modo que el rango 1 corresponde al mayor valor. El resultado es una Series de tipo float (no entero), porque de forma predeterminada los empates se resuelven calculando el promedio de los rangos empatados. Para una columna con 5 valores, los rangos van de 1.0 a 5.0; si hay empates, algunos valores pueden compartir un rango 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 resolver empates en rank()

El parámetro method controla qué ocurre con los valores empatados. Las opciones son: 'average' (predeterminada: los valores empatados comparten la media de sus rangos), 'min' (todos reciben el rango más bajo), 'max' (todos reciben el rango más alto), 'first' (los rangos se asignan según el orden de aparición, sin empates) y 'dense' (no hay saltos en los rangos: 1, 2, 3, 3, 4 se convierte en 1, 2, 3, 3, 4 en lugar de 1, 2, 3, 3, 5). El método 'dense' es especialmente útil para rangos de tipo 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)

Cálculo de rangos dentro de grupos con groupby().rank()

groupby('group_col')['value_col'].rank() calcula los rangos de forma independiente dentro de cada grupo. El rango se reinicia al comienzo de cada grupo: por tanto, el empleado de mejor rendimiento de la región Este obtiene el rango 1 en el Este, y el mejor de la región Oeste también obtiene el rango 1 en el Oeste. Esto es lo que hace que groupby rank resulte tan útil para realizar comparaciones 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))

Cálculo de percentiles con rank(pct=True)

Establecer pct=True en rank() devuelve el rango percentil: un valor entre 0 y 1 que representa la fracción de valores del grupo que son menores o iguales que el valor actual. Un rango percentil de 0.8 significa que el valor se encuentra en el percentil 80: es superior al 80 % de todos los valores. Esta normalización permite comparar directamente valores de grupos de distinto tamaño sin conocer el tamaño real de los 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: agrupación basada en cuantiles

pd.qcut(series, q) divide los valores en q intervalos con igual frecuencia, de modo que cada intervalo contiene aproximadamente el mismo número de observaciones. A diferencia de pd.cut, que utiliza intervalos de igual amplitud, pd.qcut adapta los límites de los intervalos a la distribución de los datos. Es ideal para crear cuartiles (q=4), quintiles (q=5) o deciles (q=10) para clasificar niveles de rendimiento.

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 frente a pd.qcut

pd.cut(series, bins=n) crea intervalos de igual amplitud (el mismo rango, con distinta cantidad de valores). pd.qcut(series, q=n) crea intervalos de igual frecuencia (la misma cantidad de valores, con distintos rangos). En datos sesgados, pd.cut produce intervalos casi vacíos en los extremos, mientras que pd.qcut distribuye las observaciones de manera uniforme. Elija pd.cut cuando los límites de los intervalos tengan un significado empresarial natural (rangos de precios o grupos de edad); elija pd.qcut para clasificar niveles de rendimiento cuando desee grupos del mismo tamaño.

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

Creación de etiquetas de deciles con pd.qcut

pd.qcut(series, q=10, labels=range(1,11)) asigna cada observación a su decil (del 1 al 10). Esta es una técnica habitual en analítica de marketing (deciles de valor del cliente), evaluación crediticia (deciles de riesgo) y pruebas AB (deciles de tráfico para el análisis de cohortes). El parámetro labels puede ser cualquier lista cuya longitud sea igual a q; utilice cadenas como ['Bottom 10%', ..., 'Top 10%'] para obtener resultados más fáciles 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 percentiles dentro de grupos

Combine groupby con pd.qcut mediante transform para crear intervalos percentiles dentro de cada grupo. Esto resulta útil cuando desea clasificar a los clientes dentro de cada región, en lugar de hacerlo globalmente: un cliente del decil inferior global podría encontrarse en el decil superior de su región. Utilice 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))

Los N primeros dentro de grupos

Una pregunta empresarial habitual es «¿quiénes son los 3 empleados de mejor rendimiento de cada región?». Utilice groupby().rank() y filtre después por rango: df[df['rank_col'] <= 3]. Esto funciona correctamente con grupos de cualquier tamaño y gestiona los empates de forma adecuada, conservando más de 3 filas si hay rangos empatados en el límite. Como alternativa, utilice groupby().nlargest(n), que devuelve directamente los n valores más grandes de cada grupo sin añadir una columna de rangos.

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

Combinación de rank y transform para la normalización

Puede utilizar groupby().rank(pct=True) junto con transform para añadir una columna de rangos percentiles al DataFrame original. Esta columna normalizada suele ser más útil como característica de un modelo que el valor sin procesar: no depende de la escala y permite comparar grupos. En aprendizaje automático, los rangos percentiles son una alternativa sencilla a la estandarización (cálculo de puntuaciones z) y ofrecen mayor robustez frente a valores atípicos.

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

Comprobación rápida

Compruebe su comprensión de las operaciones de rangos y percentiles de esta lección.

Resumen de la lección

En esta lección ha aprendido que Series.rank() calcula rangos numéricos con resolución de empates configurable; groupby().rank() reinicia los rangos dentro de cada grupo para permitir comparaciones justas entre grupos; pct=True convierte los rangos en percentiles (de 0 a 1); y pd.qcut crea intervalos de igual frecuencia para asignar niveles de cuartiles, deciles y percentiles. A continuación exploraremos consejos de rendimiento de Pandas: creación de perfiles, cómo evitar bucles lentos y tipos de datos eficientes.

Gratis para empezar

Aprende Python con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
30
Lecciones
120

Preguntas frecuentes

¿La lección «Rangos y percentiles dentro de grupos» es gratis?

Sí — el texto completo de «Rangos y percentiles dentro de grupos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Pandas & NumPy Academy, actualiza a CoddyKit PRO. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Rangos y percentiles dentro de grupos»?

Calcule rangos dentro de cada grupo mediante groupby().rank() y cree intervalos percentiles con pd.qcut para realizar comparaciones relativas. Practicas Pandas & NumPy Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Pandas & NumPy Academy?

No se requiere experiencia previa. Pandas & NumPy Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Rangos y percentiles dentro de grupos»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Pandas & NumPy Academy?

Sí. Cada lección de Pandas & NumPy Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Ventanas móviles
  2. Ventanas expansivas
  3. Media móvil ponderada exponencialmente
  4. Rangos y percentiles dentro de grupos
← Volver a Pandas & NumPy Academy