{
    "version": "https:\/\/jsonfeed.org\/version\/1",
    "title": "Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: roistat",
    "home_page_url": "http:\/\/test.leftjoin.ru\/tags\/roistat\/",
    "feed_url": "http:\/\/test.leftjoin.ru\/tags\/roistat\/json\/",
    "icon": "http:\/\/test.leftjoin.ru\/user\/userpic@2x.jpg",
    "author": {
        "name": "Николай Валиотти",
        "url": "http:\/\/test.leftjoin.ru\/",
        "avatar": "http:\/\/test.leftjoin.ru\/user\/userpic@2x.jpg"
    },
    "items": [
        {
            "id": "149",
            "url": "http:\/\/test.leftjoin.ru\/all\/problema-roistat-kak-gotovogo-resheniya-dlya-analitiki\/",
            "title": "Проблема Roistat как готового решения для аналитики",
            "content_html": "<p><img src=http:\/\/test.leftjoin.ru\/pictures\/unnamed-1.png  border=“0” width=100% height=100%><\/p>\n<p>Нередко компании, которые обращаются к нам за системой сквозной аналитики, уже используют какие-то инструменты для работы с данными. Иногда это «коробочные» решения. Сразу отказываться от них в пользу кастома не обязательно — на старте их можно интегрировать в новую систему, чтобы сэкономить время и деньги на разработку. Но в этом случае надо быть готовым к тому, что готовые решения не гарантируют точность данных на том же уровне, что специально построенная под вас система аналитики. Именно с этой проблемой мы столкнулись, когда начали строить аналитику для стартапа Refocus: до нас они использовали Roistat, но ошибки в его работе вынудили нас отказаться от него раньше, чем мы планировали.<\/p>\n<p>Способов построить сквозную аналитику, не обращаясь к готовым решениям, немало: можно собрать in-house отдел аналитики или найти специалистов на аутсорсе.<\/p>\n<p>При этом нужно учитывать, что работа целой команды аналитики может стоить порядка 5-7 тысяч евро или 500-700 тысяч рублей в месяц. И это не предел — все зависит от размеров компании-заказчика, состояния аналитики до начала работ и других факторов.<\/p>\n<p>На начальном этапе для многих компаний, у которых объем данных небольшой, такие расходы неоправданы. Из-за этого они часто предпочитают «коробочные» продукты — например, тот же <b>Roistat.<\/b> Это самое популярное решение на рынке, которое предлагает премиум-тариф с максимумом функционала и полной поддержкой от 1500 евро \/ 150 тысяч рублей в месяц. Базовые тарифы у них и того дешевле. У этого инструмента больше 200 встроенных интеграций, и не только с популярными Google Analytics или Яндекс Директ, но и с целым списком нишевых кабинетов и систем, о которых вы даже не слышали.<\/p>\n<p>После настройки интеграций с вашими источниками Roistat позволит создать дашборды за пару кликов, а сверху предложит автоматизацию маркетинга и лидогенерации. Конкуренты Roistat вроде <b>OWOX BI<\/b> или <b>Alytics<\/b> также предоставляют базовые возможности для аналитики и визуализации. В общем, недорогих решений под любые потребности много.<\/p>\n<p>Хотя “коробка” гораздо лучше бесконечных таблиц и поначалу хорошо оптимизирует работу с данными, она далеко не идеальна. С точки зрения эксперта все минусы универсальных решений видны как на ладони:<\/p>\n<ul>\n<li>не заглянешь под капот, если что-то идет не так;<\/li>\n<li>их система интеграций может не включать нужных источников;<\/li>\n<li>интеграции могут устаревать, создавая ошибки в выгрузке данных.<\/li>\n<\/ul>\n<p>Кроме того, у вендоров таких готовых решений почти никогда нет ресурсов и возможностей, чтобы подогнать его под специфические требования каждого клиента. Более того, чаще такие “кастомные” требования многократно превосходят стоимость самой коробки (в этом суть готового решения). Если что-то не так на бэке у юзера, если меняется воронка в CRM, если нужно настроить кастомные интеграции — велики шансы, что за это придется доплатить немаленькие деньги, если вендор вообще согласится реализовать эти “специфичные требования”.<\/p>\n<p>На одном из наших проектов нам довелось поработать именно с такой ситуацией: Roistat не смог обеспечить точность данных и интеграций со всеми нужными источниками, и с этим пришлось разбираться самой компании — и нам.<\/p>\n<h2>Как Refocus работал с аналитикой<\/h2>\n<p>Один из наших заказчиков,  который строил аналитику на самых ранних этапах — EdTech-стартап Refocus. Неудивительно, что когда мы пришли создавать для них кастомную систему, они уже работали с Roistat.<\/p>\n<p>Почему Refocus выбрали именно его? Во-первых, он доступен на русском языке, и это было удобно для команды, в которой много русскоязычных сотрудников. Во-вторых, им нужна была именно система сквозной аналитики, а не CRM с функцией автоматического построения графиков, поэтому альтернативы вроде HubSpot не подходили. В-третьих, все базовые потребности компании он удовлетворял за относительно небольшую сумму. Ну и в-четвертых — многие сотрудники из команды Refocus уже имели опыт работы с Roistat, так что для них это был удобный и знакомый инструмент.<\/p>\n<p>Roistat тянул данные из двух основных рекламных кабинетов — <b>Google и Facebook<\/b>*. В нем трекались все базовые метрики — бюджеты, показы, клики, конверсия — и отражались на автоматически созданных внутри программы дашбордах.<\/p>\n<p><img src=http:\/\/test.leftjoin.ru\/pictures\/unnamed-(2).png border=“0” width=100% height=100%><\/p>\n<p>К сожалению, скрины дашбордов из Roistat у нас не сохранились, но мы нашли для вас пример в интернете — выглядели они примерно так.<\/p>\n<p>Там даже присутствовал свой пользовательский идентификатор — когда мы вводили общий айди студентов, мы думали попробовать использовать его для мэтчинга персональных данных. На практике оказалось, что сопоставление по нему проходит очень неточно, и эту идею оставили.<\/p>\n<p>Стартап рос, число требований и людей увеличилось, поэтому нас позвали  строить для ребят кастомную аналитику и более подробные дашборды, и подключились мы.<\/p>\n<h2>Как обнаружилась проблема<\/h2>\n<p>Мы провели аудит существующей системы, получили доступы к нужным ресурсам и начали выстраивать инфраструктуру и готовить первый дашборд — маркетинговый, самый актуальный для компании на тот момент.<\/p>\n<p>Откуда мы тянули данные для этого дашборда? Конечно, из Roistat, ведь именно там уже лежали готовые маркетинговые показатели. Настройка хранилища и выгрузка данных из источников, которые Roistat не трекал, еще не была закончена, а смотреть на классные, качественные дашборды хотелось уже сейчас.<\/p>\n<p>Например, так выглядел код для выгрузки данных о затратах на рекламу через API:<\/p>\n<pre class=\"e2-text-code\"><code>def get_costs(channel, channel_level, date_from, date_to, source):\r\n         \r\n     data = '{&quot;dimensions&quot;: [&quot;marker_level_2&quot;, &quot;marker_level_3&quot;, &quot;marker_level_4&quot;],\\\r\n     &quot;metrics&quot;:[&quot;visitsCost&quot;, &quot;impressions&quot;, &quot;visits&quot;],\\\r\n     &quot;period&quot;:{&quot;from&quot;:&quot;' + date_from + '&quot;,&quot;to&quot;:&quot;' + date_to + '&quot;},\\\r\n     &quot;filters&quot;:[{&quot;field&quot;:&quot;' + channel_level + '&quot;, &quot;operation&quot;:&quot;=&quot;, &quot;value&quot;:&quot;' + channel + '&quot;}],\\\r\n     &quot;interval&quot;: &quot;1d&quot;}'\r\n     columns=['dimensions.marker_level_2.value',\r\n             'dimensions.marker_level_2.title',\r\n             'dimensions.marker_level_3.value',\r\n             'dimensions.marker_level_3.title',\r\n             'dimensions.marker_level_4.value',\r\n             'dimensions.marker_level_4.title',\r\n             'dateFrom'\r\n             ]\r\n     headers = {'content-type': 'application\/json'}\r\n\r\n     response = requests.request(&quot;POST&quot;, url, data=data, headers=headers)\r\n     df = response.json()['data']\r\n\r\n     l = []\r\n     for i in range(len(df)):\r\n         if df[i]['items'] != []:\r\n             l.append(df[i])\r\n     if len(l) &gt; 0:\r\n         res1 = pd.json_normalize(l, 'items', ['dateFrom'])[columns]\r\n         res2 = pd.DataFrame()\r\n         q = pd.json_normalize(l, ['items', 'metrics'])\r\n         for x in [&quot;visitsCost&quot;, &quot;impressions&quot;, &quot;visits&quot;]:\r\n             res2[x] = q.query('metric_name == @x')['value'].values\r\n         res = pd.concat([res1, res2], axis=1)\r\n         res['source'] = source\r\n         return res\r\n     else:\r\n         return pd.DataFrame()<\/code><\/pre><p>Подробнее о <tt>marker_levels<\/tt> можно прочитать <a href=\"https:\/\/help-en.roistat.com\/API\/methods\/analytics\/#sample-report\">здесь<\/a> в документации API Roistat.<\/p>\n<p>Когда Refocus посмотрели на первую версию дашборда, они заметили, что цифры не сходятся с их реальными бюджетами, кликами, конверсией в рекламных кабинетах.<\/p>\n<p>Мы начали искать ошибку с нашей стороны, но убедились, что наш код работает исправно и данные в нашем дашборде один в один совпадают с цифрами в Roistat. Никаких сбоев между источником и дашбордом не происходило.<\/p>\n<p>Дело в том, что пока компания смотрела только на визуализации Roistat, расхождения в данных не бросались в глаза. А наш дашборд содержал несколько новых графиков, более подробных, чем мог предложить Roistat. Когда Refocus стали сравнивать детали по разным срезам, на которые Roistat не позволял посмотреть, расхождения стали очевидны.<\/p>\n<p><img src=http:\/\/test.leftjoin.ru\/pictures\/unnamed-(1).png border=“0” width=100% height=100%><\/p>\n<p>У нас дашборд был более гибкий — можно было отфильтровать данные по разным важным срезам, более тонко настроить гранулярность. И благодаря этому ошибки в метриках стали заметны.<\/p>\n<p>Об этом мы и сообщили коллегам, продемонстрировав совпадение. Как раз в этот момент сыграл роль недостаток закрытой системы — посмотреть внутрь Roistat и найти, где именно происходит ошибка, не могли ни мы, ни бэкенд-специалисты Refocus.<\/p>\n<p>Разумеется, кастомная система уже предполагала отключение этого источника в будущем — зачем платный посредник, если мы все равно будем грузить данные напрямую и из рекламных кабинетов, и из кучи других источников?<\/p>\n<p>После обнаружения ошибки стало ясно, что затягивать с этим не стоит, если мы хотим предоставить клиенту чистые данные.<\/p>\n<p>Полностью отказаться от Roistat сразу не получилось, поэтому мы добавили на дашборд фильтр <tt>“match with Roistat”.<\/tt><\/p>\n<p>На нем мы отмечали количество рекламных кампаний, данные по которым расходятся с показателями на нашем дашборде и на Roistat’овском.<\/p>\n<h2>Как мы наконец заменили Roistat на кастомную систему и уточнили данные о воронках<\/h2>\n<p>Мы сразу написали несколько скриптов для выгрузки тех же самых данных из GA4 и Facebook Ads* через API.<br \/>\nНапример, так данные о рекламных показах, затратах и кликах  за день в разрезе {campaign, adset, adname} выгружались из кабинета GA4:<\/p>\n<pre class=\"e2-text-code\"><code>def get_google_costs(date_start, customer_id):\r\n\r\n    request = RunReportRequest(\r\n        property = f&quot;properties\/{property_id}&quot;,\r\n        dimensions = [Dimension(name = &quot;date&quot;),\r\n                     Dimension(name = &quot;sessionGoogleAdsCustomerId&quot;),\r\n                     Dimension(name = &quot;sessionGoogleAdsCampaignId&quot;),\r\n                     Dimension(name = &quot;sessionGoogleAdsCampaignName&quot;),\r\n                     Dimension(name = &quot;sessionGoogleAdsAdGroupId&quot;),\r\n                     Dimension(name = &quot;sessionGoogleAdsAdGroupName&quot;),\r\n                     Dimension(name = &quot;sessionGoogleAdsCreativeId&quot;)],\r\n        metrics = [Metric(name=&quot;advertiserAdCost&quot;),\r\n                   Metric(name=&quot;advertiserAdImpressions&quot;),\r\n                   Metric(name='advertiserAdClicks')],\r\n        date_ranges = [DateRange(start_date = date_start, end_date = date_start)],\r\n        dimension_filter = FilterExpression(\r\n            filter = Filter(\r\n                field_name = &quot;sessionGoogleAdsCustomerId&quot;,\r\n                string_filter = Filter.StringFilter(value = customer_id),\r\n            )\r\n        ),\r\n    )\r\n    response = client.run_report(request)\r\n\r\n    date = map(lambda x: x.dimension_values[0].value, response.rows)\r\n    customer_id = map(lambda x: int(x.dimension_values[1].value), response.rows)\r\n    campname_id = map(lambda x: int(x.dimension_values[2].value), response.rows)\r\n    campname = map(lambda x: x.dimension_values[3].value, response.rows)\r\n    groupname_id = map(lambda x: int(x.dimension_values[4].value), response.rows)\r\n    groupname = map(lambda x: x.dimension_values[5].value, response.rows)\r\n    adnam_id = map(lambda x: int(x.dimension_values[6].value), response.rows)\r\n    visitsCost = map(lambda x: float(x.metric_values[0].value), response.rows)\r\n    impressions = map(lambda x: int(x.metric_values[1].value), response.rows)\r\n    visits = map(lambda x: int(x.metric_values[2].value), response.rows)\r\n\r\n    df = pd.DataFrame({'region': customer_id, 'date': date, 'campname': campname, 'groupname': groupname, 'campname_id': campname_id, 'groupname_id': groupname_id, 'adnam_id': adnam_id, 'visitsCost': visitsCost, 'impressions': impressions, 'visits': visits})\r\n    return df\r\n\r\ndf = pd.DataFrame()<\/code><\/pre><p>После этого обнаружилось еще одно несовпадение: метрики в самом кабинете были чуть меньше, чем у нас на дашбордах. Отклонение не было критичным, но клиент снова не был уверен в точности наших данных.<\/p>\n<p>Оказалось, что в GA4 по умолчанию включается настройка <b>Thresholding applied.<\/b> Ее цель — предотвратить возможную идентификацию отдельных пользователей по данным о возрасте, гендере и интересах. В результате ее применения терялись данные о кампаниях с небольшим (меньше 50) количеством кликов. Чтобы этого избежать, нужно было или не трекать эти показатели, или не использовать в дашбордах метрики, касающиеся пользователей напрямую. Roistat тоже не выгружал эти “пограничные” кампании через свою дефолтную интеграцию с GA4, поэтому для Refocus расхождение стало неожиданностью. Зато при прямой выгрузке через API этого ограничения не было, и ускользнувшие поначалу кампании считались, так что данные были более полными без дополнительных ухищрений.<\/p>\n<p>С добавлением все новых и новых источников, конечно, пришлось еще и мэтчить данные рекламных кабинетов о затратах, кликах и показах с записями на вебинары и их посещением, со сделками в CRM — но Roistat этого вовсе не делал, и поэтому не мог дать полной картины о воронках.<\/p>\n<p>После введения прямой выгрузки из рекламных кабинетов данные наконец-то стали полностью совпадать с реальностью, и Roistat отключили, заменив другими источниками. Теперь Refocus могли не платить дважды и получить куда более точный анализ.<\/p>\n<h2>Выводы<\/h2>\n<p>Кастомная сквозная аналитика — конечно, не панацея, и есть много причин и возможностей обойтись без услуг агентств, которые ее строят. Тем не менее, даже самое популярное на рынке коробочное решение, очевидно, не всегда корректно работает. Обрубать его сразу не обязательно, и ничего не мешает сочетать Roistat и похожие продукты с кастомом. В конце концов, иногда удобно просто достроить интеграции, которые не поддерживает Roistat, и держать дашборды в нем. Но в этом случае важно понимать, что данные там могут быть неполными или некорректными, а сообщение с бэком компании — ненадежным. В результате обнаружить или исправить ошибки может быть сложнее, чем организовать прямую выгрузку данных в хранилище — и здесь кастомная система отлично справится.<\/p>\n<p><i>*Facebook принадлежит компании Meta, которая признана экстремистской организацией в России.<\/i><\/p>\n",
            "date_published": "2024-10-02T15:25:45+03:00",
            "date_modified": "2024-10-02T14:55:37+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/unnamed-(2).png",
            "_date_published_rfc2822": "Wed, 02 Oct 2024 15:25:45 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "149",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-(2).png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-(1).png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-1.png"
                ]
            }
        }
    ],
    "_e2_version": 3365,
    "_e2_ua_string": "E2 (v3365; Aegea)"
}