Недавно передо мною встала задача сортировки фильмов по рейтингу. Каждый фильм имеет 2 значения — это средняя оценка и количество прогосовавших (точно так же, как на Кинопоиске и IMDB).
Первая мысль, естественно, была о Топ-250 Кинопоиска с их формулой. Но уже при первом взгляде она показалась мне неидеальной — непонятный выбор порога входа и общая средняя оценка вводят в ступор, ведь уже при чуть-чуть измененных условиях (меньшем количестве оценок или голосов) она работает крайне плохо.
О том, как я решил конкретно эту задачу и попутно для любых подобных рейтингов, читайте в статье.
Суть проблемы
В теории — чем выше оценка, тем фильм интереснее и тем выше он должен выходить, но…
Лучше всего ситуацию опишут примеры.
Пример №1
Фильм №1
Количество проголосовавших — 2
Средняя оценка — 10
Фильм №2
Количество проголосовавших — 100
Средняя оценка — 7
Да, фильм №1 клевый, но вряд ли кто-то захочет ориентироваться на мнение двух человек. Из этого следует, что вести расчет, исходя только из средней оценки — глупо, и в топе у нас всегда будут «фильмы на любителя».
Малобюджетных фильмов огромное количество, и несложно догадаться, что у большинства из них оценки будут достаточно высокими (смотреть их будут уже заинтересованные зрители, готовые к низкому качеству и ищущие плюсы, а не минусы).
Кинопоиск решил эту проблему введя порог голосов (M=500) и дав шансы малорекламируемым фильмам, приблизив их к средней оценке всех фильмов — чем ниже просмотров у фильма, тем на больший коэффициент поднимется его рейтинг.
Где:
V – количество голосов за фильм
M – порог голосов, необходимый для участия в рейтинге Топ-250 (сейчас: 500)
R – среднее арифметическое всех голосов за фильм
С – среднее значение рейтинга всех фильмов (сейчас: 7.3837)
Пример №2
Теперь рассмотрим следующие 2 фильма и посчитаем их рейтинг по формуле Кинопоиска:
Фильм №1
V = 500 (количество голосов за фильм)
M = 500 (порог голосов)
R = 2 (средняя оценка фильма)
C = 7.3837 (средняя оценка всех фильмов)
Raiting = 500/(500+500) * 2 + 500/(500+500) * 7,3837 = 4,69185 (кстати формулу можно сразу вставить в калькулятор :-)
Фильм №2
V = 1000
M = 500
R = 3
C = 7.3837
Raiting = 1000/(1000+500) * 3 + 500/(1000+500) * 7,3837 = 4,4612
Фильм с вдвое меньшим количеством голосов и меньшей оценкой получил бОльший рейтинг! Скорее всего, Кинопоиску выгоден такой подход — он стимулирует голосовать и, как следствие, в бесконечном будущем дает большую объективность.
Но у меня не стояло задачи призывать пользователей к голосованию, и такой подход не подходил — ведь в каждой весовой категории в дискретный момент времени будет нечестная сортировка. Да и выбор порога неочевиден и субъективен.
Итак, задача:
Составить честный топ фильмов с учетом количества голосов и средней оценки за фильм не используя константных значений
Решение
В итоге, мы решили подойти к вопросу не с математической точки зрения, а с философской:
- Итак, люди больше голосуют за прорекламированный фильм, обычно это фильм с бОльшим бюджетом. Чем выше бюджет, тем выше техническое качество фильма.
- Да, в целом, оценка фильма понятие субъективное, но нас интересует Топ для всех и, следовательно, стоит ориентироваться на популярность (popular — общераспространенный, общепонятный).
- Чем больше голосов за фильм — тем объективнее мы считаем оценку.
В итоге, мы пришли к тому, что и оценка и количество голосов являются примерно равными по важности факторами, и решение пришло само собой.
Для уравнивания этих двух значений — необходимо привести их к одной шкале. Максимальное количество голосов всегда разное, а вот рейтинг (о да!) от 1 до N. Для упрощения возьмем N=10. Следовательно, задача свелась к приведению количества голосов фильма к проценту от максимально возможного среди всех фильмов.
Дальше я расскажу о реализации подхода на Mysql — так как математики задачу уже решили, а остальным, надеюсь, интересно пощупать готовенькое.
Итак, создаем таблицу
CREATE TABLE IF NOT EXISTS `films` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`raiting` float NOT NULL,
`count_votes` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Добавляем 4 записи из примеров
Вы, наверное, уже догадались, что для высчитывания процента голосов нам понадобится функция max и логарифм. Итак:
select @a:=POW(max(count_votes), 1/10) from films;
select id,name,raiting, count_votes, (LOG(@a,count_votes))*raiting as actual_raiting from films order by actual_raiting desc ;
Мы получаем корень из 10 от максимального количества голосов для последующего подсчета логарифма. Так мы получаем долю количества голосов конкретного фильма от максимального, приведенного к 10. И перемножаем на среднюю оценку — так мы их коррелируем.
Добро пожаловать к результатам:
Фильм с двумя голосами имеет наименее объективную оценку и он ниже всех. Фильм №4, несмотря на малое количество голосов, существенно опережает соперников по рейтингу.
Таким образом, мы создали весовые категории фильмов (по популярности), и в каждой весовой категории сортируем их по рейтингу. Фильмы с примерно равнозначимым количеством голосов идеально сортируются по рейтингу.
Итак, мы избавились от порога и заставили фильмы играть честно, при этом давая шанс малорекламируемым, но клевым фильмам выбраться вверх в своей весовой категории.
Комплимент от шеф-повара: рейтинг актеров
Пришла пора объясниться за главную картинку статьи. Это рейтинг актеров.
После решения первой задачки — здесь решение нашлось мгновенно по тем же правилам: чем в больших фильмах снялся актер, тем он популярнее и лучше играет. Также его игра влияет на рейтинг фильма.
Итак, мы взяли максимальное количество фильмов у отдельного актера, подсчитали количество фильмов и их среднюю оценку для каждого актера и применили ту же формулу.
Вот так стал выглядеть Топ актеров
Для тех, кому интересно посмотреть на результат: http://vk.com/droptv
Автор: arvitaly