Más sobre ciencia de datos y estadística en: cienciadedatos.net
- Correlacion lineal
- Ajuste y selección de distribuciones
- Kernel density estimation KDE
- Comparación de distribuciones con Python: test Kolmogorov–Smirnov
- Análisis de normalidad
- Análisis de homocedasticidad
- Correlación lineal
- Test de permutación
- Bootstrapping
- T-test
- ANOVA
- U-test
- Reglas de asociación
- Ranking estadístico de productos
Introducción¶
Imagina que gestionas una plataforma de e-commerce y quieres ordenar tu catálogo de productos en base a la calidad percibida por los usuarios. Para ello dispones de un sistema de reseñas donde los clientes pueden valorar cada producto como "positivo" o "negativo". Una primera aproximación para ordenar los productos es calcular el porcentaje de reseñas positivas ($k/N$) y ordenarlos de mayor a menor. Sin embargo, este método tiene un gran problema: no tiene en cuenta la confianza estadística que da el tamaño de la muestra (número de reseñas).
Para ilustrar este problema, imagina que tienes los siguientes tres productos con sus respectivas reseñas:
| Producto | Reseñas positivas | Total reseñas | % reseñas positivas |
|---|---|---|---|
| Auriculares bluetooth | 1 | 1 | 100% |
| Novela bestseller | 4 | 5 | 80% |
| Portátil gaming | 95 | 100 | 95% |
El porcentaje de reseñas positivas (porcentaje simple) sitúa a los auriculares con una sola reseña por delante de un portátil con 95 reseñas positivas de 100. Intuitivamente sabemos que el portátil es mejor producto, pero el porcentaje simple ignora por completo la confianza estadística que da el tamaño de la muestra.
Este es un problema clásico en estadística conocido como el problema de la estimación de proporciones con muestras pequeñas, y aparece en multitud de sistemas reales:
- Ordenar los post de un foro o red social por su número de "likes" o "upvotes".
- Ordenar los restaurantes de una ciudad por su número de reseñas positivas.
- Ordenar los jugadores de un deporte por su porcentaje de aciertos (canastas, saques, penaltis, etc.).
En este documento se estudian y comparan estos cuatro enfoques estadísticos:
Promedio Bayesiano (Empirical Bayes): Asume que todo producto es "promedio" hasta que demuestre lo contrario con suficientes reseñas.
Modelo Beta-Binomial: La base teórica del promedio bayesiano, que además permite calcular intervalos de credibilidad al 95%.
Límite Inferior del Intervalo de Wilson: Clasifica por el peor rendimiento razonable de cada producto, no por su media.
Umbral Mínimo + Porcentaje Simple: Excluye del ranking a los productos con menos de $N_{min}$ reseñas.
Librerías¶
Las librerías utilizadas en este documento son:
# Tratamiento de datos
# ==============================================================================
import numpy as np
import pandas as pd
# Estadística
# ==============================================================================
from scipy.stats import norm, beta
# Visualización
# ==============================================================================
import matplotlib.pyplot as plt
Fundamentos estadísticos¶
El problema de las proporciones simples¶
Cuando un cliente deja una reseña positiva o negativa, estamos ante un ensayo de Bernoulli: el resultado es binario (positiva = 1, negativa = 0) con una probabilidad desconocida $p$ de ser positiva. El número total de reseñas positivas $k$ de entre $N$ reseñas totales sigue una distribución Binomial:
$$k \sim \text{Binomial}(N,\, p)$$La estimación más natural de $p$ es el estimador de máxima verosimilitud:
$$\hat{p}_{simple} = \frac{k}{N}$$Este estimador es insesgado y consistente, pero presenta un problema crítico cuando $N$ es pequeño: su varianza es muy alta.
$$\text{Var}(\hat{p}) = \frac{p(1-p)}{N}$$Con $N=1$, la varianza es máxima. El estimador toma solo los valores 0 o 1, sin ninguna capacidad discriminativa.
✏️ Nota
El problema no es el cálculo en sí, sino la incertidumbre. Un producto con 1 reseña positiva de un total de 1 reseña tiene un porcentaje del 100%, pero ese único dato no nos permite saber si su verdadera probabilidad de reseña positiva es del 50%, del 80% o del 99%. Necesitamos más datos para reducir esa incertidumbre.
Promedio Bayesiano (Empirical Bayes)¶
La solución del Bayes Empírico parte de una idea intuitiva: todo producto nuevo es, en principio, tan bueno como el promedio del catálogo, hasta que sus reseñas demuestren lo contrario. La fórmula añade a cada producto unos pseudoconteos basados en la media global:
$$\hat{p}_{bayes} = \frac{k + C \cdot \mu}{N + C}$$Donde los parámetros son:
| Parámetro | Descripción |
|---|---|
| $k$ | Número de reseñas positivas del producto |
| $N$ | Número total de reseñas del producto |
| $\mu$ | Porcentaje de reseñas positivas promedio de todo el catálogo (media global ponderada: $\sum k_i / \sum N_i$) |
| $C$ | Constante de confianza: cuánto peso le damos al promedio global. Habitualmente se usa la media de $N$ en el catálogo |
¿Por qué funciona? La fórmula puede reescribirse como una media ponderada entre la media global y el porcentaje observado del producto:
$$\hat{p}_{bayes} = \underbrace{\frac{N}{N+C}}_{\text{peso de los datos}} \cdot \frac{k}{N} \;\;+\;\; \underbrace{\frac{C}{N+C}}_{\text{peso del prior}} \cdot \mu$$Cuando $N \ll C$, el peso del prior domina y la estimación se acerca a $\mu$. Cuando $N \gg C$, el peso de los datos domina y la estimación converge a $k/N$. El parámetro $C$ controla exactamente cuántas reseñas necesita un producto para que el sistema "confíe" en sus propios datos por encima de la media del catálogo.
⚠️ ¿Cuándo NO usar el Promedio Bayesiano?
Este método asume que la media global μ es una referencia significativa para todos los productos. Si el catálogo es muy heterogéneo (por ejemplo, mezcla libros de texto con electrónica y ropa), la media global pierde sentido como prior. En ese caso, es preferible calcular μ y C por categoría de producto.💡 Tip: elección de C
Una elección práctica y habitual es usar la media del número de reseñas por producto en todo el catálogo. Esto significa que un producto nuevo tiene que acumular, al menos, tantas reseñas como el producto típico antes de que el sistema confíe plenamente en sus datos reales. Nótese que C es un hiperparámetro: un valor mayor de C implica un shrinkage más agresivo hacia la media global, mientras que un valor menor permite que incluso productos con pocas reseñas dependan más de sus propios datos.
Sensibilidad de las estimaciones a C: La siguiente tabla muestra cómo varía la estimación del Promedio Bayesiano de dos productos al cambiar el valor de $C$. Observa cómo un $C$ mayor acerca a ambos productos a la media global $\mu$, independientemente de su porcentaje observado.
# Sensibilidad del Promedio Bayesiano al hiperparámetro C
# ==============================================================================
mu_global = 367 / 600 # media global ponderada del catálogo
productos_sensibilidad = {
'Auriculares bluetooth': {'k': 1, 'N': 1},
'Portátil gaming': {'k': 95, 'N': 100},
}
C_valores = [5, 10, 25, 50, 75, 150, 300]
filas = []
for C_val in C_valores:
fila = {'C': C_val}
for nombre, d in productos_sensibilidad.items():
fila[nombre] = (d['k'] + C_val * mu_global) / (d['N'] + C_val)
filas.append(fila)
sensibilidad_df = pd.DataFrame(filas).set_index('C')
sensibilidad_df.loc['mu (prior)'] = {nombre: mu_global for nombre in productos_sensibilidad}
sensibilidad_df.style.format('{:.3f}').set_caption(
'Promedio Bayesiano según valor de C (última fila = media global μ usada como prior)'
)
| Auriculares bluetooth | Portátil gaming | |
|---|---|---|
| C | ||
| 5 | 0.676 | 0.934 |
| 10 | 0.647 | 0.919 |
| 25 | 0.627 | 0.882 |
| 50 | 0.619 | 0.837 |
| 75 | 0.617 | 0.805 |
| 150 | 0.614 | 0.747 |
| 300 | 0.613 | 0.696 |
| mu (prior) | 0.612 | 0.612 |
Conexión con el modelo Beta-Binomial¶
La fórmula del Promedio Bayesiano no es arbitraria: es exactamente la media de la distribución posterior de un modelo bayesiano con prior Beta y verosimilitud Binomial.
Si asumimos un prior $p \sim \text{Beta}(\alpha_0, \beta_0)$ con:
$$\alpha_0 = C \cdot \mu \qquad \beta_0 = C \cdot (1 - \mu)$$Tras observar $k$ reseñas positivas de $N$ totales, la distribución posterior es:
$$p \mid k,N \sim \text{Beta}(\alpha_0 + k,\; \beta_0 + N - k)$$La media de esta distribución posterior es exactamente:
$$\mathbb{E}[p \mid k, N] = \frac{\alpha_0 + k}{\alpha_0 + \beta_0 + N} = \frac{k + C \cdot \mu}{N + C}$$Que es la fórmula del Promedio Bayesiano. La ventaja adicional de conocer la distribución posterior completa es que permite calcular intervalos de credibilidad al 95% de forma analítica con scipy.stats.beta.ppf, sin necesidad de simulación.
✏️ Nota: ¿Por qué no hace falta PyMC?
PyMC es una librería de programación probabilística que usa simulación MCMC para aproximar distribuciones posteriores complejas. Para este problema, la conjugación Beta-Binomial nos da una solución analítica exacta: la distribución posterior tiene una forma cerrada conocida. No necesitamos simulación; basta con calcular αpost y βpost y evaluar la función de distribución con scipy.stats.beta.
Límite inferior del Intervalo de Wilson¶
El intervalo de Wilson es un intervalo de confianza para proporciones que sigue siendo válido incluso con $N$ pequeño, a diferencia del intervalo de Wald (la aproximación normal clásica $\hat{p} \pm z\sqrt{\hat{p}(1-\hat{p})/N}$), que puede producir valores fuera de $[0, 1]$ y cuya cobertura real se deteriora severamente con pocas observaciones. En lugar de ordenar por la estimación puntual $k/N$, este método ordena por el límite inferior del intervalo: el valor de $p$ más pesimista que es aún estadísticamente compatible con los datos observados al nivel de confianza dado.
El límite inferior del intervalo de Wilson al nivel de confianza $1-\alpha$ es:
$$w^- = \frac{\hat{p} + \frac{z^2}{2N} - z\sqrt{\frac{\hat{p}(1-\hat{p})}{N} + \frac{z^2}{4N^2}}}{1 + \frac{z^2}{N}}$$Donde $\hat{p} = k/N$ y $z = z_{1-\alpha/2}$ es el percentil de la distribución normal estándar (por ejemplo, $z = 1.96$ para un nivel de confianza del 95%).
Ejemplo intuitivo:
- Auriculares bluetooth (1/1, 100%): mucha incertidumbre → su límite inferior es aproximadamente 20%.
- Portátil gaming (95/100, 95%): poca incertidumbre → su límite inferior es aproximadamente 88%.
✏️ Nota
Es un enfoque conservador: para subir en el ranking, hay que demostrar que se es bueno acumulando reseñas, no solo tener suerte con las primeras.
✏️ Nota: ¿Por qué no el intervalo de Wald?
El intervalo de Wald clásico p̂ ± z√(p̂(1−p̂)/N) tiene dos modos de fallo con muestras pequeñas: (1) puede producir límites fuera de [0, 1] (p. ej., un producto con 0 reseñas positivas de 3 obtiene un límite inferior de −0), y (2) su cobertura real puede quedar muy por debajo del nivel nominal (p. ej., un intervalo nominal al 95% puede tener solo un 85% de cobertura real). Brown, Cai & DasGupta (2001) demostraron que el intervalo de Wilson ofrece propiedades de cobertura sustancialmente mejores para todos los valores de p y N. Esta es la razón principal por la que las plataformas usan Wilson en lugar de la fórmula de Wald.
Umbral mínimo + porcentaje simple¶
La aproximación más sencilla consiste en:
- Definir un umbral $N_{min}$ (por ejemplo, 10 reseñas).
- Excluir del ranking a todos los productos con $N < N_{min}$. Se muestran como "sin suficientes reseñas".
- Para los productos que superan el umbral, ordenar por $\hat{p}_{simple} = k/N$.
Este enfoque es fácil de implementar y de comunicar a los usuarios. Su limitación principal es la frontera rígida: un producto con 9 reseñas se trata de forma idéntica a uno con 0 reseñas, mientras que uno con 10 reseñas mediocres entra en el ranking por delante de uno con 9 reseñas excelentes. La elección de $N_{min}$ también es arbitraria — los valores habituales en sistemas en producción van de 5 a 50 según la categoría — y suele requerir contexto de negocio para justificarse.
Los enfoques bayesiano y de Wilson evitan esta exclusión binaria aplicando una penalización suave: todos los productos aparecen en el ranking, pero los que tienen pocas reseñas quedan automáticamente relegados por la estimación conservadora.
Ejemplo¶
Datos¶
Se crea un catálogo con 8 productos para ilustrar el comportamiento de cada método.
# Datos
# ==============================================================================
datos = {
'producto': [
'Auriculares bluetooth', # 1/1: nuevo perfecto,
'Portátil gaming', # 95/100: veterano excelente
'Funda de móvil', # 200/400: mediocre con mucho historial
'Novela bestseller', # 4/5: pocas pero muy buenas
'Teclado mecánico', # 2/5: pocas reseñas mixtas
'Silla de oficina', # 8/9: justo por debajo del umbral de 10 reseñas
'Zapatillas running', # 45/60: buenas con historial medio
'Batería externa', # 12/20: mediocre con historial suficiente
],
'categoria': [
'Electrónica', 'Electrónica', 'Accesorios', 'Libros',
'Electrónica', 'Mobiliario', 'Moda', 'Electrónica',
],
'resenas_positivas': [1, 95, 200, 4, 2, 8, 45, 12],
'total_resenas': [1, 100, 400, 5, 5, 9, 60, 20],
}
datos = pd.DataFrame(datos)
datos
| producto | categoria | resenas_positivas | total_resenas | |
|---|---|---|---|---|
| 0 | Auriculares bluetooth | Electrónica | 1 | 1 |
| 1 | Portátil gaming | Electrónica | 95 | 100 |
| 2 | Funda de móvil | Accesorios | 200 | 400 |
| 3 | Novela bestseller | Libros | 4 | 5 |
| 4 | Teclado mecánico | Electrónica | 2 | 5 |
| 5 | Silla de oficina | Mobiliario | 8 | 9 |
| 6 | Zapatillas running | Moda | 45 | 60 |
| 7 | Batería externa | Electrónica | 12 | 20 |
Porcentaje simple¶
# Porcentaje simple
# ==============================================================================
datos['pct_simple'] = datos['resenas_positivas'] / datos['total_resenas']
ranking_simple = (
datos[['producto', 'resenas_positivas', 'total_resenas', 'pct_simple']]
.sort_values('pct_simple', ascending=False)
.reset_index(drop=True)
)
ranking_simple
| producto | resenas_positivas | total_resenas | pct_simple | |
|---|---|---|---|---|
| 0 | Auriculares bluetooth | 1 | 1 | 1.000000 |
| 1 | Portátil gaming | 95 | 100 | 0.950000 |
| 2 | Silla de oficina | 8 | 9 | 0.888889 |
| 3 | Novela bestseller | 4 | 5 | 0.800000 |
| 4 | Zapatillas running | 45 | 60 | 0.750000 |
| 5 | Batería externa | 12 | 20 | 0.600000 |
| 6 | Funda de móvil | 200 | 400 | 0.500000 |
| 7 | Teclado mecánico | 2 | 5 | 0.400000 |
Con el porcentaje simple, los auriculares (1/1) ocupan el primer puesto con un 100%, por delante del portátil gaming (95/100). La silla de oficina (8/9, ~89%) también aparece en posiciones altas a pesar de tener muy pocas reseñas.
Promedio Bayesiano¶
El primer paso es calcular los parámetros globales del catálogo:
$\mu$: el porcentaje de reseñas positivas promedio de todo el catálogo.
$C$: la constante de confianza, que se elige como la media del número de reseñas por producto.
# Parámetros globales del catálogo
# ==============================================================================
mu = datos['resenas_positivas'].sum() / datos['total_resenas'].sum()
C = datos['total_resenas'].mean()
print(f'Promedio global (mu): {mu}')
print(f'Constante de confianza (C): {C}')
Promedio global (mu): 0.6116666666666667 Constante de confianza (C): 75.0
✏️ Nota: media ponderada vs. media aritmética
μ se calcula como la media global ponderada Σki / ΣNi, no como la media aritmética de las proporciones individuales media(ki / Ni). Son numéricamente distintas. La versión ponderada da más influencia a los productos con más reseñas, lo cual es adecuado: refleja la verdadera proporción de reseñas positivas en todas las interacciones del catálogo. La media aritmética daría a un producto con 1 reseña la misma influencia que a uno con 10.000, distorsionando el prior.
El siguiente paso es aplicar la fórmula del Promedio Bayesiano a cada producto.
# Promedio Bayesiano
# ==============================================================================
k = datos['resenas_positivas']
N = datos['total_resenas']
datos['pct_bayesiano'] = (k + C * mu) / (N + C)
ranking_bayesiano = (
datos[['producto', 'resenas_positivas', 'total_resenas', 'pct_simple', 'pct_bayesiano']]
.sort_values('pct_bayesiano', ascending=False)
.reset_index(drop=True)
)
ranking_bayesiano
| producto | resenas_positivas | total_resenas | pct_simple | pct_bayesiano | |
|---|---|---|---|---|---|
| 0 | Portátil gaming | 95 | 100 | 0.950000 | 0.805000 |
| 1 | Zapatillas running | 45 | 60 | 0.750000 | 0.673148 |
| 2 | Silla de oficina | 8 | 9 | 0.888889 | 0.641369 |
| 3 | Novela bestseller | 4 | 5 | 0.800000 | 0.623437 |
| 4 | Auriculares bluetooth | 1 | 1 | 1.000000 | 0.616776 |
| 5 | Batería externa | 12 | 20 | 0.600000 | 0.609211 |
| 6 | Teclado mecánico | 2 | 5 | 0.400000 | 0.598437 |
| 7 | Funda de móvil | 200 | 400 | 0.500000 | 0.517632 |
El Promedio Bayesiano corrige el ranking como era de esperar. El portátil gaming sube al primer puesto: con 100 reseñas ($N > C$), su porcentaje observado (95%) tiene suficiente peso para superar la atracción del prior. Los auriculares caen notablemente: con solo 1 reseña ($N \ll C$), su estimación está casi completamente determinada por el prior $\mu$, no por el único acierto observado — este es el efecto de shrinkage de la estimación bayesiana. La funda de móvil ilustra el caso asintótico opuesto: con 400 reseñas ($N \gg C$), el prior contribuye de forma negligible y la estimación es prácticamente idéntica a su proporción real (~50%), que está muy por debajo de la media del catálogo.
Modelo Beta-Binomial: intervalo de credibilidad al 95%¶
El modelo Beta-Binomial nos permite calcular intervalos de credibilidad al 95% para cada producto. Ordenamos el ranking por la media del posterior (que coincide con el Promedio Bayesiano), pero mostramos también el intervalo completo.
# Parámetros del prior Beta
# ==============================================================================
alpha_0 = C * mu
beta_0 = C * (1 - mu)
# Parámetros de la distribución posterior por producto
# ==============================================================================
alpha_post = alpha_0 + k
beta_post = beta_0 + (N - k)
# Intervalo de credibilidad al 95%
# ==============================================================================
datos['cred_95_inf'] = beta.ppf(0.025, alpha_post, beta_post)
datos['cred_95_sup'] = beta.ppf(0.975, alpha_post, beta_post)
cols_beta = ['producto', 'total_resenas', 'pct_simple', 'pct_bayesiano', 'cred_95_inf', 'cred_95_sup']
(
datos[cols_beta]
.sort_values('pct_bayesiano', ascending=False)
.reset_index(drop=True)
)
| producto | total_resenas | pct_simple | pct_bayesiano | cred_95_inf | cred_95_sup | |
|---|---|---|---|---|---|---|
| 0 | Portátil gaming | 100 | 0.950000 | 0.805000 | 0.743307 | 0.860095 |
| 1 | Zapatillas running | 60 | 0.750000 | 0.673148 | 0.592037 | 0.749399 |
| 2 | Silla de oficina | 9 | 0.888889 | 0.641369 | 0.536522 | 0.739839 |
| 3 | Novela bestseller | 5 | 0.800000 | 0.623437 | 0.515312 | 0.725716 |
| 4 | Auriculares bluetooth | 1 | 1.000000 | 0.616776 | 0.505611 | 0.722118 |
| 5 | Batería externa | 20 | 0.600000 | 0.609211 | 0.509667 | 0.704397 |
| 6 | Teclado mecánico | 5 | 0.400000 | 0.598437 | 0.489657 | 0.702554 |
| 7 | Funda de móvil | 400 | 0.500000 | 0.517632 | 0.472692 | 0.562431 |
El intervalo de credibilidad revela la incertidumbre real que hay detrás de cada estimación puntual:
- Auriculares bluetooth (1 reseña): el intervalo va de ~51% a ~72%. Es el más amplio del catálogo. Con $C \approx 75$ pseudoreseñas virtuales y solo 1 observación real, el prior tiene aproximadamente 75× más peso que los datos — el posterior está casi completamente determinado por el prior, lo que explica por qué el intervalo, aunque amplio, se centra bastante por encima del 50%.
- Portátil gaming (100 reseñas): el intervalo es estrecho (~74% a ~86%). Con $N > C$, las 100 reseñas reales superan en peso al prior y el sistema confía en sus datos observados.
- Funda de móvil (400 reseñas): el intervalo es el más estrecho de todos (~47% a ~56%). Con $N \gg C$, el posterior está dominado casi íntegramente por las 400 reseñas observadas y el sistema sabe con alta precisión que es un producto mediocre.
Este es el insight clave del shrinkage bayesiano: el ancho del intervalo de credibilidad no depende solo de $N$, sino del cociente $N/C$. Un producto necesita $N \gg C$ reseñas para que su incertidumbre sea comparable a la de un producto con muchas reseñas en un contexto no bayesiano.
Límite inferior del Intervalo de Wilson¶
El límite inferior del intervalo de Wilson ordena por el peor rendimiento razonable de cada producto.
# Límite inferior del Intervalo de Wilson
# ==============================================================================
confianza = 0.95 # nivel de confianza
z = norm.ppf(1 - (1 - confianza) / 2) # z = 1.96 para 95%
p_hat = datos['pct_simple']
datos['wilson_lower'] = (
(p_hat + z**2 / (2 * N) - z * np.sqrt(p_hat * (1 - p_hat) / N + z**2 / (4 * N**2)))
/ (1 + z**2 / N)
)
ranking_wilson = (
datos[['producto', 'resenas_positivas', 'total_resenas', 'pct_simple', 'wilson_lower']]
.sort_values('wilson_lower', ascending=False)
.reset_index(drop=True)
)
ranking_wilson
| producto | resenas_positivas | total_resenas | pct_simple | wilson_lower | |
|---|---|---|---|---|---|
| 0 | Portátil gaming | 95 | 100 | 0.950000 | 0.888250 |
| 1 | Zapatillas running | 45 | 60 | 0.750000 | 0.627679 |
| 2 | Silla de oficina | 8 | 9 | 0.888889 | 0.565000 |
| 3 | Funda de móvil | 200 | 400 | 0.500000 | 0.451235 |
| 4 | Batería externa | 12 | 20 | 0.600000 | 0.386582 |
| 5 | Novela bestseller | 4 | 5 | 0.800000 | 0.375535 |
| 6 | Auriculares bluetooth | 1 | 1 | 1.000000 | 0.206549 |
| 7 | Teclado mecánico | 2 | 5 | 0.400000 | 0.117621 |
El Intervalo de Wilson es el método más conservador. Penaliza fuertemente la incertidumbre: los auriculares (1/1) caen casi al final del ranking porque su límite inferior es muy bajo (~21%). La silla de oficina (8/9) también baja notablemente a pesar de su alto porcentaje simple.
⚠️ Caso límite: productos sin reseñas positivas (p̂ = 0)
Cuando p̂ = 0, el término p̂(1−p̂) = 0 y la fórmula de Wilson se simplifica a w− = 0 / (1 + z2/N), que da exactamente 0. El límite inferior está bien definido en este caso. Sin embargo, si p̂ = 1 (todas las reseñas positivas), la fórmula también da un w− bien definido, pero el límite superior w+ solo se aproxima a 1 asintóticamente. En sistemas en producción, es habitual aplicar un clamp max(0, w−) para evitar cualquier caso límite de precisión numérica y manejar productos sin ninguna reseña (N = 0), donde la fórmula no está definida.
Umbral mínimo¶
El método del umbral mínimo excluye a los productos con menos de 10 reseñas.
# Umbral mínimo de reseñas
# ==============================================================================
N_min = 10
print(f'Productos con N >= {N_min} reseñas (incluidos en el ranking):')
ranking_umbral = (
datos.loc[datos['total_resenas'] >= N_min, ['producto', 'resenas_positivas', 'total_resenas', 'pct_simple']]
.sort_values('pct_simple', ascending=False)
.reset_index(drop=True)
)
display(ranking_umbral)
print(f'\nProductos excluidos del ranking (N < {N_min}):')
excluidos = datos.loc[datos['total_resenas'] < N_min, ['producto', 'resenas_positivas', 'total_resenas']]
excluidos
Productos con N >= 10 reseñas (incluidos en el ranking):
| producto | resenas_positivas | total_resenas | pct_simple | |
|---|---|---|---|---|
| 0 | Portátil gaming | 95 | 100 | 0.95 |
| 1 | Zapatillas running | 45 | 60 | 0.75 |
| 2 | Batería externa | 12 | 20 | 0.60 |
| 3 | Funda de móvil | 200 | 400 | 0.50 |
Productos excluidos del ranking (N < 10):
| producto | resenas_positivas | total_resenas | |
|---|---|---|---|
| 0 | Auriculares bluetooth | 1 | 1 |
| 3 | Novela bestseller | 4 | 5 |
| 4 | Teclado mecánico | 2 | 5 |
| 5 | Silla de oficina | 8 | 9 |
Con $N_{min} = 10$, quedan excluidos del ranking los auriculares, la novela bestseller, el teclado mecánico y la silla de oficina. Esta última, que tiene un 89% de reseñas positivas con 9 reseñas, no aparece en el ranking a pesar de ser el segundo producto con mejor porcentaje simple.
Comparación de rankings¶
El siguiente gráfico muestra la posición (ranking) de cada producto según cada uno de los cuatro métodos. Los productos con la misma posición en todos los métodos son estables; los que cambian significativamente de posición son los más afectados por la incertidumbre en sus reseñas.
# Comparación de rankings entre los cuatro métodos
# ==============================================================================
rank_df = datos[['producto']].copy()
rank_df['Simple %'] = (
datos['pct_simple']
.rank(ascending=False, method='min')
.astype(int)
)
rank_df['Bayesian Avg'] = (
datos['pct_bayesiano']
.rank(ascending=False, method='min')
.astype(int)
)
rank_df['Wilson LB'] = (
datos['wilson_lower']
.rank(ascending=False, method='min')
.astype(int)
)
included_mask = datos['total_resenas'] >= N_min
rank_df['Min Threshold'] = (
datos['pct_simple']
.where(included_mask)
.rank(ascending=False, method='min')
)
rank_df = rank_df.set_index('producto')
display(rank_df)
# Bump chart
fig, ax = plt.subplots(figsize=(8, 5))
for product, ranks in rank_df.iterrows():
ax.plot(
rank_df.columns,
ranks,
marker='o',
linewidth=2,
label=product
)
ax.invert_yaxis()
ax.set_ylabel('Rango')
ax.set_title('Comparación de rankings entre métodos')
ax.grid(axis='y', linestyle='--', alpha=0.3)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.legend(
title='Producto',
bbox_to_anchor=(1.02, 1),
loc='upper left'
);
| Simple % | Bayesian Avg | Wilson LB | Min Threshold | |
|---|---|---|---|---|
| producto | ||||
| Auriculares bluetooth | 1 | 5 | 7 | NaN |
| Portátil gaming | 2 | 1 | 1 | 1.0 |
| Funda de móvil | 7 | 8 | 4 | 4.0 |
| Novela bestseller | 4 | 4 | 6 | NaN |
| Teclado mecánico | 8 | 7 | 8 | NaN |
| Silla de oficina | 3 | 3 | 3 | NaN |
| Zapatillas running | 5 | 2 | 2 | 2.0 |
| Batería externa | 6 | 6 | 5 | 3.0 |
Conclusiones¶
Los métodos estudiados resuelven de formas distintas el mismo problema fundamental: la inestabilidad del porcentaje simple con muestras pequeñas.
| Método | Complejidad | Cuándo usarlo | Ventaja | Desventaja |
|---|---|---|---|---|
| % Simple | Muy baja | Nunca como ranking final | Intuitivo y fácil de comunicar | Sobrevalora productos con pocas reseñas |
| Promedio Bayesiano | Baja | Ranking unificado donde todos los productos aparecen desde el primer día | Justo con productos nuevos y veteranos; interpreta fácil | Requiere que la media global $\mu$ sea representativa del catálogo |
| Beta-Binomial | Baja | Igual que el Bayesiano, más intervalo de credibilidad | Da la incertidumbre completa; no requiere MCMC | Ligeramente más complejo de implementar |
| Intervalo de Wilson | Baja-media | Entornos muy competitivos donde se premia la consistencia | Muy conservador; evita el "golpe de suerte" inicial | Penaliza fuertemente los productos nuevos aunque tengan buena señal |
| Umbral mínimo | Muy baja | Sistemas simples donde la exclusión temporal es aceptable | Fácil de implementar y de comunicar a los usuarios | Excluye completamente productos nuevos del ranking |
Recomendación según caso de uso:
- Usa el Promedio Bayesiano si quieres un ranking unificado y justo donde todos los productos (nuevos y veteranos) aparezcan desde el primer día sin penalizar demasiado a nadie. Es el estándar en plataformas de e-commerce.
- Añade el intervalo de credibilidad Beta-Binomial si necesitas comunicar al usuario la incertidumbre de cada estimación (por ejemplo, mostrar una barra de confianza junto a las estrellas).
- Usa el Intervalo de Wilson si el contexto es muy competitivo y quieres ser estricto, como en rankings de comentarios o reseñas de apps, donde los primeros resultados tienen un impacto enorme.
- Usa el umbral mínimo solo si la simplicidad de implementación es un requisito y la exclusión temporal de productos nuevos es aceptable para tu negocio.
Información de sesión¶
import session_info
session_info.show(html=False)
----- matplotlib 3.10.8 numpy 1.26.4 pandas 2.2.3 scipy 1.11.4 session_info v1.0.1 ----- IPython 9.10.1 jupyter_client 8.8.0 jupyter_core 5.9.1 ----- Python 3.11.15 (main, Mar 11 2026, 17:20:07) [GCC 14.3.0] Linux-6.17.0-29-generic-x86_64-with-glibc2.39 ----- Session information updated at 2026-06-01 14:09
Instrucciones para citar¶
¿Cómo citar este documento?
Si utilizas este documento o alguna parte de él, te agradecemos que lo cites. ¡Muchas gracias!
Ranking estadístico de productos: Bayes Empírico, Intervalo de Wilson y Umbral Mínimo por Joaquín Amat Rodrigo, disponible bajo una licencia Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0 DEED) en https://www.cienciadedatos.net/documentos/pystats13-ranking-estadistico-python.html
¿Te ha gustado el artículo? Tu ayuda es importante
Tu contribución me ayudará a seguir generando contenido divulgativo gratuito. ¡Muchísimas gracias! 😊
Este documento creado por Joaquín Amat Rodrigo tiene licencia Attribution-NonCommercial-ShareAlike 4.0 International.
Se permite:
-
Compartir: copiar y redistribuir el material en cualquier medio o formato.
-
Adaptar: remezclar, transformar y crear a partir del material.
Bajo los siguientes términos:
-
Atribución: Debes otorgar el crédito adecuado, proporcionar un enlace a la licencia e indicar si se realizaron cambios. Puedes hacerlo de cualquier manera razonable, pero no de una forma que sugiera que el licenciante te respalda o respalda tu uso.
-
No-Comercial: No puedes utilizar el material para fines comerciales.
-
Compartir-Igual: Si remezclas, transformas o creas a partir del material, debes distribuir tus contribuciones bajo la misma licencia que el original.
