Анализируем DAU игр Вконтакте
By Firepush
Введение.
В данной статье я опишу одну интересную задачу и то, как я попытался ее решить. Результат получился не самым точным, надо признать. Но подход может быть применим и в других сферах.
Итак, речь пойдет о попытке определения размера активной дневной аудитории (DAU) для всех игр во Вконтакте.
Идея мне пришла после того, как я прочитал статью Галенкина о Steam Spy – его новом замечательном бесплатном сервисе анализа игр в Стиме (http://steamspy.com/). Удивительно, что до него никто не додумался до такой простой по сути идее.
Сохранение статуса игроков
Итак, наша задача – определить DAU игр. У Вконтакте есть довольно обширный набор методов API. Среди них есть метод получения информации об интересующем приложении: stats.get. Однако, подавляющее большинство игр скрывает в настройках статистику о DAU от любопытных глаз конкурентов.
Возможно, многие из вас замечали, что во время того, как запущено то или иное приложение в ВК, у вас автоматически проставляется статус с точным названием приложения:
tKN5TJ2
У пользователей есть возможность не показывать статусы из игр, но не думаю, что многие этим пользуются.
Анализируя частоту упоминаний точного вхождения названий игр в статусах пользователей ВК, теоретически можно получить оценку DAU.
Отлично! Пробуем.
Сначала создадим stand-alone приложение в ВК и сгенерируем access_token, который будем использовать для дальнейших запросов.
Первое, что приходит в голову: берем рандомные id живых людей и для каждого id смотрим статус (метод users.get, с указанием status в fields). Боль начинается тогда, когда начинаешь прикидывать, сколько времени понадобится на то, чтобы пробежаться по списку из хотя бы миллиона юзеров. ВК разрешает стучаться к API не чаще 3 раз в секунду. Дело в том, что играющих в конкретный момент юзеров в момент обхода выборки будет 1-2% от силы. С учетом того, что все они играют в разные игры, мы получим ничтожно мало данных для анализа.
Надеясь на то, что в API все же есть способ массового получения инфы о юзерах, я наткнулся на метод groups.getMembers, где тоже можно указать status в параметре fields. Супер! Теперь за раз можно получить 1000 статусов! С этим уже можно работать. Но это не все!
Update: как оказалось, в обычном users.get тоже можно запросить статус и указать сразу до 1000 id через запятую. Так что возня с группами была лишней)
У ВК есть такой неприметный метод в самом конце списка – execute. О том, что это, я узнал из этой статьи на Хабре: http://habrahabr.ru/post/248725/
“Универсальный метод, который позволяет запускать последовательность других методов, сохраняя и фильтруя промежуточные результаты”.
За один запрос мы можем запустить 25 любых других методов и получить обработанный результат! 25000 статусов за запрос – вот это поворот!
С основной проблемой разобрались, ок. Но из каких групп брать юзеров? Я решил, что лучше всего подойдут официальные сообщества игр, id которых легко спарсить из страничек описаний игр. Численные Id-шники самих приложений из топа и их названия можно легко получить методом apps.getCatalog.
В конечном итоге получаем простой алгоритм: берем id группы, собираем статусы участников (в больших группах я обходил не больше 150 тысяч пользователей), сопоставляем с эталонным списком названий игр. Если найдено соответствие конкретного статуса определенной игре, пишем в файл лога строку в формате: user_id;title;date. Переходим к следующей группе.
Далее я залил скрипт на виртуалку DigitalOcean, запустил и оставил на 8 часов. В результате получил файлик размером 50 мб формата csv, который я выгрузил в R и вручную проанализировал.
Анализ полученных данных.
1
2
3
4
5
6
7
|
library(dplyr)
p <– choose.files()
f <– read.csv(p, sep=“;”, header = FALSE, quote = “”, stringsAsFactors = FALSE, encoding=“UTF-8”)
f$V2 <– iconv(enc2native(f$V2))
df <– as.data.frame(table(f$V2))
df <– tbl_df(df)
df <– arrange(df, desc(Freq))
|
Вот таким нехитрым способом мы получаем табличку вида
title | freq | |
1 | Сокровища Пиратов | 43222 |
2 | Пасха 2015 | 40020 |
3 | Клондайк | 29546 |
4 | Инди Кот: Три в ряд! | 26142 |
5 | Вормикс | 24253 |
6 | Аватария — мир, где сбываются мечты | 22614 |
Где freq – это то, сколько раз был найден статус с игрой в выборке.
Уже определенные выводы можно сделать. Но продолжим.
Как узнать, похожи ли распределения этих частот на DAU соответствующих игр? Помните, я писал, что большая часть игр скрывает свою статистику? Но ведь у нас есть меньшая! Напишем скрипт, который пробежится по топу игр и запишет в лог те, у которых удалось выяснить точное значение DAU.
Получим таблицу вида:
id | title | dau | |
1 | 2388722 | Нано-ферма | 162633 |
2 | 2296756 | Запорожье | 139185 |
3 | 1968803 | World Poker Club – Покер | 123437 |
4 | 2164459 | Правила Войны | 115087 |
Далее делаем inner_join по столбцу title, фильтруем все, что с freq < 20 и coef>100 (там не игры получались) и считаем coef – отношение dau к freq:
1
2
3
|
result <– inner_join(dauTable, df, by=“title”)
result <– mutate(result, coef = round(dau/freq, 2))
result <– filter(result, coef < 100, freq > 20)
|
title | id | dau | freq | coef | |
1 | Нано-ферма | 2388722 | 162633 | 16759 | 9.70 |
2 | Запорожье | 2296756 | 139185 | 10250 | 13.58 |
3 | World Poker Club – Покер | 1968803 | 123437 | 4011 | 30.77 |
4 | Правила Войны | 2164459 | 115087 | 5481 | 21.00 |
5 | Войны Престолов | 2924782 | 88982 | 5485 | 16.22 |
Надежды на маленький разброс в значениях коэффициента рухнули. Без предварительных подсчетов стало ясно, что с точностью оценки все будет печально. Но доделаем дело до конца:
Посмотрим на “summary” значений coef:
Min. | 1st Qu. | Median | Mean | 3rd Qu. | Max. |
2.40 | 14.36 | 22.78 | 29.34 | 39.04 | 82.59 |
При стандартном отклонении в 19,72 коэффициент вариации равен 0,67. Много, как я и предполагал.
Посмотрим на гистограмму распределения coef:
Заметно длинное правое “плечо” у распределения. Это нужно иметь в виду, если нужно определять пределы, в которых находится значение при заданном уровне точности
Зависимость значения freq от соответствующей позиции в рейтинге DAU в нашей последней таблице:
Теперь осталось посчитать оценку DAU для всех приложений каталога:
1
2
|
resmean <– mean(result$coef)
predict <– mutate(df, pred = round(freq*resmean))
|
title | freq | pred | |
1 | Сокровища Пиратов | 43222 | 1268155 |
2 | Пасха 2015 | 40020 | 1174207 |
3 | Клондайк | 29546 | 866894 |
4 | Инди Кот: Три в ряд! | 26142 | 767019 |
5 | Вормикс | 24253 | 711595 |
6 | Аватария — мир, где сбываются мечты | 22614 | 663506 |
7 | Контра Сити | 20654 | 605999 |
8 | Долина Сладостей | 20098 | 589685 |
Выводы:
Как минимум мне удалось определить порядок размера аудитории игр в ВК, у которых скрыта статистика. Я проверил принципиальную реализуемость подобного подхода к исследованию и в целом доволен проделанной работой.
Потенциально есть много способов улучшить качество оценки: увеличить объем собираемых данных (например используя несколько аккаунтов для сбора данных), улучшить случайность выборки, увеличить частоту сбора и т.д. Но я не планирую это делать)