¿Cuál es una mejor manera de ordenar por una calificación de 5 estrellas?

Estoy tratando de clasificar un grupo de productos por calificaciones de clientes usando un sistema de 5 estrellas. El sitio que estoy configurando no tiene muchas clasificaciones y continúa agregando nuevos productos, por lo que generalmente tendrá algunos productos con un número bajo de calificaciones.

Intenté usar la calificación promedio de estrellas, pero ese algoritmo falla cuando hay un pequeño número de calificaciones.

Por ejemplo, un producto que tenga 3x calificaciones de 5 estrellas se mostraría mejor que un producto que tiene 100x clasificaciones de 5 estrellas y 2x calificaciones de 2 estrellas.

¿No debería aparecer el segundo producto más alto porque es estadísticamente más confiable debido a la mayor cantidad de calificaciones?

Para su lista de las 250 mejores películas, IMDB usa una estimación bayesiana . Esta es una buena forma de tener en cuenta el número de votantes.

Desde aquí :

La fórmula para calcular los 250 títulos más valorados da una verdadera estimación bayesiana:

calificación ponderada (WR) = (v ÷ (v + m)) × R + (m ÷ (v + m)) × C

dónde:

* R = average for the movie (mean) = (Rating) * v = number of votes for the movie = (votes) * m = minimum votes required to be listed in the Top 250 (currently 1300) * C = the mean vote across the whole report (currently 6.8) 

para los Top 250, solo se consideran los votos de votantes regulares.

Puede consultar esta página para obtener un buen análisis de la clasificación por estrellas:

http://www.evanmiller.org/ranking-items-with-star-ratings.html

Y puede mirar esta página para obtener un buen análisis de la votación arriba y abajo:

http://www.evanmiller.org/how-not-to-sort-by-average-rating.html

Para las votaciones ascendentes y descendentes, quiere estimar la probabilidad de que, dadas las clasificaciones que tiene, la puntuación “real” (si tiene clasificaciones infinitas) es mayor que alguna cantidad (como, por ejemplo, el número similar para algún otro elemento que está clasificando en contra.)

Vea el segundo artículo para la respuesta, pero la conclusión es que desea usar la confianza de Wilson. El artículo proporciona la ecuación y muestra el código Ruby (fácilmente traducido a otro idioma).

Evan Miller muestra un enfoque Bayesiano para clasificar clasificaciones de 5 estrellas: enter image description here

dónde

  • nk es el número de calificaciones k estrellas,
  • sk es el “valor” (en puntos) de k estrellas,
  • N es el número total de votos
  • K es la cantidad máxima de estrellas (p. Ej., K = 5, en un sistema de clasificación de 5 estrellas)
  • z_alpha/2 es el cuantil 1 - alpha/2 de una distribución normal. Si desea un 95% de confianza (basado en la distribución posterior bayesiana) de que el criterio de ordenación real sea al menos tan grande como el criterio de clasificación calculado, elija z_alpha/2 = 1.65.

En Python, el criterio de clasificación se puede calcular con

 def starsort(ns): """ http://www.evanmiller.org/ranking-items-with-star-ratings.html """ N = sum(ns) K = len(ns) s = list(range(K,0,-1)) s2 = [sk**2 for sk in s] z = 1.65 def f(s, ns): N = sum(ns) K = len(ns) return sum(sk*(nk+1) for sk, nk in zip(s,ns)) / (N+K) fsns = f(s, ns) return fsns - z*math.sqrt((f(s2, ns)- fsns**2)/(N+K+1)) 

Por ejemplo, si un elemento tiene 60 estrellas de cinco, 80 de cuatro estrellas, 75 de tres estrellas, 20 de dos estrellas y 25 de una estrella, entonces su calificación global de estrellas sería de aproximadamente 3.4:

 x = (60, 80, 75, 20, 25) starsort(x) # 3.3686975120774694 

y puedes ordenar una lista de clasificaciones de 5 estrellas con

 sorted([(60, 80, 75, 20, 25), (10,0,0,0,0), (5,0,0,0,0)], key=starsort, reverse=True) # [(10, 0, 0, 0, 0), (60, 80, 75, 20, 25), (5, 0, 0, 0, 0)] 

Esto muestra el efecto que pueden tener más clasificaciones sobre el valor total de la estrella.


Encontrará que esta fórmula tiende a dar una calificación general que es un poco más baja que la calificación general reportada por sitios como Amazon, Ebay o Wal-Mart, particularmente cuando hay pocos votos (digamos, menos de 300). Esto refleja la mayor incertidumbre que viene con menos votos. A medida que aumenta el número de votos (en miles), todas estas fórmulas de clasificación deberían tender a la calificación promedio (ponderada).


Dado que la fórmula solo depende de la distribución de frecuencia de las calificaciones de 5 estrellas para el artículo en sí, es fácil combinar las reseñas de múltiples fonts (o actualizar la calificación general a la luz de los nuevos votos) simplemente sumndo las distribuciones de frecuencia.


A diferencia de la fórmula de IMDb, esta fórmula no depende del puntaje promedio en todos los artículos, ni de un valor mínimo artificial de valor de corte de votos.

Además, esta fórmula hace uso de la distribución de frecuencia completa, no solo del número promedio de estrellas y el número de votos. Y tiene sentido que así sea, ya que un elemento con diez estrellas de cinco y diez de un estrellas debería tratarse como si tuviese más incertidumbre que (y por lo tanto no calificada como alta) un elemento con veinte clasificaciones de tres estrellas:

 In [78]: starsort((10,0,0,0,10)) Out[78]: 2.386028063783418 In [79]: starsort((0,0,20,0,0)) Out[79]: 2.795342687927806 

La fórmula de IMDb no tiene esto en cuenta.

Puede ordenar por mediana en lugar de media aritmética. En este caso, ambos ejemplos tienen una mediana de 5, por lo que ambos tendrían el mismo peso en un algoritmo de clasificación.

Puede usar un modo para el mismo efecto, pero la mediana es probablemente una mejor idea.

Si desea asignar un peso adicional al producto con 100 clasificaciones de 5 estrellas, es probable que desee ir con algún tipo de modo ponderado, asignando más peso a las calificaciones con la misma mediana, pero con más votos en general.

Bueno, dependiendo de qué tan complejo quieras hacerlo, podrías tener calificaciones adicionales según la cantidad de calificaciones que haya hecho la persona y las calificaciones. Si la persona solo ha hecho una calificación, podría ser una calificación de shill, y podría contar por menos. O si la persona ha calificado muchas cosas en la categoría a, pero pocas en la categoría b, y tiene una calificación promedio de 1.3 de 5 estrellas, parece que la categoría a puede estar artificialmente abrumada por la puntuación promedio baja de este usuario, y debe ser ajustado.

Pero ya basta de hacerlo complejo. Hagámoslo simple.

Suponiendo que estamos trabajando con solo dos valores, ReviewCount y AverageRating, para un artículo en particular, me parece lógico que considere que ReviewCount es esencialmente el valor de “confiabilidad”. Pero no solo queremos reducir los puntajes de los artículos con bajo puntaje de ReviewCount: una calificación de una estrella es probablemente tan poco fiable como una calificación de 5 estrellas. Entonces, lo que queremos hacer es probablemente el promedio hacia el medio: 3.

Entonces, básicamente, estoy pensando en una ecuación algo así como X * AverageRating + Y * 3 = the-rating-we-want. Para hacer que este valor salga bien, necesitamos que X + Y sea igual a 1. También necesitamos que X aumente de valor a medida que aumenta el valor de Revisión … con un conteo de revisión de 0, x debe ser 0 (dándonos una ecuación de ” 3 “), y con un recuento de revisión infinito X debe ser 1 (lo que hace que la ecuación sea igual a la media).

Entonces, ¿qué son las ecuaciones X e Y? Para la ecuación X, la variable dependiente se aproxima asintóticamente a 1 cuando la variable independiente se aproxima al infinito. Un buen conjunto de ecuaciones es algo así como: Y = 1 / (factor ^ RatingCount) y (utilizando el hecho de que X debe ser igual a 1-Y) X = 1 – (1 / (factor ^ RatingCount)

Luego, podemos ajustar el “factor” para que se ajuste al rango que estamos buscando.

Usé este sencillo progtwig de C # para probar algunos factores:

  // We can adjust this factor to adjust our curve. double factor = 1.5; // Here's some sample data double RatingAverage1 = 5; double RatingCount1 = 1; double RatingAverage2 = 4.5; double RatingCount2 = 5; double RatingAverage3 = 3.5; double RatingCount3 = 50000; // 50000 is not infinite, but it's probably plenty to closely simulate it. // Do the calculations double modfactor = Math.Pow(factor, RatingCount1); double modRating1 = (3 / modfactor) + (RatingAverage1 * (1 - 1 / modfactor)); double modfactor2 = Math.Pow(factor, RatingCount2); double modRating2 = (3 / modfactor2) + (RatingAverage2 * (1 - 1 / modfactor2)); double modfactor3 = Math.Pow(factor, RatingCount3); double modRating3 = (3 / modfactor3) + (RatingAverage3 * (1 - 1 / modfactor3)); Console.WriteLine(String.Format("RatingAverage: {0}, RatingCount: {1}, Adjusted Rating: {2:0.00}", RatingAverage1, RatingCount1, modRating1)); Console.WriteLine(String.Format("RatingAverage: {0}, RatingCount: {1}, Adjusted Rating: {2:0.00}", RatingAverage2, RatingCount2, modRating2)); Console.WriteLine(String.Format("RatingAverage: {0}, RatingCount: {1}, Adjusted Rating: {2:0.00}", RatingAverage3, RatingCount3, modRating3)); // Hold up for the user to read the data. Console.ReadLine(); 

Para que no se moleste en copiarlo, le da esta salida:

 RatingAverage: 5, RatingCount: 1, Adjusted Rating: 3.67 RatingAverage: 4.5, RatingCount: 5, Adjusted Rating: 4.30 RatingAverage: 3.5, RatingCount: 50000, Adjusted Rating: 3.50 

¿Algo como eso? Obviamente, puede ajustar el valor de “factor” según sea necesario para obtener el tipo de ponderación que desea.

Si solo necesitas una solución rápida y barata que funcione en su mayoría sin utilizar una gran cantidad de cálculos, esta es una opción (asumiendo una escala de calificación de 1-5)

 SELECT Products.id, Products.title, avg(Ratings.score), etc FROM Products INNER JOIN Ratings ON Products.id=Ratings.product_id GROUP BY Products.id, Products.title ORDER BY (SUM(Ratings.score)+25.0)/(COUNT(Ratings.id)+20.0) DESC, COUNT(Ratings.id) DESC 

Al sumr 25 y dividir por las calificaciones totales + 20, básicamente está agregando 10 puntajes más desfavorables y 10 mejores puntajes a las clasificaciones totales y luego ordenando en consecuencia.

Esto tiene problemas conocidos. Por ejemplo, recompensa injustamente a los productos de baja calificación con pocas calificaciones (como muestra este gráfico , los productos con un puntaje promedio de 1 y solo uno califican 1.2 mientras que los productos con un puntaje promedio de 1 y 1k + califican más cerca de 1.05). También podría argumentar que castiga injustamente productos de alta calidad con pocas calificaciones.

Este cuadro muestra lo que sucede para las 5 calificaciones con más de 1-1000 calificaciones: http://www.wolftwiglpha.com/input/?i=Plot3D%5B%2825%2Bxy%29/%2820%2Bx%29%2C%7Bx % 2C1% 2C1000% 7D% 2C% 7By% 2C0% 2C6% 7D% 5D

Puedes ver la subida hacia arriba en las calificaciones más bajas, pero en general es una clasificación justa, creo. También puedes mirarlo de esta manera:

http://www.wolftwiglpha.com/input/?i=Plot3D%5B6-%28%2825%2Bxy%29/%2820%2Bx%29%29%2C%7Bx%2C1%2C1000%7D%2C%7By % 2C0% 2C6% 7D% 5D

Si suelta una canica en la mayoría de los lugares en este gráfico, automáticamente se desplazará hacia productos con puntuaciones más altas y calificaciones más altas.

Obviamente, el bajo número de calificaciones coloca a este problema en una desventaja estadística. Sin embargo…

Un elemento clave para mejorar la calidad de una calificación global es “calificar al evaluador”, es decir, mantener un registro de las calificaciones que cada “evaluador” en particular ha proporcionado (en relación con los demás). Esto permite ponderar sus votos durante el proceso de agregación.

Otra solución, más de una solución, es proporcionar a los usuarios finales un recuento (o una indicación de rango) de los votos para el artículo subyacente.

Una opción es algo así como el sistema TrueSkill de Microsoft, donde la puntuación está dada por la mean - 3*stddev , donde las constantes se pueden ajustar.

Después de mirar por un rato, elijo el sistema bayesiano. Si alguien está usando Ruby, aquí una joya:

https://github.com/wbotelhos/rating

Recomiendo mucho el libro Programming Collective Intelligence de Toby Segaran (OReilly) ISBN 978-0-596-52932-1 que trata sobre cómo extraer datos significativos del comportamiento de la multitud. Los ejemplos están en Python, pero es lo suficientemente fácil de convertir.

    Intereting Posts