Русский
Русский
English
Статистика
Реклама

Analytics

Как устроены технические стажировки Авито

23.12.2020 14:04:12 | Автор: admin

Несколько раз вгод мынабираем стажёров втехнический департамент Авито. Они работают отшести месяцев догода ирешают боевые задачи. Поитогам большинство стажёров переходят вштат.


Нас зовут Станислав Юрков иИрина Мулёва, мынабираем стажёров ипомогаем имвпроцессе обучения. Встатье мырасскажем, как устроены технические стажировки иответим начастые вопросы кандидатов.



Накакие направления мынанимаем стажёров


Авито берёт стажёров понаправлениям Frontend, Backend, Mobile под iOS иAndroid, атакже вQuality Assurance. В2020-2021 году больше всего внимания мыпланируем уделить направлениям Backend иFrontend, так что расскажите онас заинтересованным друзьям.


Унас уже открыты несколько стажёрских позиций, нокаждый месяц будут добавляться новые. Все вакансии опубликованы наотдельной странице.


Кроме разработчиков, мыпериодически ищем стажёров-аналитиков понаправлениямBI иData Science. Оних вэтой статье мырассказывать небудем, ноактуальные позиции тоже будут появляются пообщей ссылке выше.


Условия стажировки


Сейчас мысобеседуем иадаптируем новых сотрудников удалённо. Работаем тоже издома. За2020год, несмотря накоронавирус, мынаняли 11стажёров втехнический департамент иуже перевели вштат некоторых изних. Иногда мыустраиваем встречи вофисе небольшим составом, чтобы улюдей была возможность познакомиться лично, ноосновная коммуникация происходит онлайн.



Наша стажировка будет интересна старшекурсникам инедавним выпускникам технических вузов, которые хотят прокачать свои навыки иполучить опыт работы ввысоконагруженном проекте.


Укаждого стажёра есть персональный наставник инженер Авито. Этот человек ставит задачи, помогает сихрешением, обучает новому ипринимает результат работы. Наставник всегда изтойже функции, что истажёр, тоесть если выпришли вофронтенд, вам будет помогать опытный фронтендер.


Спервого дня стажёры включены вкомандные процессы. Мыдаём боевые задачи инебоимся передавать ответственность. Сейчас сайтом иприложением Авито пользуются более 50миллионов человек каждый месяц, так что наши стажёры всегда могут справедливо похвастаться, что приложили руку кработе по-настоящему большого сервиса.


Стажировка длится отшести месяцев догода. Понашим наблюдениям, это оптимальный срок для того, чтобы разобраться винструментах, вникнуть взадачи ираскрыть свой потенциал. Стажёры работают 20часов внеделю погибкому графику работать можно иднём, ивечером, главное заранее договориться ографике сосвоим наставником.


Разработка вбольшинстве команд ведётся поScrum, поэтому есть ежедневные общие встречи для синхронизации позадачам, атакже встречи попланированию игрумингу задач, демо иретроспективы. Стажёру повозможности стоит участвовать вних, чтобы лучше адаптироваться вкоманде иглубже погрузиться впроцессы разработки.


Техническая стажировка оплачивается. Мыплатим 40000 рублей довычета налога это 34800 рублей накарту вмесяц. Устажёров других направлений размеры оплаты отличаются, поэтому уточняйте ихнасобеседовании.


Стажировка записывается втрудовую книжку, аесли стажёр переходит вштат наполный рабочий день, мызасчитываем еёзаиспытательный срок. Для того, чтобы стажёр стал штатным сотрудников, должны совпасть сразу несколько факторов: уровень знаний икомпетенций, возможность работать полный день, наличие открытой вакансии ибюджета нанового сотрудника.


Мыхотим, чтобы как можно больше стажёров становились сотрудниками Авито иделаем для этого всё возможное. Ноесли обстоятельства несовпадают, мыготовы дать рекомендацию иотзыв для будущего работодателя ибудем рады вернуться кобщению вбудущем.


Как попасть настажировку


Когда появляются вакансии для стажёров, мыпубликуем ихнаспециальном лендинге. Кандидату нужно выбрать интересующую позицию, выполнить тестовое задание изаполнить форму заявки.


Студенты 3-4 курса технических специальностей обычно справляются снашими отборочными заданиями: для них хватает базовых знаний впрограммировании иумения искать информацию. Недавним выпускникам икандидатам сдругим бэкграундом тоже можно откликаться настажировки.


Посмотреть примеры тестовых можно внашем аккаунте наГитхабе:



Мыпроверяем выполненные задания вместе синженерами иприглашаем понравившихся кандидатов наонлайн-интервью. Интервью проходит вдва этапа. Напервом мыпроверяем технические знания кандидата, задаём вопросы потеории идаём задачи. Это занимает час-полтора. Кандидатов, которые успешно проходят эту секцию, мыприглашаем наHR-интервью стимлидом, рекрутером направления икуратором стажировки. Здесь разговор будет посвящен целям иинтересам кандидата впрофессиональном развитии. Кандидатов, скоторыми мысовпадаем повзглядам иожиданиям, мыприглашаем выйти настажировку.


Минутка статистики


Мыпосчитали цифры постажировке вкоманды, которые создают продукты для частных пользователей, одного изнаборов 2019года. Среди кандидатов было много хороших начинающих специалистов, которых мынесмогли взять, так как количество вакансий ограничено, номынадеемся, что унас ещё появится возможность поработать вместе вбудущем. Также невсем стажёрам подошёл график идругие условия это стоит учитывать при подсчёте своих шансов.


  • Заявки свыполненными тестовыми заданиями: 150.
  • Собеседования: 50.
  • Выбрали стажёров втехнологические команды: 7.

Большинство стажёров изпрошлого набора уже работают вштате джуниор-разработчиками.


Что говорят стажёры опрограмме


image


Аделина Загитова, QAиbackend-стажёр, перешла вштат

Яискала хорошую подработку попрофессии, где можно набраться опыта параллельно сучёбой. Кроме Авито рассматривала Яндекс, Хоум Кредит Банк, Лабораторию Касперского, Wildberries. Выбрала Авито, потому что сомной провели отлично продуманное собеседование ипредложили самые удобные условия работы.

Стажировка дала мне много:
  • яузнала про скрам инаучилась ему;
  • поднатаскала всебе командные ценности;
  • научилась говорить осложностях, проблемах ирешать ихнеодна;
  • прокачалась втестировании, это отличный поинт для будущего разработчика;
  • узнала, что значит работать вбольшом проекте инаучилась этому;
  • больше узнала обАндроиде иего инструментах разработки теперь стало понятно, как работает большое сложное приложение;
  • стала уверенней всебе.


Ястажировалась сначала вQA, затем перешла настажировку поАндроиду. Стажировка была классной иводной, ивдругой функции. Тестирование неотъемлемая часть разработки конечного продукта, которая нужна каждому разработчику. Янемного знала отестировании, побольшей части теорию. Ноблагодаря своей наставнице смогла прокачаться вэтом напрактике, что мне сильно помогает сейчас.

Начетвёртом курсе, пока шла вторая часть моей стажировки, вуниверситете был курс поАндроиду. Втот момент мне стало интересно заниматься именно разработкой под Андроид, иколлеги поддержали меня. Так яперешла насвою вторую стажировку вАвито, при этом ятоже делала тестовое задание ипроходила собеседование. Навторой стажировке яначала прокачивать свою вовлечённость впродукт, потому что работала непосредственно сприложением Авито.

Вобщем, стажировки были абсолютно разными, иобе дали мне много всего положительного, что помогает мне сейчас как разработчику.



image


Владимир Нюхтилин, iOS-стажёр, перешёл вштат

Ясчитаю, что возможностью попасть втакую компанию как Авито, точно нужно пользоваться. Программа стажировки как раз про это.

Мне было важно получить как можно больше боевого опыта, найти осознанные ответы навопросы изразряда апочему делают именно так ичто действительно важно, ачем вреальности можно пренебречь. Безусловно, понимание устройства рабочего процесса большой компании дело неодного дня идаже месяца. Нопостепенно укаждого нового сотрудника находятся ответы натесамые вопросы, и, безусловно, этому способствует внутренняя атмосфера, которая царит вАвито.
Новичку здесь готов помочь каждый. Астажёра ещё инаставляет опытный ментор, который нетолько объясняет тонкости рабочего процесса, ноипомогает тебе развиваться как специалисту.

Помоим наблюдениям, программа стажировок отлажена на150% благодаря многим тонкостям, иона постоянно развивается, как ивсё здесь. Яполучил большой опыт вАвито ипродолжаю работать над собой, решая задачи, которые стоят вцелях моей команды. Поэтому навопрос Что именно дала мне стажировка вАвито? ябы ответил так: Возможность для отличного, если нелучшего, старта карьеры вIT.



image


Никита Васильев, backend-стажёр, перешёл вштат

Намой взгляд, формат длительной стажировки наполовину рабочего дня оптимален, поскольку позволяет погрузиться вработу ихорошо подходит для совмещения сучебой ввузе. Втечение этого времени тыуспеваешь понять как логику своих задач, так иособенности взаимодействия сдругими людьми впроцессе ихрешения, получаешь все инфраструктурные навыки для работы, параллельно постепенно подтягивая свои отстающие скиллы.

ВАвито япервый раз столкнулся сбольшим проектом, который работает свысокими нагрузками иимеет несколько деплоев каждый день. Наменя вылилось огромное количество новой информации, которую пришлось укладывать вголове. Стажировка отличный шанс узнать инарисовать для себя полную картину того, как должен быть построен процесс разработки.



image


Артём Ольков, стажёр вSecurity, перешёл вштат

Стажировка представляет большую ценность, впервую очередь для тех, кто только начинает карьерный путь вIT. Входе стажировки приобретаетсято, что нельзя получить вне реальных проектов: опыт иумение работать вкоманде.

Когда ятолько пришёл настажёрскую программу, тоожидал, что смогу поработать над интересными задачами вдружной команде, повысить уровень нетолько hard, ноиsoft skills, окунуться вжизнь крупной, высокотехнологичной компании. Нопрограмма превзошла даже мои, как ядумал, завышенные ожидания. ВАвито яосвоил новые технологии, которыми никогда непользовался, познакомился смножеством увлечённых своим делом людей исформировал для себя дальнейший вектор развития.



image


Константин Голик, frontend-стажёр, перешёл вштат

Одна изглавных проблем студента-программиста это оторванность отпромышленной разработки. Тыпишешь сложные программы, нопри этом неимеешь нималейшего понятия отом, чем будешь заниматься наработе. Вэтом, как помне, иесть вся ценность стажировки вАвито. Она позволяет плавно погрузиться вкоммерческую разработку истать частью команды. Все мои ожидания отстажировки оправдались, янабил руку насвоем стеке технологий ипонял все процессы разработки. Особенно круто, что более опытные коллеги всегда готовы помочь свозникающими вопросами.



Что говорят наставники


image


Ионов Владимир, Teamlead

Как только мы узнали остартовавшей вАвито стажёрской программе, сразу захотели взять себе вкоманду стажёра-фронтендера. Причин былодве. Во-первых, внашей команде хорошая экспертность попроведению технических интервью. Во-вторых, наш Senior Frontend разработчик хотел попробовать себя вроли наставника. И мы верили, что если он передаст хотя бы часть своего опыта стажёру завремя стажировки, то это будет очень ценное приобретение дляюнита.

Мы разработали тестовое задание, разместили его насайте и стали ждать. Уже через полторы недели унас было более 20тестовых работ отжелающих попасть настажировку. Сюрпризом стало то, что ребята выбрали разные фреймворки, поэтому назапуск, разбор и оценку выполненных проектов мы потратили больше времени, чем планировали. Уже после трёх склонированных репозиториев мы начали составлять рейтинг проектов, чтобы пригласить авторов лучших работ вофис наинтервью.

Первое же интервью показало, что мы сильно недооценили современных студентов. Кандидат оказался очень хорошо подкован теоретически и смог решить простые задачи закороткое время. Чтобы нащупать границу познаний вweb-разработке, нам пришлось дать ему несколько задач, которые мы решаем скандидатами на вакансии junior/middle. Кнашему удивлению, снекоторыми из них он справился. После этого мы пересмотрели план интервью, включив внего задачи и теоретические вопросы разной степени сложности.

По итогу шести встреч скандидатами в стажёры мы пришли квыводу, что большинство откликнувшихся навакансию имеют очень крепкие теоретические знания. Но своего стажёра мы ктому моменту уже выбрали. Он продемонстрировал очень крепкую теоретическую базу, хорошо знал браузерное API и быстро писал код. Ну и конечно же, горящие глаза выделяли его среди других. Уже навторой неделе работы вкоманде стажёр сверстал свой первый лендинг, который доступен и сейчас.

Параллельно сотбором кандидатов вофронтенд, нам предложили посмотреть iOS-стажёра, который уже прошёл все технические этапы отбора вдругой команде. Напервой же очной встрече стало ясно, что он подходит нам подуху: кандидат быстро нашёл общий язык снаставником и продемонстрировал сильное желание развиваться вразработке iOS-приложений. В итоге мы сделали ему предложение пройти стажировку внашей команде.

Забавно совпало, что стажёры вышли водин день, а внашей команде появилось сразу четыре новых роли: два стажёра и два наставника.

Сейчас, спустя полгода стажировки, стажёры выполняют продуктовые задачи вместе скомандой. Технический уровень ребят существенно вырос, а коммуникационные навыки позволяют им решать все рабочие вопросы самостоятельно, практически безпомощи наставников. Мы очень довольны своим выбором и готовим ребят квыходу вштат наполную ставку.



image


Костас Кряров, Senior Frontend Engineer

Ещё дотого, как я взял стажёра, уменя были неверные ожидания. Мне казалось, что это довольно просто надо показывать, как делать задачи. Насамом деле всё оказалось намного сложнее и интереснее. Во-первых, нужно грамотно и чётко формулировать задачи спояснениями, почему эта задача важна, чему стажёр научится, выполнив её. Во-вторых, нужно было постоянно работать смотивацией стажёра, чтобы задачи были интересными и соответствовали его уровню.

Наставничество научило меня отцовскому духу. Я очень хотел, чтобы стажёру понравилось унас в команде и компании вцелом, чтобы комфортно работалось.

Мы даём стажёрам разные задачи. Сначала довольно простые, чтобы человек пощупал наши процессы, code-review и прочее. С развитием стажёра, задачи становятся сложнее:
  1. Вёрстка лендинга снуля.
  2. Рефакторинг тестов, чтобы стажёр научился работать сjest и нашими внутренними библиотеками, связанными стестированием.
  3. Интеграция новой функциональности вReact-Redux приложение.
  4. Умоего стажёра была интересная задача наисследование рендеринга графа статусов наsvg или canvas, учитывая положение рёбер и вершин графа.



image


Артём Пескишев, Senior Mobile Engineer

Для наставника работа состажёром это отличный способ прокачать свои управленческие навыки. Если унаставника доэтого не было менеджерского опыта, то достаточно интересно оказаться по тусторону баррикад. Приходится учится тому, как правильно работать смотивацией подчинённых, спроцессом обучения и интеграции нового, как правило безопыта работы, сотрудника вкоманду и её процессы.

Впервое время я давал достаточно простые гигиенические задачи, и не ограничивал стажёра повремени. Как правило, это были баги, которые позволяли новому сотруднику поковыряться впроекте, изучить разные модули, понять, как устроен код и как сним работать.

Потом я дал более интересную задачу встроить интерактивную заглушку дляаватарки наэкране контактных данных. Она позволила стажёру познакомится с механизмом А/Б тестирования и научится тому, как создавать новые модули, закрывать свой код фича тоглами. Стажёр витоге разработал фичу, которой пользуются тысячи людей насайте и которую он может показать друзьям, использующим Авито. Когда я увидел, что задачи решаются вустановленные сроки, стал давать те, что напрямую влияют нацели компании, например, редизайн экранов размещения объявления.

Мой стажёр быстро вник впроект, и спустя пару месяцев уже был полноценным участником команды, который работал надзадачами наравне совсеми, планировал свою работу и выполнял её взаданные сроки.



Вместо вывода


Если увас остались вопросы потехническим стажировкам вАвито задавайте ихвкомментариях кстатье.

Подробнее..

Стоимость строительных работ в Сан-Франциско. Инфляция и рост стоимости работ за последние 30 лет

04.08.2020 14:20:22 | Автор: admin
Данные о более чем миллионе разрешений на строительство (записей в двух датасетах) от департамента по строительству Сан-Франциско позволяют проанализировать не только строительную активность в городе, но и критически рассмотреть последнии тенденции и историю развития строительной отрасли за последние 30 лет.

В прошлой статье была рассмотрена общая годовая сумма строительных объемов (инвестиций) в Сан-Франциско в период с 1980 по 2018 год. По разнице между ожидаемой (сметной) и фактической (пересмотренной) стоимостью строительства отслеживались движения настроений инвесторов в периоды экономических бумов и кризисов в регионе.

Взлёты и падения строительной отрасли Сан-Франциско. Тенденции и история развития строительной активности

В данной статье рассмотрим подробнее отдельные отрасли строительства: ремонт крыш, кухонь, лестниц и ванных комнат. После этого сравним инфляцию по отдельным типам работ с данными по официальной инфляции и другими экономическими показателями.

Выводы, разобранные в этой статье


  • Инфляция в строительстве в два раза больше официальной инфляции в стране.
  • Спрос на различные типы жилья год от года меняется и зависит от различных экономических циклов.
  • Экспоненциальный спрос на строительства апартаментов (Apartments) пришелся на последний Хай-тек бум (2016 год)
  • Пик спроса на торговые площади (Retail) и увеличение объемов строительства более чем в 16 раз пришёлся на пик Доткомов (2001 год).
  • Стоимость ремонта крыши и кухни увеличилась за последние 30 лет более чем в 3 раза.
  • Стоимость ремонта ванной комнаты увеличилась за период с 1980 по 2019 год в 5 раза.
  • Бизнес по строительству лестниц почти не повышает цены на ремонт уже почти 30 лет.
  • Цена планового ремонта по 4 категориям (крыша, ванна, кухня, лестница) у однофамильного дома и у двухфамильного дома отличается на 15%.
  • Для общего ремонта по 4 категориям каждые 15-20 лет стоимость ремонта кухни, ванной, крыши и лестницы, в одно-фамильном доме составит примерно $ 54 000 тогда как для двух-фамильного дома эта сумма составит $ 61 000.
  • Бизнес связанный со строительством домов в Сан-Франциско показывает плавный устойчивый рост без колебаний уже 30 лет.
  • Бизнес связанный со строительством торговых площадей, офисов и апартаментов волатильный и имеет многочисленные экспоненциальные взлеты и падения.
  • Если наблюдать многократный рост в течении короткого времени, в следующие два года можно ожидать такого же по интенсивности стремительного падения.
  • Если нужно узнать на сколько поднимется средняя цена на ремонт, следи за стоимостью ставки по 10 летним государственным облигациям.

Построение графиков и расчётов проводилось в Jupyter Notebook (на платформе Kaggle.com).

Взлеты и падения строительной отрасли Сан-Франциско по типу жилья


Технологическая индустрия Сан Франциско является одним из виновников резкого роста цен. Технические работники, зарабатывающие шестизначные цифры, переезжают в город, чтобы работать в стартапах и в более авторитетных компаниях, таких как Google, Facebook, Twitter и Apple резко увеличивают стоимость жизни и увеличивают спрос на жилье. Программисты с карманами, набитыми техническими деньгами, могут позволить себе перекупить большинство местных жителей на рынке недвижимости.

При этом, с точки зрения законодательства в сфере градостроительства Сан-Франциско является одним из наиболее регулируемых городов в Америке. Эти и многие другие факторы сильно ограничивают объемы строительства и влияет на цены и спрос в Сан-Франциско.

Поэтому спрос на различные типы жилья год от года меняется и зависит от различных экономических циклов в регионе. Каждый тип жилья имел и будет иметь свои взлеты и падения на рынке строительства в Сан-Франциско, как например взрыв спроса на апартаменты с 2012 по 2015 год почти в 10 раз, или хайповый, более чем в 16 раз рост спроса с 1997 по 1999 год на торговые площади.

В статье о годовой общей сумме строительных работ, график движения за период с 1980 по 2018 год разделялся на две составляющие:

  • Предположительная (сметная) стоимость работ (синяя линия)
  • Фактическая (пересмотренная) стоимость работ (желтая линия)



Рассмотрим суммарные данные по общей стоимости глубже и перейдём к следующему слою данных.

Общую суммарную стоимость работ разобьем на категории работ по параметру тип жилья (Existing Use):

  • Apartaments (апартаменты)
  • Retail (торговые площади)
  • Office (офисные пространства и бюро)
  • Жилая недвижимость (одно- двух-семейные дома)

dfn = df.dropna(subset=['description'])dfn.description.isnull().values.any()#dfn = dfn[dfn['description'].str.match('kitchen')]df_unit = dfn.loc[:,['revised_cost','existing_use', 'existing_units', 'zipcode','permit_creation_date']]df_unit = df_unit.dropna()#keys = ["hotel","appartments"]df_unit = df_unit[df_unit.existing_use.str.contains("apartments")]#data_loc = df_unit.loc[['estimated_cost', 'revised_cost','permit_creation_date']]data_cost = df_unit data_cost.permit_creation_date = pd.to_datetime(data_cost.permit_creation_date)data_cost = data_cost.set_index('permit_creation_date')data_cost = data_cost[data_cost.index > "1985-8-01"] data_cost = data_cost[data_cost.index < "2019-8-31"] data_cost = data_cost.dropna()data_cost_m = data_cost.groupby(pd.Grouper(freq='300d')).sum()#data_cost_m.head()plt.figure(figsize=(19,8))ax = sns.lineplot(data=data_cost_m.revised_cost, linewidth=3, size = 17)ax.set(xlabel='retail')major_ticks = np.arange(0, 1500000000, 200000000)ax.set_yticks(major_ticks)ax.set(ylim=(0, 1500000000))plt.savefig('plotname.png', transparent=True)



Видно что все типы недвижимости в разные экономические периоды проходили через стремительный параболический (хайповый) рост и такое же стремительное падение.

  1. Пик строительства апартаментов (Apartments) пришелся на последний Хай-тек бум, который был связан с притоком большого количества новых технических работников со всего мира в быстрорастущие фирмы силиконовой долины. Спрос на апартаменты с 2012 по 2015 увеличился в 10 раз. c $ 133 млн в 2012 году до $ 1,4 млрд. инвестированных только в апартаменты в 2015 году.
  2. Пик спроса на торговые площади (Retail) в свою очередь пришёлся на хайп Доткомов. Спрос на торговые площади с 1997 по 1999 увеличился в 16 раз. c $ 22 млн в 1997 до $ 350 млн. инвестированных только в торговые площади в 1999 году. Но схлопывание пузыря привело к исходу технических работников из города и спрос на торговые площади резко упал, и вернулся к стандартному уровню. При этом последний технический бум никак не повлиял на спрос торговых площадей и скорее уже построенные в конце 90-х лишние метры удовлетворяют современный спрос на торговые площади.
  3. Рост спроса на офисную недвижимости (Office) также связан с развитием гигантов силиконовой долины. Но здесь начиная с 2000 года, в отличии от торговой недвижимости и апартаментов, прослеживается устойчивый рост спроса который сопровождается многочисленными, небольшими взлетами и падениями.
  4. Рост инвестиций в жилую недвижимость совпадает по динамики роста с офисной недвижимостью, но отличается от роста офисных площадей плавностью роста и отсутствием больших колебаний спроса.

Если соединить эти 4 основные категории в один график, получаем знакомый по первой статье общий рост и падения всех инвестиций в строительство в городе Сан-Франциско.



Средняя стоимость ремонта кухни и ванны в Сан-Франциско


Взяв данные из характеристики (Feature) Description, мы можем дополнительно отобрать данные по отдельным категориям работ и посмотреть, сколько в среднем стоит ремонт кухни или ванны в Сан-Франциско для различных типов жилья.

fam1 = df_unit[df_unit['existing_use']=='1 family dwelling']['estimated_cost'].mean()fam2 = df_unit[df_unit['existing_use']=='2 family dwelling']['estimated_cost'].mean()office = df_unit[df_unit['existing_use']=='office']['estimated_cost'].mean()apartments = df_unit[df_unit['existing_use']=='apartments']['estimated_cost'].mean()data = {'1 family dwelling':fam1,'2 family dwelling':fam2,'Apartments':apartments}typedf = pd.DataFrame(data = data,index=['redevelopment of the bathroom'])typedf.plot(kind='barh', title="Average estimated cost by type", figsize=(8,6));



Стоимость ремонта кухни в Сан-Франциско почти в два раза больше чем стоимость ремонта ванной комнаты. Логично при этом что средняя стоимость ремонта ванной комнаты на $2 000 больше для двух-фамильного дома ($16 000) чем для одно-фамильного дома ($14 000).

Но при этом средняя стоимость ремонта кухни для двух-фамильного дома ($25 000) почти на 3000$ меньше чем для одно-фамильного дома ($28 000).

Средняя стоимость ремонта крыши и лестницы в Сан-Франциско


По той же характеристике (Feature) Description, отберем только те строки, которые содержат слова reroofing (перекладка крыши) и stairs (ремонт лестницы).



По средней стоимости ремонта крыши, логично что ремонт крыши (из за большей площади крыши у двух-фамильных домов) в среднем на 2000$ больше чем у одно-фамильных домов.

Стоимость ремонта лестницы также в два раза больше для двух-фамильного дома, потому что в одно-фамильном доме лестница или отсутствует (или это только однопролетная лестница).

Стоимость планового ремонта дома в Сан-Франциско


Ремонт кухни, ванной в среднем рекомендуется проводить один раз в 10-15 лет. Ремонт крыши и лестницы один раз в 15-20 лет.



В общем если теоретически через 15 лет после строительства дома произвести ремонт кухни, ванной, крыши и лестницы за один год, то в одно-фамильном доме вам нужно будет накопить для этого $54 000 тогда как для двух-фамильного дома эта сумма составит $61 000. Разница в общей стоимости работ по этим четырем категориям составляет всего 15%.
Таким образом после строительства нового дома, для того чтобы произвести ремонт в доме по четырем категориям (кухня, ванна, крыша, лестница), необходимо ежемесячно откладывать по $350, чтобы через 15 лет накопить необходимые $60 000 для ремонта.

Рост стоимости строительных работ в Сан-Франциско


Взяв данные по категориям работ, и сгруппировав их по годам, можно наблюдать за ростом (и инфляцией) средней стоимости ремонтных работ по типу жилья.

years = list(range(1980, 2020)) keywords = ['1 family dwelling','2 family dwelling','apartments']val_data = []for year in years:    iss_data = []    for word in keywords:        v = df_unit[(df_unit['existing_use']==word) & (df_unit['issued_date']== year)]['estimated_cost'].mean()        iss_data.append(v)    val_data.append(iss_data)#print(val_data)

В следующем графике данные о средней стоимости по типу жилья представлен, как и в предыдущем параграфе, в столбчатом виде.



Этот же график, для наглядного отображения но уже в виде линий, даёт уже более понятную инфляционную картину.

dfnew.plot.bar(figsize=(20, 8)) plt.xlabel("Years")plt.ylabel("Estimated cost of reroofing")plt.title("Estimated cost of reroofing by year");dfnew.plot.line(figsize=(12, 6))



Среднюю же стоимость ремонта крыши характеризует плавный рост начиная с 1990 года.
В отличии от жилых домов средняя стоимость ремонта крыши апартаментов в этот же период проходила многочисленные подъемы и падения.


В стоимости ремонта крыш апартаментов прослеживаются краткосрочные 3 летние циклы.

В отличии от ровной динамики роста средней стоимости ремонта крыш, средняя стоимость ремонт кухонь имеет бОльшую волатильность.



В ремонте кухонь, так же как и в ремонте крыш апартаментов прослеживаются краткосрочные 2x-3-x летние циклы.

В ремонте же ванных комнат уже таких циклов не прослеживается и рост средней стоимости строительства здесь более пологий. Выделяется только повышенная средняя стоимость ремонта ванн в апартаментах перед бумом доткомов?!



Инфляция стоимости строительных работ в Сан-Франциско.


Для того чтобы найти инфляцию средней стоимости ремонта за весь период с 1980 по 2019 год дополним данные трендовой линией. При вычислении инфляции (взяв начальную и конечную точку трендовой линии) получаем, что максимальная инфляция в стоимости за период с 1990 по 2018 год произошла в сфере ванных комнат.

Средняя стоимость ремонта ванной комнаты за последние 30 лет возросла почти в 5 раз (возможно стоимость ремонта выросла из за появления появившихся на рынке новых отделочных материалов и дорогой (и доступной) керамике и сантехники?!).

sns.regplot(y=dfnew_2['2 family dwelling'],x=dfnew_2['index'],data=dfnew_2, fit_reg=True) #sns.jointplot(dfnew_2['index'], dfnew_2['2 family dwelling'], data=dfnew_2, fit_reg=True, stat_func=stats.pearsonr)lines = plt.gca().lineslower1990 = [line.get_ydata().min() for line in lines]upper2019 = [line.get_ydata().max() for line in lines]plt.scatter(1990, lower1990, marker='x', color='C3', zorder=3)plt.scatter(2019, upper2019, marker='x', color='C3', zorder=3)print("In 1990 it cost = $" + str(lower1990[0].round()) + "; In 2019 it cost = $ " + str(upper2019[0].round()))print("Inflation for the period 1980-2019 = " + str(((upper2019[0]-lower1990[0])/lower1990[0]*100).round())+"%")all2 = [line.get_ydata() for line in lines]



Меньше всего отклонений по значениям в категории ремонт крыш, где за последние 30 лет инфляция составила 250% (средняя цена увеличилась более чем в 3 раза). Стоимость ремонта кухни также увеличилась за последние 30 лет в 3 раза.
В тот же самый период, стоимость ремонта лестницы с 1980 по 2019 год почти не изменилась и инфляция средней стоимости в этой сфере строительства составила лишь 85%.


Представим теперь развитие роста инфляции для большей наглядности в едином масштабе, где инфляция варьируется от 0 до 9% и посмотрим на падение ежегодной инфляции стоимости ремонта по категориям в период с 1980 по 2019 год.



Заметно что ежегодная инфляция за последние 30 лет снизилась во всех категориях почти в 2-4 раз (например в ремонте крыш с 8% в 1990 до почти 2% в 2019 году). Это полностью совпадает с экономической политикой в этот период (с 1980 по 2019 год).
Если сравнить официальные данные по инфляции и данные по инфляции в строительном секторе будет видно что только в одном секторе официальная инфляция совпала с инфляцией стоимости работ.


Рост стоимости работ по ремонту лестниц полностью совпадал с официальной инфляцией. В остальных же категориях работ ежегодный рост стоимости строительных работ за последние 30 лет опережал официальную инфляцию почти в 2 раза.

Движение же инфляции по таким категориям как ремонт крыши, ремонт ванных комнат и кухонь, почти полность совпадало с движением процентной ставки по 30 летним кредитам (и соответственно с доходности 10 летних казначейских облигаций).


30-летняя фиксированная ипотека это кредит, процентная ставка которого остается неизменной на протяжении всего срока кредита.
Например, при 30-летней ипотеке в размере 300 000 долларов США с 20% первоначальным взносом и процентной ставкой 3,75% ежемесячные выплаты составят около 1111 долларов США (без учета налогов и страхования). Таким образом, процентная ставка 3,75% (и ежемесячный платеж) остаются неизменными на протяжении всего срока кредита.
10-летний казначейский курс это доход, полученный за инвестиции в выпущенные правительством США казначейские ценные бумаги со сроком погашения 10 лет.

Инфляция в строительстве


Можно видеть, что изменения в стоимости работ совпадает с процентной ставкой по государственным облигациям. График Пола Шмельцинга (профессора Гарвардского университета) показывает, как изменялись глобальные реальные процентные ставки в течение последних восьми веков.



Собирая данные по реальным процентным ставкам в странах с развитой экономикой, Шмельцинг показывает, что реальные показатели показывают отрицательный тренд процентной ставки с 14 века.

Для сравнения период, который рассматривался в статье обозначен желтым цветом.



Начиная с 1311 года, данные из отчета показывают, как средние реальные ставки изменились с 5,1% в 1300-х годах до среднего 2% в 1900-х годах.

Средняя реальная ставка в период 2000-2018 годов составляет 1,3%.


Вместе с реальной ставкой, конечно же снижается и доходность отраслей, которая коррелирует с этой ставкой. Это в первую очередь такие древние отрасли, как аграрная промышленность и строительная отрасль.
Скорее всего в период с 2020 по 2030 год мы увидим новые рекордные минимумы реальных ставок и соответственно снижение доходности в строительной отрасли. Но если доходность снижается, возможно это означает, что производительность будет увеличиваться на эти же недостающие проценты.
Если раньше в строительстве была большая маржа в 10-15%, и компаниям не нужно было задумываться о внедрении новых технологий (которых в принципе было немного), то теперь мы вступаем в новую эпоху низких реальных ставок и низкой маржи в 2-5%, где основную роль в строительной компании будет играть наличие новых инструментов и процессов в работе компании.
Инструментов и новых технологий, которые могут использоваться уже сейчас в строительстве в данный момент в переизбытке.
Строительным компаниям понадобятся десятилетия, для того чтобы эти новые технологии нашли своё место в тяжелоповоротливой и сопротивляющейся строительной отрасли .
Примерно в то же время, когда в Москве начнут работать беспилотные такси российские строительные компании начнут постепенно заменять планировщиков на нижних уровнях автоматизироваными скриптами и инструментами, используюшие технологии больших данных и машинного обучения.

Ссылки на предыдущие публикации по этой теме:


Ссылка на Jupyter Notebook: San Francisco. Building sector 1980-2019.

Если вам нравится мой контент, пожалуйста, подумайте о покупке мне кофе.

Спасибо за вашу поддержку! Купить кофе автору
Подробнее..

Из песочницы Создание системы антифрода в такси с нуля

28.07.2020 10:08:24 | Автор: admin

Добрый день. Меня зовут Никита Башун, работаю дата-аналитиком в группе компаний Везёт. Мой рассказ будет о том, как мы командой из трёх человек с нуля создавали систему антифрода для сервиса заказа поездок.


image


Введение


Кто раз умеет обмануть, тот много раз еще обманет.
Лопе де Вега

Фрод в нашем случае это ситуация, когда водитель обманывает компанию. Мошенничество с целью получения денег.


Первый код в компании был написан еще в те времена, когда о мобильных приложениях никто не слышал, доллар был по 25, а в университетах изучали Delphi. Все ловили такси на улице или заказывали по телефону. Менеджеры и кураторы городов вручную искали поездки, похожие на фрод. Процент успеха был крайне низким, и безнаказанные водители продолжали свою криминальную деятельность. Но всему в этом мире рано или поздно приходит конец


Постановка задачи и паттерны поведения


Первоначальная цель создать MVP, который за минимум времени разработки даст максимальный результат.

После консультаций с директорами городов, кураторами и менеджерами были выявлены самые частые схемы мошенничества:


  • Воровство комиссии. Водитель отменяет заказ (или просит по-братски отменить поездку пассажира), потом его выполняет, забирает у клиента наличку. В компанию не приходит ни копейки;
  • Фейковые заказы с целью получения доплат. Доплаты это сумма, которую агрегатор из своего кармана добавляет к выручке водителя за поездку. Тогда ему становится выгоднее брать непопулярные заказы (короткие и дешёвые, например). Водитель делает такой заказ сам себе или другу с левой сим-карты, потом проезжает 200 метров и получает незаслуженную денежку.
  • Более мелкие паттерны, которые мы объединили в один Подозрительные водители:
    • Водители, у которых отключена комиссия (скажем, менеджер города мог поставить 0% комиссии своему другу);
    • Водители, которые покупают безлимитный тариф и по одному аккаунту работают вдвоём-втроём.

image


Функции в нашей команде распределены так:


  • Аналитик отвечает за связь с менеджерами на местах, решает возникающие проблемы, продумывает критерии поиска, в свободное время пишет код;
  • Дата-аналитик (ваш покорный слуга) пишет код, придумывает как сделать так, чтобы всё работало автоматически и точно;
  • Директор по аналитике накидывает свои идеи, критикует чужие, ревьюит код, следит чтобы получался хороший продукт а не как в прошлый раз.

Сложности


  • Важность снижения как False Positive, так и False Negative ошибок. Человеческим языком:
    • Не хочется обвинять честного водителя, так как он нас покинет (и будет прав, чёрт возьми!);
    • Не хочется оставлять нарушителя безнаказанным.
  • Необходимость ручной проверки и человеческий фактор. На этом пункте мы ещё остановимся подробнее;
  • Водители. Да, сами водители это сложность для аналитика. Любая задача, связанная с ними, намного тяжелее аналогичной задачи, связанной с пассажирами. Занимались мы как-то предсказанием оттока и тех, и других Но это уже совсем другая история.

Процесс


image


Основную задачу выполняет SQL-запрос в DWH, который возвращает подозрительные поездки и водителей. Те должны обладать набором заранее рассчитанных признаков. Вот, например, как выглядит фильтрация по паттерну Доплаты:


WHERE susp = 1 -- Флаг на подозрительность  AND finished_orders >= 3 -- Три и более УСПЕШНЕ поездки с одним водителем  AND cancelled >= 3 -- Три и более водителя, с которыми у номера телефона были только ОТМЕН  AND dist_fin_drivers <= 2 -- Успешные поездки максимум с ДВУМЯ водителями  AND ok <= 2 -- Не больше 2-х УСПЕШНХ поездок с другими водителями

Признаки определяются статистически на основании исторических данных, а также путём консультаций с опытными управленцами на местах.


Еще пример. В паттерне Воровство комиссии ключевую роль играют координаты водителя. Отменил заказ, но отметился на всём его протяжении? Ну-ну. Считаем расстояния, добавляем ещё несколько важных фильтров и выгружаем такие поездки.


image


Далее в работу вступает скрипт на python. Он оборачивает выгрузку в pandas, сохраняет ее в postgres, преобразует в нужный вид и выгружает на проверку в листы Google (о них мы еще поговорим подробнее). Часть скрипта, выгружающая поездки, запускается автоматически дважды в день с помощью Apache Airflow.


Рассмотрим работу с API на примере.
Считываем креды и коннектимся:


credentials = ServiceAccountCredentials.from_json_keyfile_dict(    config.crd,    ['https://www.googleapis.com/auth/spreadsheets',     'https://www.googleapis.com/auth/drive'])httpAuth = credentials.authorize(httplib2.Http())service = googleapiclient.discovery.build('sheets', 'v4', http=httpAuth)sheet = service.spreadsheets()

Добавляем данные на лист:


base_range = f'{city_name}!A{ss_row + 1}:Z{ss_row + reserved_rows}'sheet.values().append(spreadsheetId=spreadsheetid,                                 range=base_range,                                 body={"values": df_pos.values.tolist()},                                 valueInputOption='RAW').execute()

Забираем резолюции:


range_from_ss = f'{city_name}!A{ss_row}:S{ss_row + reserved_rows}'data_from_ss = service.spreadsheets().values().get(            spreadsheetId=spreadsheetid,            range=range_from_ss).execute().get('values', [])data_from_ss = pd.DataFrame(data_from_ss)data_from_ss_cols = ['id', 'Резолюция', 'Комментарий']data_from_ss = data_from_ss.loc[1:, data_from_ss_cols]

Заносим их в PG:


vls_ss = ','.join([f"""({', '.join([f(d[c]) for c in data_from_ss_cols])}                    )""" for d in data_from_ss.to_dict('rows')])sql_update = f"""    WITH updated as (        UPDATE fraud_billing        SET resolution = tb.resolution,            comment=tb.comment,            dt = NOW()        FROM (VALUES {vls_ss}) AS tb(fraud_billing_id, resolution, comment)        WHERE fraud_billing.fraud_billing_id = CAST(tb.fraud_billing_id AS INTEGER)            AND ((fraud_billing.resolution IS NULL AND tb.resolution IS NOT NULL)                OR (fraud_billing.comment IS NULL AND tb.comment IS NOT NULL)                OR (fraud_billing.comment IS NOT NULL AND tb.comment IS NOT NULL                   AND fraud_billing.comment <> tb.comment)                OR (fraud_billing.resolution IS NOT NULL AND tb.resolution IS NOT NULL                    AND fraud_billing.resolution <> tb.resolution)               )        RETURNING {alias_cols_text_with_id}        )    INSERT INTO fraud_billing_history ({cols_text_with_id})    SELECT {cols_text_with_id}    FROM updated;"""crs_postgres.execute(sql_update)con_postgres.commit()

В самой postgres для каждого паттерна реализовано две таблицы:


  • хранение записей о поездках и водителях;
  • история обновлений.

Логи скрипта:
image


Менеджеры на местах проверяют поездки на наличие ошибок первого рода (казнить нельзя, помиловать).


Пример того, как выглядит работа менеджера с листом:


image


Иногда по всем признакам сразу видно водитель фродил.


Иногда приходится копать глубже: смотреть трекинг, вызывать водителя в офис, созваниваться с пассажиром.


И так для каждого паттерна.


К сожалению, без ручной проверки на данный момент никак не обойтись. Очень часто встречаются две идентичные поездки, но одна из них оказывается фродом, а вторая нет. Для максимизации доли пойманного фрода приходится идти на жертвы и подозревать честных водителей.


image


На картинке справа наша FP-ошибка будет равна нулю, но мы не поймаем многих мошенников.


На картинке слева поймаем всех, но нужна дополнительная проверка, чтобы определить невиновных. Это наш выбор.


После подтверждения факта фрода в игру вступает карательная машина правосудия. В зависимости от степени тяжести, рецедивов и общей ситуации в городе, водителя:


  • предупреждают;
  • штрафуют;
  • урезают в правах;
  • блокируют временно или навсегда.

На данном этапе мы лишь наблюдаем и логируем информацию.


Отмечу, что результат работы зависит от населённого пункта. Города различаются по населению, площади, уровню конкуренции, условиям для водителей, менеджменту. Для примера сравним кол-во подозрений и фрода за последние недели:


image


Как видите, подобрать универсальные правила и для Новосибирска, и для Магнитогорска нетривиальная задача.


Важную роль во всей системе играют Google Spreadsheets. Они выступают интерфейсом между бэкэндом и конечными пользователями. Несколько лет назад я скептически относился к их использованию в проектах, но на практике они показывают себя очень хорошо:


  • Менеджеры городов хорошо в них разбираются, так как эксельку знают все;
  • У Гугла превосходный API удобный и лёгкий;
  • Они бесплатны;
  • Они обладают всем необходимым функционалом, в том числе контролем версий.

Самая большая проблема


Ручная проверка является важной частью системы антифрода. А там, где люди там ищи проблемы:


  • Самоуправство. Кто-то ведёт на листе расчёты, кто-то удаляет столбцы, кто-то придумывает свои виды резолюций. Мы ввели защиту на листы, но алгоритмы практически при каждом обновлении всё равно ругались на аномалии в данных. Пришлось написать дополнительные скрипты для проверки спрэдшитов перед обновлениями;
  • Необязательность. Проблема встречается не так часто, как предыдущая, но несёт намного больше вреда:
    • кто-то вообще не проверяет поездки, хотя их уже накопилось более тысячи;
    • кто-то не оставляет комментарии;
    • кто-то проставляет резолюции по своим внутренним ощущениям, без проверки;
    • кто-то не наказывает пойманных водителей.

Для борьбы с подобным отношением мы, вместе с кураторами, разрабатываем уголовный кодекс строгий набор правил. Он будет обязательным для всех, оставляя минимум возможностей для творчества.


Описанное выше стандартный рабочий процесс, рутина. Все без исключения наши коллеги ответственные профессионалы, которые качественно выполняют свой этап работы и помогают нам совершенствовать систему. Спасибо им за это!

Развитие и первые результаты


Как мы совершенствуем алгоритмы, то есть снижаем ошибки?


  • Невыявленные случаи фрода. Здесь нам помогают кураторы и менеджеры городов. Бывает, что они самостоятельно ловят фродовые поездки и водителей, которые пропустили наши скрипты. Мы выясняем причину и дорабатываем код;
  • Ложные обвинения. Таких ошибок гораздо больше, плюс их мы можем посчитать. Здесь помогают комментарии менеджеров к поездкам, которые они признали честными.

В начале нашего пути ошибка по самому масштабному паттерну (воровство комиссии) в среднем составляла около 35%. Сейчас меньше 25%. В то же время, по другому паттерну доплатам удалось не только свести ошибку к нулю, но и в десятки раз уменьшить количество таких случаев. Выдвинем гипотезу: водители поняли, что теперь за подобное наказывают, и решили, что риск не стоит свеч. И придумали другие схемы.


image


За первые месяцы работы удалось достичь следующих результатов:


  • 15 тысяч поездок признаны фродом;
  • 6800 водителей понесли наказание;
  • более 500 тысяч рублей вернулись в компанию только по воровству комиссий.

Но самый главный успех это постепенное снижение самих случаев подозрения во многих городах. Ведь, в идеале, мы не хотим ловить больше, мы хотим, чтобы ловить было нечего.


Заключение


Я постарался описать основной функционал и принципы работы системы антифрода, а также сложности, с которыми мы столкнулись. В планах: использование ML для оптимизации поиска, создание системы мониторинга санкций (сейчас она на начальном этапе), улучшение интерфейса для работы менеджеров, создание динамической отчетности, разработка новых паттернов и многое другое.


В конце концов, мы лишь в начале пути.

Подробнее..

Создание автоматической системы борьбы с злоумышленниками на сайте (фродом)

29.08.2020 12:21:18 | Автор: admin
Последние примерно полгода я занимался созданием системы борьбы с фродом (fraudulent activity, fraud, etc.) без какой-либо начальной инфраструктуры для этого. Сегодняшние идеи, которые мы нашли и реализовали в нашей системе, помогают нам обнаруживать множество мошеннических действий и анализировать их. В этой статье я хотел бы рассказать о принципах, которым мы следовали, и о том, что мы сделали для достижения текущего состояния нашей системы, не углубляясь в техническую часть.


Принципы нашей системы


Когда вы слышите такие термины, как automatic и fraud, вы, скорее всего, начинаете думать о машинном обучении, Apache Spark, Hadoop, Python, Airflow и других технологиях экосистемы Apache Foundation и области Data Science. Я думаю, что есть один аспект использования этих инструментов, который, обычно, не упоминается: они требуют наличия определенных предварительных условий в вашей корпоративной системе, прежде чем начать их использовать. Короче говоря, вам нужна корпоративная платформа данных, которая включает озеро данных и хранилище. Но что, если у вас нет такой платформы, и вам все еще нужно развивать эту практику? Следующие принципы, о которых я рассказываю ниже, помогли нам достичь момента, когда мы можем сосредоточиться на улучшении наших идей, а не на поиске работающей. Тем не менее, это не плато проекта. В плане еще много вещей с технологической и продуктовой точек зрения.

Принцип 1: ценность для бизнеса в первую очередь


Во главе всех наших усилий мы поставили ценность для бизнеса. В целом, любая система автоматического анализа относится к группе сложных систем с высоким уровнем автоматизации и технической сложностью. Создание законченного решения займет уйму времени, если вы создадите его с нуля. Мы решили поставить на первое место ценность бизнеса, а на второе технологическую завершенность. В реальной жизни это означает, что мы не принимаем передовые технологии, как догму. Мы выбираем технологию, которая лучше всего работает для нас на данный момент. Со временем может показаться, что нам придется заново реализовать некоторые модули. Этот компромисс, который мы приняли.

Принцип 2: Расширенный интеллект человека (augmented intelligence)


Бьюсь об заклад, большинство людей, которые не вовлечены глубоко в разработку решений машинного обучения, могут подумать, что замена людей это цель. На самом деле, решения машинного обучения далеки от совершенства и только в определенных областях возможна замена. Мы отказались от этой идеи с самого начала по нескольким причинам: несбалансированные данные о мошеннической деятельности и невозможность предоставить исчерпывающий список функций для моделей машинного обучения. В отличие от этого, мы выбрали вариант с расширенным интеллектом. Это альтернативная концепция искусственного интеллекта, которая фокусируется на вспомогательной роли ИИ, подчеркивая тот факт, что когнитивные технологии предназначены для улучшения человеческого интеллекта, а не для его замены. [1]

Учитывая это, разработка полного решения машинного обучения с самого начала потребовала бы огромных усилий, которые задержали бы создание ценности для нашего бизнеса. Мы решили построить систему с итеративно растущим аспектом машинного обучения под руководством наших экспертов в предметной области. Сложная часть разработки такой системы состоит в том, что она должна предоставлять нашим аналитикам кейсы не только с точки зрения того, является ли это мошеннической деятельностью или нет. В общем, любая аномалия в поведении клиентов это подозрительный случай, который специалистам необходимо расследовать и как-то реагировать. Лишь некоторая часть этих зафиксированных случаев действительно может быть отнесена к категории мошенничества.

Принцип 3: платформа обширных аналитических данных


Самая сложная часть нашей системы это сквозная проверка рабочего процесса системы. Аналитики и разработчики должны легко получать наборы данных за прошлые периоды со всеми метриками, которые использовались для анализа. Кроме того, платформа данных должна предоставлять простой способ дополнить существующий набор показателей новым. Процессы, которые мы создаем, а это не только программные процессы, должны позволять легко пересчитывать предыдущие периоды, добавлять новые метрики и изменять прогноз данных. Мы могли бы добиться этого, накапливая все данные, которые генерирует наша производственная система. В таком случае данные постепенно стали бы помехой. Нам нужно было бы хранить растущий объем данных, которые мы не используем, и защищать их. В таком сценарии со временем данные будут становиться все более и более нерелевантными, но по-прежнему требуют наших усилий для управления ими. Для нас накопление данных (data hoarding) не имело смысла, и мы решили использовать другой подход. Мы решили организовать хранилища данных в реальном времени вокруг целевых сущностей, которые мы хотим классифицировать, и хранить только те данные, которые позволяют проверять самые последние и актуальные периоды. Сложность этих усилий заключается в том, что наша система является неоднородной с несколькими хранилищами данных и программными модулями, которые требуют тщательного планирования для согласованной работы.

Конструктивные концепции нашей системы


У нас есть четыре основных компонента в нашей системе: система приема (ingestion system), вычисления (computational), анализ (BI analysis) и система отслеживания (tracking system). Они служат для конкретных изолированных целей, и мы держим их изолированными, следуя определенным подходам в разработке.

image

Дизайн на основе контрактов


Прежде всего, мы договорились, что компоненты должны полагаться только на определенные структуры данных (контракты), которые передаются между ними. Это позволяет легко интегрировать между ними и не навязывать конкретный состав (и порядок) компонентов. Например, в некоторых случаях это позволяет нам напрямую интегрировать систему приема с системой отслеживания предупреждений. В таком случае это будет сделано в соответствии с согласованным контрактом на оповещения. Это означает, что оба компонента будут интегрированы с использованием контракта, который может использовать любой другой компонент. Мы не будем добавлять дополнительный контракт на добавление предупреждений в систему отслеживания из системы ввода. Такой подход требует использования заранее определенного минимального количества контрактов и упрощает систему и коммуникации. По сути, мы используем подход, который называется Contract First Design, и применяем его к контрактам потоковой передачи данных. [2]

Стриминг везде


Сохранение и управление состоянием в системе неизбежно приведет к усложнениям в ее реализации. В общем случае, состояние должно быть доступным из любого компонента, оно должно быть согласованным и предоставлять наиболее актуальное значение для всех компонентов, и оно должно быть надежным с правильными значениями. Кроме того, наличие вызовов к постоянному хранилищу для получения последнего состояния увеличит количество операций ввода-вывода и сложность алгоритмов, использующихся в наших конвейерах реального времени. Из-за этого мы решили убраться хранение состояния, по возможности, полностью из нашей системы. Этот подход требует включения всех необходимых данных в передаваемый блок данных (сообщение). Например, если нам нужно вычислить общее количество некоторых наблюдений (количество операций или случаев с определенными характеристиками), мы вычисляем его в памяти и генерируем поток таких значений. Зависимые модули будут использовать разбиение (partition) и пакетирование (batch) для дробления потока по сущностям и оперирования последними значениями. Этот подход устранил необходимость иметь постоянное дисковое хранилище для таких данных. Наша система использует Kafka в качестве брокера сообщений, и его можно использовать как базу данных с KSQL. [3] Но его использование сильно связало бы наше решение с Kafka, и мы решили не использовать его. Выбранный нами подход позволяет заменить Kafka другим брокером сообщений без серьезных внутренних изменений системы.

Эта концепция не означает, что мы не используем дисковые хранилищя и базы данных. Для проверки и анализа производительности системы нам необходимо хранить на диске значительную часть данных, которые представляют различные показатели и состояния. Важным моментом здесь является то, что алгоритмы реального времени не зависят от таких данных. В большинстве случаев мы используем сохраненные данные для автономного анализа, отладки и отслеживания конкретных случаев и результатов, которые выдаёт система.

Проблемы нашей системы


Есть определенные проблемы, которые мы решили до определённого уровня, но они требуют более продуманных решений. Сейчас я просто хотел бы упомянуть их здесь, потому что каждый пункт стоит отдельной статьи.

  • Нам все еще необходимо определить процессы и политики, которые способствуют накоплению значимых и актуальных данных для нашего автоматического анализа, обнаружения и исследования данных.
  • Внедрение результатов анализа человеком в процесс автоматической настройки системы для её обновления на последних данных. Это не только обновление нашей модели, но также обновление процессов и улучшение понимания наших данных.
  • Нахождение баланса между детерминированным подходом IF-ELSE и ML. Кто-то сказал: ML это инструмент для отчаявшихся. Это означает, что вы захотите использовать ML, когда больше не понимаете, как оптимизировать и улучшить свои алгоритмы. С другой стороны, детерминированный подход не позволяет обнаружение аномалий, которые не были предвидены.
  • Нам нужен простой способ проверить наши гипотезы или коореляции между метриками в данных.
  • Система должна иметь несколько уровней истинно положительных (true positive) результатов. Случаи мошенничества это лишь часть всех случаев, которые можно считать положительными для системы. Например, аналитики хотят получать все подозрительные случаи для проверки, и лишь небольшая часть из них мошенничество. Система должна эффективно предоставлять аналитикам все случаи, независимо от того, является ли это реальным мошенничеством или просто подозрительным поведением.
  • Платформа данных должна позволять получать наборы данных за прошлые периоды с вычислениями, созданными и рассчитанными на лету.
  • Простое и автоматическое развертывание любого из компонентов системы как минимум в трех различных средах: производственной, экспериментальной (бета) и для разработчиков.
  • И последнее, но не менее важное. Нам необходимо создать обширную платформу проверки производительности, на которой мы сможем анализировать наши модели. [4]


Ссылки


  1. What is Augmented Intelligence?
  2. Implementing an API-First Design Methodology
  3. Kafka Transforming Into Event Streaming Database
  4. Understanding AUC ROC Curve
Подробнее..

Группа М.Видео-Эльдорадо объявила итоги конкурса для аналитиков Analyze.ME

05.03.2021 16:08:13 | Автор: admin


В начале февраля мы анонсировали конкурс для аналитиков Analyze.ME. Для участия в проекте все желающие могли зарегистрироваться в период с 3 по 23 февраля 2021 года на специальном сайте.
Далее, с 26 по 28 февраля добровольцам нужно было представить решения для предложенных организаторами конкурсных заданий.

В итоге побороться за призовой фонд в 360 000 рублей изъявили желание более 400 участников. 28 февраля из них были определены 12 финалистов. Под катом подробный рассказ о конкурсных задачах и информация о победителях.

Введение


Поскольку соревнование проводили мы, то и задачи были напрямую связаны с нашим бизнесом. В торговой сети М.Видео-Эльдорадо свыше 1000 магазинов, в более чем 250 городах. Современные реалии требуют от нас быть ближе к клиентам: повышать скорость оформления заказов и предлагать им удобные и быстрые способы доставки товаров. Для этого требуется менять многочисленные процессы и ИТ-системы.

Пандемия значительно ускорила трансформацию нашей компании. Раньше для внедрения крупной доработки требовалось несколько месяцев. Сейчас мы всё чаще используем итеративный подход, чтобы поставлять сначала MVP, а затем последовательно добавлять какие-то возможности.

Например, когда из-за карантина закрылись все наши магазины, мы буквально за несколько дней запустили MVP экспресс-доставки товаров на такси, а затем итеративно его развивали. Сначала было ручное оформление, затем мы автоматизировали вызов такси, потом начали оптимизировать процесс оформления заказа на сайте. Но некоторые улучшения до сих пор трудно внедрить из-за legacy-технологий на сайте.

К сожалению, мы не можем в одночасье изменить и улучшить все свои системы: в М.Видео-Эльдорадо за годы развития накопилось много устаревших систем. Мы это прекрасно понимаем, непрерывно переписываем и обновляем их.

К примеру, сейчас запущены проекты по полному переносу сайта на новый стек технологий. Также мы оптимизируем мобильное приложение, переводя его на Core-сервисы. Но для избавления от legacy требуется немало времени и сил.

Поэтому мы идём двумя путями: внедряем новую функциональность, стремясь к централизации общей логики, разрабатывая слой core-микросервисов, которыми пользуются фронт-системы и постепенно переписываем розничные системы, сайты, CRM-систему и так далее.

Столь длинное вступление понадобилось нам, для объяснения общего контекста представленных на наш конкурс задач.



Первая задача


Сначала вводные данные.
Клиент заходит на сайт, выбирает товары, кладёт их в корзину, меняя её содержимое как угодно, а потом выбирает удобный способ получения. Сейчас товар можно получить самовывозом, доставкой со склада, быстрой доставкой из магазина или из партнерских пунктов выдачи.

Для каждого исполнителя доставки могут задаваться ограничения по весу и габаритам заказа. Мы стараемся использовать больше пеших курьеров, потому что автотранспорт экологически вреден, хотя в машину может поместиться гораздо больше, чем в рюкзак. Цена доставки может зависеть от ряда параметров.

Например, от стоимости корзины заказа (для более дорогих покупок доставка дешевле или бесплатна) или расстояния до клиента. Если товар есть в наличии поблизости, то это тоже снижает стоимость доставки. На такси мы сейчас можем доставлять только заказы, оплаченные на сайте. Участники могли предложить снять это ограничение.

Выбрав способ получения заказа, покупатель подтверждает заказ. При оплате учитываются скидки и бонусы. В магазине или на складе собирают заказ. Товары по оформленным на сайте заказам должны дождаться своего покупателя, магазины не могут их продать. При этом в магазине товары находятся в двух зонах: на складе и в торговом зале. Ради экологичности и экономии бумаги заказ должен передаваться курьеру без бумажного подтверждения: всяких квитанций, товарных чеков, накладных и прочего.

Курьеры разных компаний могут работать в собственных информационных системах, с которыми можно интегрироваться по API. Покупатель может на сайте или в мобильном приложении посмотреть статус своего заказа. В магазинах есть розничная информационная система будем считать, что она еще legacy в которой ведется учет товарных остатков, продаж и так далее.

В каждом магазине есть свой экземпляр этой розничной системы с базой данных, серверными приложениями, приложениями кассиров, кладовщиков и так далее. И есть центральная база данных, в которую через определенные интервалы передаются данные из каждого магазина.

При переработке архитектуры этой системы участники могли выбирать любой способ интеграции. Также у нас есть сервис для расчета заказа с учетом примененных скидок и сервисы, возвращающие мастер-данные по товарам (каталог, наименование, характеристики), цены, промо-акции и так далее.

А теперь перейдём к самой задаче. Участникам предлагалось описать в любой нотации бизнес-процессы оформления заказа от открытия клиентом сайта до получения заказа. Включая действия, которые выполняются без участия клиента.

Задача состояла из двух частей:
  • описание процесса и архитектуры системы;
  • предложение новых способов получения заказа, чтобы клиентам было еще удобнее.

Участники могли пользоваться любыми открытыми источниками с информацией по ассортименту, расположению наших магазинов, количеству товаров, их габаритам и так далее.

После описания бизнес-процессов на примере какого-нибудь одного способа доставки нужно было описать архитектуру проекта либо составить компонентную диаграмму с интеграционными потоками. Компоненты, не указанные в описании задачи, можно было добавлять по своему усмотрению.

После этого необходимо было выбрать одну из интересных систем, получившихся в архитектуре, и подробнее расписать функциональные (варианты использования, диаграммы активности) и нефункциональные требования к этой системе.

По желанию можно было использовать любые другие диаграммы, например, диаграмму состояний, последовательностей и так далее. Полезно было описать и логическую модель данных для выбранной системы (в любой нотации).

Вторая задача


Вторая задача была предназначена не для бизнес-аналитиков, а для кросс-системных аналитиков. В чём между ними различие? Кросс-системные аналитики те же самые аналитики, просто они смотрят на задачу под другим углом.

Обычные бизнес-аналитики работают внутри одной системы, то есть аналитик плотно и ежедневно взаимодействует в рамках определенной функциональности или конкретного продукта. А кросс-системные аналитики используются тогда, когда требуется решить задачу, затрагивающую сразу несколько функций внутри М.Видео Эльдорадо.

Например, сейчас в стране вводится обязательная маркировка определённых групп товаров. Это коснулось и нашего ассортимента. Внедрять маркировку требуется в двух наших брендах, в шести наших фронтальных решениях. Необходимо доработать бэк-офис, большое количество сервисов, и такого рода задачи берут на себя кросс-системные аналитики.

Таким образом, результатом работы кросс-системного аналитика является не требование к системе, не функциональное или техническое задание, а проектные решения: документы, в которых описано, какая задача решается, какие бизнес-процессы компании подвергаются изменению, как должны протекать эти процессы в разрезе систем.

То есть в какой момент времени, какая операция и в какой системе должна быть выполнена, когда система должна обратиться к другой системе, какие действия сотрудника сопровождают это событие, насколько изменяется процесс относительно текущего.

Вторая задача на соревновании была сформулирована так же, как и первая, но решить её нужно было в виде достаточно подробного описания бизнес-процессов, в виде проектного решения. Нужно было описать предлагаемые процесс и необходимые доработки во всех системах, включенных в выполняемое изменение.

Дополнительно участникам нужно было составить чек-лист для проверки корректности созданной функциональности. В нашей компании это является неотъемлемой частью работы кросс-системного аналитика.

Победители


В соревновании приняли участие 426 человек. До полуфинала дошли 107, а в финале встретились 12 человек, решавших задачу для бизнес-аналитиков, и 6 решавших задачу для кросс-системных аналитиков.

Все предложенные финалистами решения были очень интересными и выполненными на высоком уровне. Так что выбрать победителей было действительно сложно.

Призёры, решавшие задачу для бизнес-аналитиков:

Третье место с призом в 30 тысяч рублей заняла Элина Семенова. Она описала процесс оформления и исполнения заказа, детально проработал систему управления доставкой.

Второе место с призом в 50 тысяч рублей завоевал Павел Бобров. Он уделил много внимания архитектуре, а также верхнеуровнево описал бизнес-процесс.

И первое место с призом в 100 тысяч рублей жюри единодушно отдало Алексею Никитину. Он подробно описал процесс заказа и доставки товара, на архитектурой схеме предложил свое видение реализации этих процессов. Алексей отлично презентовал результат своей работы, рассказал о целях и проблемах, которые хотел решить.

Алексей Никитин:
Я участвовал в подобном конкурсе впервые. Не потому, что раньше это было мне не интересно, а потому, что не встречал таких мероприятий. Поэтому, когда увидел объявление, то сразу побежал записываться.

Задачу выбрать было не сложно. Организаторы сделали так, что формулировка бизнес-проблемы была в обоих треках одна и та же, но в треке для кросс-системных аналитиков нужно было описать больше систем, пусть и не так подробно. Поэтому я решил сперва поработать над первой задачей, а потом переключиться на вторую. Задача была интересная, и у меня было несколько идей. Но я решил остановиться на том варианте, который, как мне показалось, мне будет проще объяснить.

В первый день мне удалось описать все процессы, которые хотел. Но на второй день я начал спотыкаться на каких-то мелочах, которые не стыковались, тратил на них время, и в какой-то момент понял, что описал 80 % того, о чём хотел сказать, и отложил ручку в сторону. Качественно описать и оформить вторую задачу я уже никак не успевал, и внутренний перфекционист посоветовал не позориться.

Предложил ли я что-то уникальное? Не думаю. Но я сам не так давно заказывал в М.Видео стиральную машину, и должен сказать, что ожидать курьера с 10 до 18 это плохой опыт. Поэтому мне не нужно было влезать в шкуру клиента, я просто описал, как это было бы удобно для меня.

В голове я держал сценарий Яндекс.Лавки, которая анонсировала в Москве доставку по звонку в течение 15 минут. Но даже там речь не шла о доставке за 15 минут, а лишь о том, чтобы клиенту было удобно, пусть даже с доставкой на следующий день. Думаю, это невозможно масштабировать по всей стране, где клиенты живут не так плотно, поэтому я старался сделать удобно для людей, но не гнаться за минимальным временем доставки.

Призёры, решавшие задачу для кросс-системных аналитиков:

Третье место с призом в 30 тысяч рублей завоевал Павел Оразбаев. Он предложил самое чётко прописанное решение, а в качестве подрядчика по доставке предложена конкретная компания Достависта. Было рассчитано, почему она может иметь преимущества перед остальными компаниями. Павел даже предложил, как можно реализовать способ доставки заказов в течение получаса тем покупателям, которые хотят получить свои покупки в конкретный день.

Второе место с призом в 50 тысяч рублей заняла Ксения Алексеева. Ее решение тоже сделано на более высоком уровне в части проработки процессов, в нём использованы разные типы UML-диаграмм.

И первое место с призом в 100 тысяч рублей досталось Михаилу Березовскому. Его решение максимально четко совпадало с критериями приемки задания, спроектировано именно на том уровне, который был задан.

Михаил Березовский:
Я первый раз участвовал в таком конкурсе, и раньше ничего подобного не встречал. Задачи были очень похожи, и я, можно сказать, выбирал душой. Вторая показалась сложнее сложная, поэтому её и выбрал.

Сценарий, на мой взгляд, настоящий, не про придумывание самого красивого космического корабля, а про решение реальной задачи, стоящей перед игроками retail-сектора. Мне это было интересно, так как я больше люблю описывать решения, которые на следующий день можно начать реализовывать.

Постановка задачи позволяла предлагать сложные архитектурные решения или пытаться работать в стиле бритвы Оккама. Я пытался не впадать в крайности и усложнял только в тех случаях, где это было действительно необходимо.

Я исходил из того, что в моей категории соревнуются специалисты, которые должны предлагать схемы взаимодействия многих систем, поэтому попытался сделать фичей саму архитектуру решения. В итоге описал сценарий, который позволил бы компании менять клиентский опыт с минимальным time to market, что, как мне кажется, и сыграло в мою пользу.

Минувшее соревнование стало первым нашим конкурсом для аналитиков. К концу года мы планируем удвоить свой IT-департамент, и приглашаем на работу специалистов не только из России, но и живущих за рубежом. Работаем на удаленке и возвращаться в офисы не планируем, а рабочий ноутбук доставим вам на дом.

Если вы заинтересованы в интересной работе, смелее заходите на сайт или пишите сразу на почту: dmitry.treskunov@mvideo.ru.

Подробнее..

Почему большинство юнит тестов пустая трата времени? (перевод статьи)

17.05.2021 16:18:04 | Автор: admin

Автор: James O Coplien

Перевод: Епишев Александр

1.1 Наши дни

Во времена FORTRAN, когда функция была функцией, иногда заслуживающей функциональных проверок, юнит-тестирование было одним из главных составляющих. Компьютеры производили вычисления, в то время как функции и процедуры представляли собой вычислительные блоки. В те времена доминирующий подход в дизайне предполагал создание комплексной внешней функциональности из более мелких кусков, которые, в свою очередь управляли еще более мелкими, и так далее, вплоть до уровня хорошо понятных примитивов. Каждый слой поддерживал находящийся над ним слой. В целом, у вас были большие шансы отследить, как функциональность на самом дне, так называемые функции и процедуры, были связаны с требованиями, выраженными в доступном человеку интерфейсе. Можно было рассчитывать, что хороший дизайнер поймет бизнес цель той или иной функции. Такими же возможными для понимания были и взаимосвязи в дереве вызовов, как минимум в хорошо структурированном коде. Вы могли мысленно смоделировать выполнение кода во время код-ревью.

Постепенно объектное ориентирование штурмом захватывает мир, а мир проектирования, и вовсе, переворачивает вверх дном. Во-первых, проектируемые блоки превратились из вычислительных единиц в маленькие гетерогенные композиты, называемые объектами и совмещающие в себе несколько программных артефактов, включая и функции, и данные внутри одной обертки. Объектная парадигма использовала классы для оборачивания нескольких функций вместе с общими для этих функций спецификациями данных. Подобный класс становился шаблоном, из которого в ран-тайме создавались объекты. В таком вычислительном контексте, необходимая функция определяется во время выполнения и не может быть вызвана из исходного кода, как это было возможно с FORTRAN. Это привело к невозможности разобраться во взаимосвязях поведения кода в ран-тайме лишь за счет анализа. Теперь вам необходимо запустить программу для того, чтобы получить малейшее представление о том, что же в ней происходит.

Итак, тестирование снова вступает в игру. На этот раз - юнит-тестирование на стероидах. Объектно-ориентированное сообщество обнаружило ценность ранней обратной связи, обусловленной возрастающей скоростью техники, а также появлением всё большего числа персональных компьютеров. Дизайн становится более сфокусированным на данных, так как объекты всё больше конструируются исходя из структур этих данных, чем каких-либо свойств своих же методов. Нехватка явно выраженной структуры вызовов усложнила возможность поместить выполнение какой-либо атомарной функции в контекст своего же выполнения. И насколько бы малым ни был шанс для подобной возможности, и тот был отнят полиморфизмом. В результате, интеграционное тестирование оказывается вне, а первоочередную роль принимает на себя юнит-тестирование. Где-то на фоне всё ещё виднелось системное, но, скорее уже как проблема кого-то другого, или, что еще более опасно, как один из вариантов более зрелой версии юнит-тестирования, которое проводилось самими же создателями кода.

Классы превратились в объекты анализа, и, в определенной степени, проектирования. Популярной техникой дизайна стали CRC-карты (обычно представляющие Классы, Ответственности и Кооперацию), каждый класс в которых представлен отдельным человеком. Объектная ориентация начала ассоциироваться с антропоморфным дизайном. Классы, к тому же, превратились в единицы администрирования, дизайна и программирования, а их антропоморфная суть усилила стремление каждого создателя класса его протестировать. Поскольку у некоторых методов класса сохранилась такая же контекстуализация, как и у функции FORTRAN, у программистов возникла необходимость предоставлять контекст перед выполнением метода (помните, что мы не тестируем классы и, даже, не тестируем тестовые объекты, единицей функционального теста является метод). Юнит тесты обеспечивали выполнение сценариев драйверами. Моки - контекст состояния окружения (энва) и других методов, от которых зависел тестируемый метод. При подготовке к тесту, тестовые окружения поставляли необходимые средства для создания каждого объекта в его правильном состоянии.

1.2 Лекарство хуже болезни

Конечно же, юнит-тестирование не является проблемой исключительно объектно-ориентированного программирования, de rigueur (лат. "крайней необходимостью"), скорее всего, его сделала комбинация объектной-ориентированности, эджайла, разработки программного обеспечения, а также рост инструментов и вычислительных мощностей. Как консультант, я часто слышу вопросы о юнит-тестировании, включая следующий от одного из своих клиентов, Ричарда Якобса (Richard Jacobs) из Sogeti (Sogeti Nederland B.V.):

Второй вопрос касается юнит-тестов. Если я правильно припоминаю, вы говорили, что юнит-тесты - это пустая трата времени. Во-первых, я был удивлен. Тем не менее, сегодня моя команда сообщила, что их тесты сложнее, чем сам код. (Это не та команда, которая изначально написала код и юнит тесты. Поэтому некоторые тесты застают их врасплох. Текущая команда более высоко квалифицирована и дисциплинирована.) В таком случае, по моему, это пустая трата времени... Когда я ежедневно программировал, то создавал действительно тестируемый код, однако, почти никогда не писал никакие юнит тесты. При этом я заслуживал признание за свой качественный код и почти безошибочное программное обеспечение. Мне хотелось бы разобраться, ПОЧЕМУ такой вариант работал в моем случае?

Вспомните из своего университетского образования, что любую программу можно смоделировать как ленту Тьюринга, и то, на что именно способна эта программа, каким-то образом связано с количеством битов на данной ленте перед её выполнением. Если вы хотите тщательно протестировать такую программу, вам понадобится тест, по крайней мере, с таким же объемом информации: то есть другая лента Тьюринга, как минимум, с таким же количеством бит.

На практике же, особенности языка программирования затрудняют достижение подобной компактности выражения в тесте, поэтому для проведения полного тестирования, количество строк кода в юнит тестах должно было бы быть на порядок выше, чем в самом тестируемом юните. Немногие разработчики признают, что проводят только выборочное или частичное тестирование, многие же остальные скажут вам, что делают полное тестирование, исходя из некоего своего предполагаемого представления о том, что значит полное. К таким представлениям относится заявление типа Каждая строка кода покрыта, что, с точки зрения теории вычислений, является откровенной чепухой, особенно в понимании того, действительно ли код делает, что должен. Более детально мы обсудим эту проблему ниже. Всё же, большинство программистов, понимают юнит тестирование именно так, изначально обрекая его на провал.

Будьте скромны относительно способностей ваших юнит тестов, кроме тех случаев, когда для тестируемого юнита у вас есть оракул в виде внешних требований. Сомнительно, что юнит тесты способны протестировать более одной триллионной от функциональности любого предоставленного метода в рамках разумного цикла тестирования. Придите в себя.

(Здесь, триллион - не риторический прием, а цифра, основанная на различных возможных состояниях, с учетом того, что средний размер объекта представляет собой четыре слова, и, по консервативной оценке, вы используете 16-битные слова).

1.3 Тесты ради тестов и спроектированные тесты

У меня был клиент из Северной Европы, разработчики которого должны были предоставить 40% покрытия кода, для, так называемого, 1-го уровня зрелости программного обеспечения, 60% для 2-го уровня и 80% для 3-го, хотя были и стремящиеся к 100%. Без проблем! Как вы могли бы предположить, достаточно сложная процедура с ветвлениями и циклами стала бы вызовом, однако, это всего лишь вопрос принципа divide et impera (разделяй и властвуй). Большие функции, для которых 80% покрытие было невозможным, разбивались на множество более мелких, для которых 80% уже было тривиальным. Такой подход повысил общий корпоративный показатель зрелости команд всего лишь за один год, потому как вы обязательно получаете то, что поощряете. Конечно же, это также означало, что функции больше не инкапсулировали алгоритмы. Невозможным оказалось понимание контекста выполняемой строки, точнее тех, которые предшествуют и следуют за ней во время выполнения, поскольку эти строки кода больше не имеют прямого отношения к той, которая нас интересует. Такой переход в последовательности теперь происходил благодаря вызову полиморфной функции - гипер-галактической GOTO. Даже если всё, что вас беспокоит, - это покрытие решений (branch coverage), это больше не имеет значения.

Если вы обнаруживаете, что тестировщики дробят функции для поддержки процесса тестирования, вы разрушаете свою системную архитектуру, а, вместе с ней, и понимание кода. Тестируйте на более грубом уровне детализации.

Речь идет о простых объемах кода. Вы можете их уменьшить, однако такой код будет содержать циклы, обходящие стороной теорию информации, и заворачивать множество строк кода в маленькое пространство. Это означает, что тесты, по крайней мере, должны быть такими же вычислительно сложными, как и код. Помимо множества тестов, последние еще и очень время затратные. Для проверки любой разумной комбинации индексов в цикле простой функции могут потребоваться столетия.

Задумайтесь на секунду о вычислительной сложности этой задачи. Под 100% покрытием, я подразумеваю проверку всех возможных комбинаций всех возможных ветвлений, проходящих через все методы класса, которые воспроизводят все возможные конфигурации битов данных, доступные этим методам, в каждой инструкции машинного языка во время выполнения программы. Все остальное - это эвристика, о корректности которой нельзя сделать никаких формальных заявлений. Число возможных путей выполнения с помощью функции невелико: скажем, 10. Перекрестное произведение этих путей с возможными конфигурациями состояний всех глобальных данных (включая данные экземпляра, которые для области видимости метода являются глобальными) и формальных параметров в действительности же очень велико. Перекрестное произведение этого числа с возможной последовательностью методов внутри класса представляется счетно-бесконечным. Если вы возьмете несколько типичных чисел, то быстро осознаете, насколько вам повезло, если получите покрытие лучше, чем 1 из 1012.

Одна из брут-форс атак такой проблемы - непрерывный прогон тестов. Люди смешивают понятие автоматизированных тестов с юнит тестами: иногда настолько, что, когда я критикую юнит-тестирование, они упрекают меня за критику автоматизации.

Если вы пишете тест с целью покрытия как можно большего количества возможных сценариев, тогда можете уже выделять ферму машин для прогона тестов и отслеживания последних результатов 24/7.

Помните, однако, что автоматизированный хлам - это всё ещё хлам. И те из вас, у кого есть корпоративная Lean-программа, могли заметить, что основы производственной системы Toyota, которые лежали в основе Scrum, очень сильно противились автоматизации интеллектуальных задач (http://personeltest.ru/away/www.computer.org/portal/web/buildyourcareer/Agile Careers/-/blogs/autonomation). Более эффективно - это постоянно удерживать человека процессе, что становится еще более очевидным при исследовательском тестировании. Если вы собираетесь что-то автоматизировать, автоматизируйте что-нибудь ценное. Автоматизировать необходимо рутинные вещи. Возможно даже, вы получите еще больше прибыли от инвестиций, если автоматизируете интеграционные тесты, тесты для проверки регрессионных багов, а также системные, вместо того, чтобы заниматься автоматизацией юнит тестов.

Более разумный подход уменьшает объем тестового кода за счет формального проектирования тестов: то есть, формальной проверки граничных условий, большего количества тестов белого-ящика и т.д. Для этого необходимо, чтобы программный юнит проектировался как тестируемый. Вот как это делают инженеры по аппаратному обеспечению: разработчики предоставляют контрольные точки, способные считывать значения c J-Tag микросхем, для доступа к внутренним значениям сигналов микросхем - это равносильно доступу к значениям между промежуточными вычислениями, содержащимися в вычислительном юните. Я настоятельно рекомендую делать подобное на системном уровне, на котором должно быть сосредоточено основное внимание тестирования; я никогда не видел, чтобы кто-то достигал подобного на уровне юнита. Без таких приемов вы ограничиваете себя юнит-тестированием черного ящика.

Я мог бы поверить в формализованный дизайн юнит теста, если бы такое поведение можно было формализовать, то есть, если бы существовал некий абсолютный, формальный оракул корректности, на основе которого можно было бы создать тест. Подробнее об этом ниже. В противном случае это лишь догадка программиста.

Тесты следует создавать с огромной осторожностью. Большинство функциональных тестов должны скорее разрабатываться представителями бизнеса, чем программистами. Юнит тесты должны же ограничиваться теми, которые можно сопоставить с определенными сторонними приемочными критериями.

1.4 Убеждение, что тесты умнее кода, говорит о скрытом страхе или плохом процессе

У программистов существует негласное убеждение о том, что они способны мыслить более ясно (или, скорее, догадываться), во время создания тестов, чем при написании исходного кода, или что в тесте, каким-то образом, содержится больше информации, чем в самом коде. Это просто формальная ерунда. Психология здесь поучительна и важна, потому что именно она, а не какое-то рассчитываемое свойство, больше всего руководит поведением разработчика.

Если ваши кодеры предоставляют в юнит-тестах больше строк, чем в исходном коде, это, вероятно, означает одно из следующего. У них может быть параноидальное отношение к корректности; паранойя вытесняет ясное мышление и инновации, которые служат предзнаменованием высокого качества. Им может не хватать аналитических ментальных инструментов или дисциплины мышления, и хотелось бы, чтобы машина думала за них. Машины хорошо справляются с повторением механических задач, однако дизайн тестов по-прежнему требует тщательного обдумывания. Или может случиться так, что в вашем процессе частая интеграция невозможна из-за плохо налаженных процессов разработки или неподходящих инструментов. Программисты делают всё возможное, чтобы компенсировать подобную ситуацию за счет тестов там, где они хотя бы в какой-то степени обретают контроль над собственной судьбой.

Если у вас большой объем юнит-тестов, оцените обратную связь в процессе разработки. Интегрируйте код чаще; сократите время сборки и интеграции; сократите количество юнит тестирования и перейдите больше к интеграционному.

Проблема также может быть в другом: разработчики не обладают достаточно развитыми навыками проектирования, или же процесс не стимулирует архитектурное мышление и добросовестный подход к дизайну. Возможно, требования настолько плохи, что разработчики даже и не поняли бы что нужно протестировать, если бы им пришлось это сделать, поэтому всё, чем они руководствуются - это их лучшие догадки. Исследования программной инженерии показали, что наиболее экономически эффективным для исправления ошибок является момент перехода от анализа к проектированию, сам дизайн, а также дисциплина кодирования. Намного проще избежать ошибок, чем их устранять.

Если у вас есть комплексные юнит тесты, но по-прежнему наблюдается высокий процент ошибок в системных тестах или общее низкое качество, нет необходимости автоматически винить в этом тесты (ни юнит-тесты, ни системные). Тщательно проанализируйте свои требования и процесс проектирования, а также его связь с интеграционными тестами и системными тестами.

Тем не менее, будем честны, ошибки будут всегда. Тестирование никуда не денется.

1.5 У тестов с низким уровнем риска низкая (даже потенциально отрицательная) отдача

Как то я озвучил своему клиенту предположение о том, что множество их тестов могут быть тавтологическими. Предположим, задача какой-то функции - это присвоение X значения 5, и я готов поспорить, что существует соответствующий тест для данной функции, который, после запуска, проверяет, равняется ли X 5. Снова же, хорошее тестирование, основывается на тщательном размышлении, а также базовых принципах управления рисками. Управление рисками строится на статистике и теории информации; если тестировщики (или, по крайней мере, менеджер по тестированию) не обладают хотя бы элементарными навыками в этой области, вы, с большой вероятностью, создаете множество бесполезных тестов.

Разберем тривиальный пример. Цель тестирования - предоставить информацию о вашей программе. (Тестирование само по себе не повышает качество; это делают программирование и проектирование. Тестирование лишь сообщает об упущениях команды в создании правильного проектирования и соответствующей реализации.) Большинство программистов хотят услышать информацию о том, что их программный компонент работает. Поэтому, как только в проекте трехлетней давности была создана первая функция, тут же для нее был написан и юнит тест. Тест ни разу не падал. Вопрос: Много ли информации содержится в этом тесте? Другими словами, если 1 - это успешно выполненный тест, а 0 - упавший, тогда сколько будет информации в следующей строке результатов:

11111111111111111111111111111111

Существует несколько возможных ответов, обусловленных видом применяемого формализма, хотя большинство из них не верны. Наивный ответ - 32, однако, это биты данных, а не информации. Возможно, вы информационный теоретик и скажете, что количество битов информации в однородной двоичной строке равносильно двоичному логарифму длины этой строки, которая в данном случае равна 5. Однако это не то, что я хочу знать: в конце концов хотелось бы понять, сколько информации можно получить после одноразового прогона такого теста. Информация основывается на вероятности. Если вероятность успешного прохождения теста равняется 100%, тогда, по определению теории информации, этой информации нет вообще. Ни в одной из единиц указанной выше строки не содержится почти никакой информации. (Если бы строка была бесконечно длинной, то в каждом тестовом прогоне было бы ровно ноль битов информации.)

Далее, сколько бит информации в следующей строке тестовых прогонов?

1011011000110101101000110101101

Ответ... намного больше. Вероятно, 32. Это означает, что в каждом тесте содержится намного больше информации. Если мы изначально не способны предсказать, пройдет ли тест успешно или нет, тогда каждый запуск теста содержит полный бит информации, и добиться чего-то лучшего вы не сможете. Видите ли, разработчики любят поддерживать тесты, которые проходят успешно, потому что это подогревает их эго и уровень комфорта. Однако, информация поступает от упавших тестов. (Конечно же, мы могли бы взять другую крайность:

00000000000000000000000000000000

в которой, фактически, нет никакой информации, в том числе, даже о процессе улучшения качества.)

Если вы хотите сократить объем тестов, первое, что следует сделать, - это обратить внимание на те, которые ни разу за год не упали, и рассмотреть вопрос об их удалении. Такие тесты не предоставляют вам никакой информации, или, по крайней мере, очень мало информации. Ценность производимой ими информации может не оправдывать затрат на поддержку и выполнение таких тестов. Эти тесты - первые претенденты для удаления, и не имеет значения, это юнит тесты, интеграционные или системные.

У другого моего клиента так же было слишком много юнит тестов. Я отметил, что подобная ситуация замедлит скорость разработки, потому как каждое изменение функции потребует изменения соответствующего теста. В ответ мне сообщили, что тесты написаны таким образом, чтобы их не нужно было обновлять при изменении функциональности. Разумеется, это означает, что тесты не проверяли функциональность, поэтому всё, что не тестировалось бы такими тестами, не имело особой ценности.

Не стоит недооценивать интеллект своих людей, как и не стоит недооценивать коллективную глупость множества совместно работающих людей в сложном домене. Возможно, вы подумали, что никогда не допустили бы ошибок описанной выше команды, но я всегда нахожу все больше и больше таких случаев, в которые сложно поверить. Скорее всего и у вас в шкафу есть какие-то из этих скелетов. Отыщите их, посмейтесь над собой, исправьте их и двигайтесь дальше.

Если у вас есть подобные тесты - это второй претендент на удаление.

Третий набор для удаления - тавтологические тесты. Я сталкиваюсь с ними чаще, чем вы можете себе представить, особенно среди последователей, так называемой, разработки через тестирование (TDD). (Кстати, проверка this на ненулевое/не пустое (non-null) значение при входе в метод, не является тавтологической, и может быть очень информативной. Однако, как и в случае с большинством юнит тестов, лучше сделать ассершн, чем пичкать свой тестовый фреймворк подобными проверками. Подробнее об этом ниже.)

Во многих компаниях, единственные тесты с бизнес-ценностью - это те, в основании которых лежат бизнес-требования. Большинство же юнит тестов основываются на фантазиях программистов о том, как должна работать функция: на их надеждах, стереотипах, а иногда и желаниях, как все должно было бы быть. У всего этого нет подтвержденной ценности. В 1970-х и 1980-х годах существовали методологии, опирающиеся на прослеживаемость (tracebility), и стремящиеся сократить системные требования вплоть до уровня юнитов. В общем, это NP-трудная (нелинейная полиномиальная) задача (если только вы не выполняете чисто процедурную декомпозицию), поэтому я очень скептичен в отношении всех, кто говорит, что способен её решить. В итоге, единственный вопрос, который следовало бы задавать каждому тесту: Если тест упадет, какое из бизнес-требований будет нарушено? В большинстве случаев, ответ: Я не знаю. Если вы не понимаете ценность теста, тогда, теоретически, он может иметь нулевую ценность для бизнеса. У теста есть стоимость: поддержка, время вычислений, администрирование и так далее. Значит, у теста может быть чистая отрицательная ценность. И это четвертая категория тестов, которые необходимо удалять. Такие тесты, не смотря на их способность что-то проверять, в действительности ничего не проверяют.

Если вы не можете объяснить, как сбой какого-либо юнит теста отражается на рисках, следует подумать о возможном удалении такого теста. При отсутствии формальных критериев корректности, существуют более эффективные техники устранения изъянов в качестве, такие как исследовательское тестирование и методы Монте-Карло. (Они великолепны, и я рассматриваю их как отдельную категорию в отличие от того, о чем рассказываю здесь.) Не используйте юнит тесты для подобных проверок.

Обратите внимание, существуют определенные модули, а также тесты, которые дают четкий ответ на вопрос о ценности для бизнеса. Одни из таковых - это регрессионные тесты; они, однако, редко пишутся на уровне модульных тестов, больше - на системном. При падении регрессионного теста, в силу его внутреннего устройства, мы точно знаем об особенности ошибки. Кроме того, в определенных системах существуют ключевые алгоритмы, типа алгоритмов сетевой маршрутизации, которые можно протестировать за счет API. Как я уже отмечал выше, для таких API существует формальный оракул, на основании которого можно создавать тесты. Такие юнит тесты имеют ценность.

Подумайте, должна ли основная часть ваших юнит тестов проверять ключевые алгоритмы, для которых существует оракул успешности, предоставленный третьей стороной, а не той же командой, которая пишет код. Понятие успешности здесь должно отражать бизнес-видение, а не, скажем, мнение члена команды, называемого тестировщиком, которое ценится только лишь потому, что оно независимо. Хотя, конечно же, независимая оценка тоже важна.

1.6 Сложное - сложно

Существует следующая дилемма: большая часть интересных показателей о качестве определенных программ находиться в распределении результатов тестирования, несмотря на то, что традиционные подходы к статистике, всё же, предоставляют ложную информацию. Так, в 99,99% всех случаев тест может быть успешным, но однажды упав за десять тысяч раз, он убьет вас. Опять же, заимствуя аналогию из мира железа, для уменьшения вероятности ошибки до сколь угодно низкого уровня, вы можете всё проектировать с учетом заданной вероятности отказа или же провести анализ наихудшего случая (WCA). Специалисты по аппаратному обеспечению обычно используют WCA при проектировании асинхронных систем для защиты от сбоев в сигналах, выходящих за пределы проектных параметров: один сбой на 100 миллионов раз. В области аппаратного обеспечения, сказали бы, что коэффициент качества (FIT rate) такого модуля равняется 10 - десять отказов на триллион (Failures In a Trillion).

Клиент, упомянутый мной в начале этой статьи, был озадачен причиной отсутствия положительных результатов тестирования в своей текущей команде, по сравнению с предыдущей работой. Я отправил ему более раннюю версию данной статьи, на что он ответил:

Приятно читать статью, проливающую свет на причину моего успеха (и остальной части моей команды). Возможно, Вы уже знаете, что я инженер по авионике, чья карьера началась с разработки встраиваемого программного обеспечения, и, отчасти, разработки оборудования. И вот с таким образом мышления, ориентированным на особенности работы оборудования, я начал тестировать свое программное обеспечение. (Команда состояла из четырех человек: 3-х инженеров-электриков из Делфтского университета (включая меня, в качестве специалиста по авионике) и одного инженера-программиста (из Гаагского университета). Мы были очень дисциплинированы в разработке систем безопасности для банков, пенитенциарных учреждений, пожарных, полицейских участков, служб экстренной помощи, химических заводов и т.д. В каждом из случаев всё должно было правильно заработать с первого раза.)

Обычно, при наличии разумных предположений, вы можете провести WCA аппаратного обеспечения, из-за легко прослеживаемых причинно-следственных связей: можно взять схему и разобраться в причинах изменения состояний в элементах памяти. Состояния в машине фон Неймана изменяются в результате побочных эффектов (side effects) выполнения функции, и, как правило, отследить причину такого изменения нереально, даже если некое состояние является достижимым. Объектная ориентация еще больше усугубляет ситуацию. Невозможно понять, какая из инструкций последней изменила состояние, используемое программой.

Большинство программистов убеждены, что построчное покрытие исходного кода, или, по крайней мере, покрытие ветвлений является вполне достаточным. Нет. С точки зрения теории вычислений, покрытие наихудшего случая означает анализ всевозможных комбинаций в последовательностях работы машинного языка, при котором гарантируется достижение каждой инструкции, а также - воспроизведение каждой возможной конфигурации битов данных в каждом из значений счетчика команд выполняемой программы. (Недостаточна и симуляция состояния среды выполнения только лишь для модуля или класса, содержащего тестируемую функцию или метод: как правило, любое изменение в каком-либо месте может проявиться в любом другом месте программы, а поэтому, потребует повторного тестирования всей программы. Формальное доказательство предложено в статье: Перри и Кайзера (Perry and Kaiser), Адекватное тестирование и объектно-ориентированное программирование (Adequate Testing and Objectoriented Programming), Журнал объектно-ориентированного программирования 2 (5), январь 1990 г., стр. 13). Даже взяв небольшую программу, мы уже попадаем в такое тестовое окружение, количество комбинаций в котором намного превышает количество молекул во Вселенной. (Мое определение понятия покрытие кода - это процент всех возможных пар, {Счетчик команд, Состояние системы}, воспроизводимых вашим набором тестов; все остальное - эвристика, которую, очевидно, вам сложно будет как-либо обосновать). Большинство выпускников бакалавриата смогут распознать проблему остановки (Halting Problem) в большинстве вариантов подобных задачах и поймут, что это невозможно.

1.7 Меньше - это больше или вы не шизофреник

Вот еще одна проблема, которая имеет особое отношение к первоначальному вопросу моего клиента. Наивный тестировщик пытается извлечь множество данных из результатов тестирования, при этом постоянно поддерживая все существующие тесты или даже добавляя новые; это приводит к точно такой же ситуации, в которой оказался мой клиент, когда сложность тестов (объемы кода или какие-только-хотите-метрики) начинает превосходить сложность исходного кода. Тестируемые классы - это код. Тесты - это код. Разработчики пишут код. Когда разработчики пишут код, они допускают около трех ошибок, непосредственно влияющих на систему, на каждые тысячу строк кода. Если бы мы случайным образом выбрали участки кода с подобными ошибками у моего клиента, включая тесты, то обнаружили бы, что в тестах содержиться код, который приводит к неправильным результатам чаще, чем реальный баг, останавливающий выполнение кода!

Некоторые мне говорят, что подобное не имеет к ним отношения, поскольку они уделяют значительно больше внимания тестам, чем исходному коду. Во-первых, это просто вздор. (Меня действительно смешат утверждающие, что, с одной стороны, они способны забывать о своих ранее сделанных предположениях во время создания изначального кода, и, с другой, те, кто может привнести свежий и независимый взгляд во время тестирования. Как первые, так и вторые должны быть шизофрениками.) Посмотрите, что делают разработчики при запуске тест-сьютов: они их запускают, но не думают (кстати, это же относится и к большей части Agile манифеста). На моей первой работе в Дании был проект, в значительной степени построенный на XP методологии и юнит тестировании. Я всячески пытался собрать билд на своей локальной машине, и после долгой борьбы с Maven и другими инструментами, наконец-то, мне это удалось. Каким же было разочарование, когда я обнаружил, что юнит-тесты не проходят. Пришлось обратиться к своим коллегам, которые сказали: О, так тебе нужно запустить Maven с вот этим флагом, он отключает вот эти тесты - из-за изменений эти тесты уже не работают, поэтому их необходимо отключить.

Если у вас 200, 2000, или 10 000 тестов, вы не будете тратить время на тщательное исследование и (кхе-кхе) рефакторинг каждого из них каждый раз, когда тест падает. Самая распространенная практика, которую я наблюдал, работая в стартапе еще в 2005 году, - это просто переписать результат старых тестов (ожидаемый результат или результаты вычислений такого теста) новыми результатами. С психологической перспективы, зеленый статус - это вознаграждение. Современные быстрые машины создают иллюзию возможности замены мышления программиста; их скорость намекает на исключение моей необходимости мыслить. Ведь, в любом же случае, если клиент сообщит об ошибке, я, в свою очередь, сформулирую гипотезу о ее действительной причине, внесу изменения, исправляющие поведение системы, и, в результате, с легкостью смогу себя убедить, что функция, в которую я добавил исправление, теперь работает правильно. То есть я просто переписываю результат выполнения этой функции. Однако, подобное - просто лженаука, основанная на колдовстве, связь с которым - причинность. В таком случае, необходимо повторно запустить все регрессионные и системные тесты.

Во-вторых, даже если бы правдой оказалось утверждение о том, что тесты гарантируют качество лучше, чем исходный код, из-за более совершенного процесса или повышенной к ним доли внимания, я бы посоветовал такой команде улучшить свой процесс за счет принятия пилюль, повышающих умственные способности именно во время создания кода, а не тестов.

1.8 Вы платите за поддержку тестов и качество!

Суть в том, что код - это часть вашей системной архитектуры. Тесты - это модули. Тот факт, что кто-то может не писать тесты, не освобождает его от ответственности заниматься проектированием и техническим обслуживанием возрастающего количества модулей. Одна из методик, которую часто путают с юнит-тестированием, но использующая последнее в качестве техники - это разработка через тестирование (TDD). Считается, что она улучшает метрики сцепления и связности (coupling and coherence), хотя, эмпирические данные свидетельствуют об обратном (одна из статей, опровергающих подобное представление на эмпирических основаниях принадлежит Янзену и Саледиану (Janzen and Saledian), Действительно ли разработка через тестирование улучшает качество проектирования программного обеспечения? IEEE Software 25(2), март/апрель 2008 г., стр. 77 - 84.) Еще хуже то, что таким образом, в качестве запланированного изменения, вы уже вводите связанность (coupling) между каждым модулем и сопровождающими их тестами. У вас появляется необходимость относиться к тестам так же как и к системным модулям. Даже если вы удаляете их перед релизом, это никак не сказывается на необходимости их обслуживать. (Подобное удаление может быть даже достаточно плохой идеей, но об этом дальше.)

Я обратил внимание, что подавляющая часть юнит-тестов, особенно созданных с помощью JUnit, представляет собой замаскированные утверждения (assertions). Когда же я создаю какое-нибудь восхитительное программное обеспечение, то засыпаю его подобными утверждениями, описывая ожидаемые мной результаты, и по отношению к тем, кто вызывает мои функции, и в отношении самих функций, которые обязаны что-то предоставить своим клиентам. Такие проверки дорабатываются в том же артефакте, где находится и остальная часть моего кода. Множество энвайерментов предоставляют средства административной стерилизации этих тестов во время релиза.

Более профессиональный подход - оставить эти асершены в коде даже после релиза, автоматически отправлять отчеты об ошибках от имени конечного пользователя и, возможно, пытаться перезапускать приложение каждый раз, когда подобная проверка провалена. В одном из ранее упомянутых стартапов, мой босс настаивал, чтобы мы такого не делали. Я указал, что отрицательный результат проверок означает: в программе что-то пошло совсем не так, и, скорее всего, данная программа выдаст неправильный результат. Даже мельчайшая ошибка в создаваемом нами продукте, может обойтись клиенту в 5 миллионов долларов дополнительных доработок. На что он ответил: Для компании намного важнее избегать видимости чего-то неправильно сделанного, чем останавливаться еще до получения ошибочных результатов. Я ушел из этой компании. Возможно, сегодня вы один из её клиентов.

Превратите юнит-тесты в утверждения (assertions). Используйте их для обеспечения отказоустойчивости архитектуры высокодоступных систем. Это решает проблему поддержки множества дополнительных программных модулей, которые оценивают ход выполнения (программы) и проверяют правильность (её) поведения; это одна часть юнит-тестирования. Другая - это драйвер, выполняющий сам код: в этом отношении, рассчитывайте, на свои стресс-, интеграционные и системные тесты.

Почти последнее, существуют такие юнит-тесты, которые повторяют системные, интеграционные или другие виды тестов. На заре вычислений, когда компьютеры были медленными, вместо того, чтобы дожидаться запуска системных тестов, юнит-тесты предоставляли разработчику более быструю обратную связь о том, сломало ли их изменение код. Сегодня, когда появились более дешевые и мощные компьютеры, этот аргумент кажется менее убедительным. Каждый раз, внося изменения в свое приложение Scrum Knowsy, я тестирую его на системном уровне. Разработчики должны непрерывно интегрироваться и, так же непрерывно проводить тестирование системы, а не сосредотачиваться на своих юнит-тестах и откладывать интеграцию, даже на час. Так что избавляйтесь от юнит-тестов, которые дублируют то, что уже делают системные тесты. Если системный уровень обходится слишком дорого, создайте наборы интеграционных тестов. Рекс (Rex) считает, что следующим большим скачком в тестировании будет разработка таких юнит, интеграционных и системных тестов, которые устраняют случайные упущения и дублирование.

Проверьте свои тестовые наборы на предмет репликации; вы же способны профинансировать это в рамках своих гибких (Lean) программ. В первую очередь, создавайте системные тесты с хорошим функциональным покрытием (а не покрытием кода) - помните, что правильная реакция на неправильные входные данные или другие непредвиденные условия является частью ваших функций.

Последнее: однажды я от кого-то услышал оправдание в необходимости создания (особого) юнит-теста, из-за невозможности проверить некий блок кода с помощью внешнего интерфейса инструмента для тестирования. Если учесть, что подобные интерфейсы хорошо спроектированы и способны воспроизводить различное, наблюдаемое в реальном мире, поведение программы, и вы нашли такой код, к которому не достучаться из вашей тестировочной системы, тогда... удалите этот код! Серьезно, размышление о коде с позиции системных тестов может быть отличным способом для обнаружения мертвого кода. Это даже более ценно, чем поиск ненужных тестов.

1.9 Это процесс, глупец или лихорадка зеленого статуса

Возможно, самая серьезная проблема юнит-тестов заключается в том, что они нацелены на исправление ошибок, а не улучшение системы. Я довольно-таки часто встречал программистов, которые, склонив головы, пытались добиться успешного прохождения и, тем самым, повысить зеленые показатели. Тестировщик формулирует гипотезу, но, находясь в своей изолированной среде, он не может с легкостью получить достаточно информации, чтобы ее можно было подтвердить или опровергнуть. Поэтому он или она просто пытаются сделать хотя бы что-то, дабы понять, приближает ли это вас к зеленому статусу - и насколько это вообще возможно.

Тестирование преследует две потенциальные цели. Одна из них - обучение: для большего ознакомления с программой и тем, как она работает. Вторая - использовать его как оракул. Обычно, люди сталкиваются с ошибками во время обращения именно к этой цели: тест - это оракул, а цель - правильный результат. Они упускают из виду тот факт, что цель определяет обширное понимание, а понимание дает ключ к исправлению ошибки.

Именно поэтому необходимо ненадолго отрываться от терминала. Так вы уходите от рефлекса Павлова завязанного на появлении зеленого статуса и, основываясь на тестах, можете начать выстраивать свое общее понимание шаг за шагом. Когда его будет достаточно, у вас появится костяк общей картины. Если и он будет достаточно полным, ошибка станет очевидной.

Системные тесты почти сразу же погружают вас в подобную позицию размышления. Разумеется, вам все еще нужна будет более подробная информация, для этого на помощь приходит отладка (debugging). Отладка - это использование инструментов и устройств, помогающих изолировать ошибку. Отладка - не тестирование. Во-первых, она представляет собой ad-hoc (интуитивную активность) и, во-вторых, выполняется на основании последовательного перехода от ошибки к ошибке. Юнит тесты могут быть полезным инструментом отладки. На собственном опыте я обнаружил, что лучше всего работает комбинация различных инструментов, среди которых наиболее эффективными являются наборы с невалидными данными и доступ к глобальному контексту, включающий все значения данных и случайную трассировку стека.

1.10 Подводим итоги

Вернемся к моему клиенту из компании Sogeti. Вначале, я упоминал его высказывание:

Когда я ежедневно программировал, то создавал действительно тестируемый код, однако почти никогда не писал никакие юнит тесты. При этом я заслуживал признание за свой качественный код и почти безошибочное программное обеспечение. Мне хотелось бы разобраться, ПОЧЕМУ такой вариант работал в моем случае?

Возможно, Ричард - это один из тех редких людей, которые знают, как думать самому, вместо того, чтобы позволять компьютеру думать за него - будь то проектирование системы или дизайн более низкого уровня. Я чаще наблюдаю подобную ситуацию в Восточноевропейских странах, где отсутствие общедоступного компьютерного оборудования заставляло людей мыслить. Просто не хватало компьютеров. Когда я впервые посетил Сербию в 2004 году, студенты из ФОН (факультета информатики) могли получить доступ к компьютеру для выхода в интернет один раз в неделю. Расплата за ошибку высока: если запуск вашего кода не отработает, придется ждать еще неделю, чтобы повторить свою попытку.

К счастью, я вырос именно в такой культуре программирования, мой код записывался на перфокартах, которые отдавались оператору для установки в очередь машины, а затем, через сутки, собирались результаты. Такой формат действительно заставлял вас или же задуматься - или же, потерпеть неудачу. У Ричарда из Sogeti было аналогичное воспитание: у них была неделя на подготовку кода и всего один час на его запуск. Всё должно было делаться правильно с первого раза. В любом случае, обдуманный проект должен оценивать возможные риски, связанные с затратами, и устранять их по одному в каждой итерации, уделяя особое внимание постоянно растущей ценности. Одна из моих любимых циничных цитат: Я считаю, что недели программирования и тестирования могут сэкономить мне часы планирования. Что меня больше всего беспокоит в культуре раннего провала (fail-fast), так это не столько понятие провала, сколько слово раннее. Много лет назад мой босс Нил Халлер мне сказал, что отладка - это не то, что вы делаете, сидя перед своей программой с отладчиком; это то, что вы делаете, откинувшись на спинку стула и глядя в потолок, или обсуждение ошибки с командой. Однако многие, якобы ярые приверженцы эджайл методологий, ставят процессы и JUnit выше людей и взаимодействий.

Лучший пример, услышанный мной в прошлом году, был от моей коллеги, Нэнси Гитинджи (Nancy Githinji), управлявшей вместе со своим мужем IT-компанией в Кении; сейчас они оба работают в Microsoft. Последний раз, посещая свой дом (в прошлом году), она познакомилась с детьми, которые проживают в джунглях и пишут программы. Они могут приезжать раз в месяц в город, чтобы получить доступ к компьютеру и апробировать свой код. Я хочу нанять этих детей!

Мне, как стороннику эджайла (да и просто из принципа), немного больно признавать, что Рекс оказался прав, как, впрочем-то это было и ранее , достаточно красноречиво сказав: В этой культуре раннего провала (fail fast) есть нечто небрежное, она побуждает швырнуть кучу спагетти на стену, особо даже не задумываясь отчасти, из-за чрезмерной уверенности в заниженных рисках, предоставляемых юнит-тестами. Культура раннего провала может хорошо работать при очень высокой дисциплине, подкрепленной здоровым скептицизмом, однако редко можно встретить такое отношение в динамичном IT-бизнесе. Иногда ошибки требуют обдумывания, а последнее требует больше времени, чем результаты, достигаемые ранним провалом. Как только что напомнила моя жена Гертруда: Никто не хочет, чтобы ошибки затягивались на долго

Если вы наймете профессионального менеджера или консультанта по тестированию, он сможет помочь вам разобраться в вопросах более масштабной картины тестирования: интеграционным и системным тестированием, а также подходящим для них инструментарием и процессами. Это важно. Но не забывайте о позиции продакт овнера, бизнес-аналитика или менеджера в Scrum: управление рисками находится в центре их деятельности, и, возможно, именно поэтому Джефф Сазерленд говорит, что заказчик должен разрабатывать (а в лучшем случае и проектировать) системные тесты для использования в качестве входных данных до или во время планирования спринта.

Что касается Интернета: грустно и откровенно страшно, что там так много всего. Много советов, но очень мало из них подкреплено теорией, данными или даже моделью того, почему необходимо довериться тому или иному совету. Хорошее тестирование немыслимо без скептицизма. Относитесь скептически к себе: измеряйте, доказывайте, делайте повторные попытки. Ради всего святого, отнесись ко мне скептически.

Пишите мне свои комментарии на jcoplien@gmail.com с копией Рексу вначале этого письма.

В заключение:

  • Сохраняйте регрессионные тесты до года, большинство из них должны быть тестами системного уровня, а не юнит-тестами.

  • Оставляйте юнит-тесты, проверяющие ключевые алгоритмы, для которых существует широкий, формальный, независимый оракул правильности и, которому можно приписать бизнес-ценность.

  • Исключая предыдущее заявление, если у X есть определенная бизнес-ценность и вы можете протестировать X системным или же юнит-тестом, используйте системный: контекст - это всё.

  • Разрабатывайте тест более тщательно, чем код.

  • Превратите большинство юнит-тестов в утверждения (assertions).

  • Удалите тесты, которые за год ни разу не падали.

  • Тестирование не способно заменить хорошую разработку: высокий показатель падающих тестов говорит о том, что вам следует сократить интервалы разработки, возможно, радикально, а также убедиться, что у вашей архитектуры и дизайна есть зубы.

  • Если вы обнаруживаете, что определенные тестируемые функции тривиальны, еще раз перепроверьте способы стимуляции производительности разработчиков. Одобрение за покрытие или другие бессмысленные метрики могут привести к быстрому разрушению архитектуры.

  • Будьте скромны в отношении способностей тестов. Тесты не улучшают качество: это делают разработчики.

Подробнее..

Apache Airflow делаем ETL проще

27.07.2020 12:16:39 | Автор: admin

Привет, я Дмитрий Логвиненко Data Engineer отдела аналитики группы компаний Везёт.


Я расскажу вам о замечательном инструменте для разработки ETL-процессов Apache Airflow. Но Airflow настолько универсален и многогранен, что вам стоит присмотреться к нему даже если вы не занимаетесь потоками данных, а имеете потребность периодически запускать какие-либо процессы и следить за их выполнением.


И да, я буду не только рассказывать, но и показывать: в программе много кода, скриншотов и рекомендаций.



Что обычно видишь, когда гуглишь слово Airflow / Wikimedia Commons


Введение


Apache Airflow он прямо как Django:


  • написан на Python,
  • есть отличная админка,
  • неограниченно расширяем,

только лучше, да и сделан совсем для других целей, а именно (как написано до ката):


  • запуск и мониторинг задач на неограниченном количестве машин (сколько вам позволит Celery/Kubernetes и ваша совесть)
  • с динамической генерацией workflow из очень легкого для написания и восприятия Python-кода
  • и возможностью связывать друг с друг любые базы данных и API с помощью как готовых компонентов, так и самодельных плагинов (что делается чрезвычайно просто).

Мы используем Apache Airflow так:


  • собираем данные из различных источников (множество инстансов SQL Server и PostgreSQL, различные API с метриками приложений, даже 1С) в DWH и ODS (у нас это Vertica и Clickhouse).
  • как продвинутый cron, который запускает процессы консолидации данных на ODS, а также следит за их обслуживанием.

До недавнего времени наши потребности покрывал один небольшой сервер на 32 ядрах и 50 GB оперативки. В Airflow при этом работает:


  • более 200 дагов (собственно workflows, в которые мы набили задачки),
  • в каждом в среднем по 70 тасков,
  • запускается это добро (тоже в среднем) раз в час.

А о том, как мы расширялись, я напишу ниже, а сейчас давайте определим ber-задачу, которую мы будем решать:


Есть три исходных SQL Serverа, на каждом по 50 баз данных инстансов одного проекта, соответственно, структура у них одинаковая (почти везде, муа-ха-ха), а значит в каждой есть таблица Orders (благо таблицу с таким названием можно затолкать в любой бизнес). Мы забираем данные, добавляя служебные поля (сервер-источник, база-источник, идентификатор ETL-задачи) и наивным образом бросим их в, скажем, Vertica.

Поехали!


Часть основная, практическая (и немного теоретическая)


Зачем оно нам (и вам)


Когда деревья были большими, а я был простым SQL-щиком в одном российском ритейле, мы шпарили ETL-процессы aka потоки данных с помощью двух доступных нам средств:


  • Informatica Power Center крайне развесистая система, чрезвычайно производительная, со своими железками, собственным версионированием. Использовал я дай бог 1% её возможностей. Почему? Ну, во-первых, этот интерфейс где-то из нулевых психически давил на нас. Во-вторых, эта штуковина заточена под чрезвычайно навороченные процессы, яростное переиспользование компонентов и другие очень-важные-энтерпрайз-фишечки. Про то что стоит она, как крыло Airbus A380/год, мы промолчим.


    Осторожно, скриншот может сделать людям младше 30 немного больно




  • SQL Server Integration Server этим товарищем мы пользовались в своих внутрипроектных потоках. Ну а в самом деле: SQL Server мы уже используем, и не юзать его ETL-тулзы было бы как-то неразумно. Всё в нём в хорошо: и интерфейс красивый, и отчётики выполнения Но не за это мы любим программные продукты, ох не за это. Версионировать его dtsx (который представляет собой XML с перемешивающимися при сохранении нодами) мы можем, а толку? А сделать пакет тасков, который перетащит сотню таблиц с одного сервера на другой? Да что сотню, у вас от двадцати штук отвалится указательный палец, щёлкающий по мышиной кнопке. Но выглядит он, определенно, более модно:




Мы безусловно искали выходы. Дело даже почти дошло до самописного генератора SSIS-пакетов...


а потом меня нашла новая работа. А на ней меня настиг Apache Airflow.


Когда я узнал, что описания ETL-процессов это простой Python-код, я только что не плясал от радости. Вот так потоки данных подверглись версионированию и диффу, а ссыпать таблицы с единой структурой из сотни баз данных в один таргет стало делом Python-кода в полтора-два 13 экрана.


Собираем кластер


Давайте не устраивать совсем уж детский сад, и не говорить тут о совершенно очевидных вещах, вроде установки Airflow, выбранной вами БД, Celery и других дел, описанных в доках.


Чтобы мы могли сразу приступить к экспериментам, я набросал docker-compose.yml в котором:


  • Поднимем собственно Airflow: Scheduler, Webserver. Там же будет крутится Flower для мониторинга Celery-задач (потому что его уже затолкали в apache/airflow:1.10.10-python3.7, а мы и не против);
  • PostgreSQL, в который Airflow будет писать свою служебную информацию (данные планировщика, статистика выполнения и т. д.), а Celery отмечать завершенные таски;
  • Redis, который будет выступать брокером задач для Celery;
  • Celery worker, который и займется непосредственным выполнением задачек.
  • В папку ./dags мы будет складывать наши файлы с описанием дагов. Они будут подхватываться на лету, поэтому передёргивать весь стек после каждого чиха не нужно.

Кое-где код в примерах приведен не полностью (чтобы не загромождать текст), а где-то он модифицируется в процессе. Цельные работающие примеры кода можно посмотреть в репозитории https://github.com/dm-logv/airflow-tutorial.

docker-compose.yml
version: '3.4'x-airflow-config: &airflow-config  AIRFLOW__CORE__DAGS_FOLDER: /dags  AIRFLOW__CORE__EXECUTOR: CeleryExecutor  AIRFLOW__CORE__FERNET_KEY: MJNz36Q8222VOQhBOmBROFrmeSxNOgTCMaVp2_HOtE0=  AIRFLOW__CORE__HOSTNAME_CALLABLE: airflow.utils.net:get_host_ip_address  AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgres+psycopg2://airflow:airflow@airflow-db:5432/airflow  AIRFLOW__CORE__PARALLELISM: 128  AIRFLOW__CORE__DAG_CONCURRENCY: 16  AIRFLOW__CORE__MAX_ACTIVE_RUNS_PER_DAG: 4  AIRFLOW__CORE__LOAD_EXAMPLES: 'False'  AIRFLOW__CORE__LOAD_DEFAULT_CONNECTIONS: 'False'  AIRFLOW__EMAIL__DEFAULT_EMAIL_ON_RETRY: 'False'  AIRFLOW__EMAIL__DEFAULT_EMAIL_ON_FAILURE: 'False'  AIRFLOW__CELERY__BROKER_URL: redis://broker:6379/0  AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow:airflow@airflow-db/airflowx-airflow-base: &airflow-base  image: apache/airflow:1.10.10-python3.7  entrypoint: /bin/bash  restart: always  volumes:    - ./dags:/dags    - ./requirements.txt:/requirements.txtservices:  # Redis as a Celery broker  broker:    image: redis:6.0.5-alpine  # DB for the Airflow metadata  airflow-db:    image: postgres:10.13-alpine    environment:      - POSTGRES_USER=airflow      - POSTGRES_PASSWORD=airflow      - POSTGRES_DB=airflow    volumes:      - ./db:/var/lib/postgresql/data  # Main container with Airflow Webserver, Scheduler, Celery Flower  airflow:    <<: *airflow-base    environment:      <<: *airflow-config      AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL: 30      AIRFLOW__SCHEDULER__CATCHUP_BY_DEFAULT: 'False'      AIRFLOW__SCHEDULER__MAX_THREADS: 8      AIRFLOW__WEBSERVER__LOG_FETCH_TIMEOUT_SEC: 10    depends_on:      - airflow-db      - broker    command: >      -c " sleep 10 &&           pip install --user -r /requirements.txt &&           /entrypoint initdb &&          (/entrypoint webserver &) &&          (/entrypoint flower &) &&           /entrypoint scheduler"    ports:      # Celery Flower      - 5555:5555      # Airflow Webserver      - 8080:8080  # Celery worker, will be scaled using `--scale=n`  worker:    <<: *airflow-base    environment:      <<: *airflow-config    command: >      -c " sleep 10 &&           pip install --user -r /requirements.txt &&           /entrypoint worker"    depends_on:      - airflow      - airflow-db      - broker

Примечания:


  • В сборке композа я во многом опирался на известный образ puckel/docker-airflow обязательно посмотрите. Может, вам в жизни больше ничего и не понадобится.
  • Все настройки Airflow доступны не только через airflow.cfg, но и через переменные среды (слава разработчикам), чем я злостно воспользовался.
  • Естественно, он не production-ready: я намеренно не ставил heartbeats на контейнеры, не заморачивался с безопасностью. Но минимум, подходящий для наших экспериментиков я сделал.
  • Обратите внимание, что:
    • Папка с дагами должна быть доступна как планировщику, так и воркерам.
    • То же самое касается и всех сторонних библиотек они все должны быть установлены на машины с шедулером и воркерами.

Ну а теперь просто:


$ docker-compose up --scale worker=3

После того, как всё поднимется, можно смотреть на веб-интерфейсы:



Основные понятия


Если вы ничего не поняли во всех этих дагах, то вот краткий словарик:


  • Scheduler самый главный дядька в Airflow, контролирующий, чтобы вкалывали роботы, а не человек: следит за расписанием, обновляет даги, запускает таски.


    Вообще, в старых версиях, у него были проблемы с памятью (нет, не амнезия, а утечки) и в конфигах даже остался легаси-параметр run_duration интервал его перезапуска. Но сейчас всё хорошо.


  • DAG (он же даг) направленный ацикличный граф, но такое определение мало кому что скажет, а по сути это контейнер для взаимодействующих друг с другом тасков (см. ниже) или аналог Package в SSIS и Workflow в Informatica.


    Помимо дагов еще могут быть сабдаги, но мы до них скорее всего не доберёмся.


  • DAG Run инициализированный даг, которому присвоен свой execution_date. Даграны одного дага могут вполне работать параллельно (если вы, конечно, сделали свои таски идемпотентными).


  • Operator это кусочки кода, ответственные за выполнение какого-либо конкретного действия. Есть три типа операторов:


    • action, как например наш любимый PythonOperator, который в силах выполнить любой (валидный) Python-код;
    • transfer, которые перевозят данные с места на место, скажем, MsSqlToHiveTransfer;
    • sensor же позволит реагировать или притормозить дальнейшее выполнение дага до наступления какого-либо события. HttpSensor может дергать указанный эндпойнт, и когда дождется нужный ответ, запустить трансфер GoogleCloudStorageToS3Operator. Пытливый ум спросит: зачем? Ведь можно делать повторы прямо в операторе! А затем, чтобы не забивать пул тасков подвисшими операторами. Сенсор запускается, проверяет и умирает до следующей попытки.

  • Task объявленные операторы вне зависимости от типа и прикрепленные к дагу повышаются до чина таска.


  • Task instance когда генерал-планировщик решил, что таски пора отправлять в бой на исполнители-воркеры (прямо на месте, если мы используем LocalExecutor или на удалённую ноду в случае с CeleryExecutor), он назначает им контекст (т. е. комплект переменных параметров выполнения), разворачивает шаблоны команд или запросов и складывает их в пул.



Генерируем таски


Сперва обозначим общую схему нашего дага, а затем будем всё больше и больше погружаться в детали, потому что мы применяем некоторые нетривиальные решения.


Итак, в простейшем виде подобный даг будет выглядеть так:


from datetime import timedelta, datetimefrom airflow import DAGfrom airflow.operators.python_operator import PythonOperatorfrom commons.datasources import sql_server_dsdag = DAG('orders',          schedule_interval=timedelta(hours=6),          start_date=datetime(2020, 7, 8, 0))def workflow(**context):    print(context)for conn_id, schema in sql_server_ds:    PythonOperator(        task_id=schema,        python_callable=workflow,        provide_context=True,        dag=dag)

Давайте разбираться:


  • Сперва импортируем нужные либы и кое что ещё;
  • sql_server_ds это List[namedtuple[str, str]] с именами коннектов из Airflow Connections и базами данных из которых мы будем забирать нашу табличку;
  • dag объявление нашего дага, которое обязательно должно лежать в globals(), иначе Airflow его не найдет. Дагу также нужно сказать:
    • что его зовут orders это имя потом будет маячить в веб-интерфейсе,
    • что работать он будет, начиная с полуночи восьмого июля,
    • а запускать он должен, примерно каждые 6 часов (для крутых парней здесь вместо timedelta() допустима cron-строка 0 0 0/6 ? * * *, для менее крутых выражение вроде @daily);
  • workflow() будет делать основную работу, но не сейчас. Сейчас мы просто высыпем наш контекст в лог.
  • А теперь простая магия создания тасков:
    • пробегаем по нашим источникам;
    • инициализируем PythonOperator, который будет выполнять нашу пустышку workflow(). Не забывайте указывать уникальное (в рамках дага) имя таска и подвязывать сам даг. Флаг provide_context в свою очередь насыпет в функцию дополнительных аргументов, которые мы бережно соберём с помощью **context.

Пока на этом всё. Что мы получили:


  • новый даг в веб-интерфейсе,
  • полторы сотни тасков, которые будут выполняться параллельно (если то позволят настройки Airflow, Celery и мощности серверов).

Ну, почти получили.



Зависимости кто будет ставить?


Чтобы всё это дело упростить я вкорячил в docker-compose.yml обработку requirements.txt на всех нодах.


Вот теперь понеслась:



Серые квадратики task instances, обработанные планировщиком.


Немного ждем, задачи расхватывают воркеры:



Зеленые, понятное дело, успешно отработавшие. Красные не очень успешно.


Кстати, на нашем проде никакой папки ./dags, синхронизирующейся между машинами нет всё даги лежат в git на нашем Gitlab, а Gitlab CI раскладывает обновления на машины при мёрдже в master.

Немного о Flower


Пока воркеры молотят наши тасочки-пустышки, вспомним про еще один инструмент, который может нам кое-что показать Flower.


Самая первая страничка с суммарной информацией по нодам-воркерам:



Самая насыщенная страничка с задачами, отправившимися в работу:



Самая скучная страничка с состоянием нашего брокера:



Самая яркая страничка с графиками состояния тасков и их временем выполнения:



Догружаем недогруженное


Итак, все таски отработали, можно уносить раненых.



А раненых оказалось немало по тем или иным причинами. В случае правильного использования Airflow вот эти самые квадраты говорят о том, что данные определенно не доехали.


Нужно смотреть лог и перезапускать упавшие task instances.


Жмякнув на любой квадрат, увидим доступные нам действия:



Можно взять, и сделать Clear упавшему. То есть, мы забываем о том, что там что-то завалилось, и тот же самый инстанс таска уйдет планировщику.



Понятно, что делать так мышкой со всеми красными квадратами не очень гуманно не этого мы ждем от Airflow. Естественно, у нас есть оружие массового поражения: Browse/Task Instances



Выберем всё разом и обнулим нажмем правильный пункт:



После очистки наши такси выглядят так (они уже ждут не дождутся, когда шедулер их запланирует):



Соединения, хуки и прочие переменные


Самое время посмотреть на следующий DAG, update_reports.py:


from collections import namedtuplefrom datetime import datetime, timedeltafrom textwrap import dedentfrom airflow import DAGfrom airflow.contrib.operators.vertica_operator import VerticaOperatorfrom airflow.operators.email_operator import EmailOperatorfrom airflow.utils.trigger_rule import TriggerRulefrom commons.operators import TelegramBotSendMessagedag = DAG('update_reports',          start_date=datetime(2020, 6, 7, 6),          schedule_interval=timedelta(days=1),          default_args={'retries': 3, 'retry_delay': timedelta(seconds=10)})Report = namedtuple('Report', 'source target')reports = [Report(f'{table}_view', table) for table in [    'reports.city_orders',    'reports.client_calls',    'reports.client_rates',    'reports.daily_orders',    'reports.order_duration']]email = EmailOperator(    task_id='email_success', dag=dag,    to='{{ var.value.all_the_kings_men }}',    subject='DWH Reports updated',    html_content=dedent("""Господа хорошие, отчеты обновлены"""),    trigger_rule=TriggerRule.ALL_SUCCESS)tg = TelegramBotSendMessage(    task_id='telegram_fail', dag=dag,    tg_bot_conn_id='tg_main',    chat_id='{{ var.value.failures_chat }}',    message=dedent("""\         Наташ, просыпайся, мы {{ dag.dag_id }} уронили        """),    trigger_rule=TriggerRule.ONE_FAILED)for source, target in reports:    queries = [f"TRUNCATE TABLE {target}",               f"INSERT INTO {target} SELECT * FROM {source}"]    report_update = VerticaOperator(        task_id=target.replace('reports.', ''),        sql=queries, vertica_conn_id='dwh',        task_concurrency=1, dag=dag)    report_update >> [email, tg]

Все ведь когда-нибудь делали обновлялку отчетов? Это снова она: есть список источников, откуда забрать данные; есть список, куда положить; не забываем посигналить, когда всё случилось или сломалось (ну это не про нас, нет).


Давайте снова пройдемся по файлу и посмотрим на новые непонятные штуки:


  • from commons.operators import TelegramBotSendMessage нам ничто не мешает делать свои операторы, чем мы и воспользовались, сделав небольшую обёрточку для отправки сообщений в Разблокированный. (Об этом операторе мы еще поговорим ниже);
  • default_args={} даг может раздавать одни и те же аргументы всем своим операторам;
  • to='{{ var.value.all_the_kings_men }}' поле to у нас будет не захардкоженным, а формируемым динамически с помощью Jinja и переменной со списком email-ов, которую я заботливо положил в Admin/Variables;
  • trigger_rule=TriggerRule.ALL_SUCCESS условие запуска оператора. В нашем случае, письмо полетит боссам только если все зависимости отработали успешно;
  • tg_bot_conn_id='tg_main' аргументы conn_id принимают в себя идентификаторы соединений, которые мы создаем в Admin/Connections;
  • trigger_rule=TriggerRule.ONE_FAILED сообщения в Telegram улетят только при наличии упавших тасков;
  • task_concurrency=1 запрещаем одновременный запуск нескольких task instances одного таска. В противном случае, мы получим одновременный запуск нескольких VerticaOperator (смотрящих на одну таблицу);
  • report_update >> [email, tg] все VerticaOperator сойдутся в отправке письма и сообщения, вот так:


    Но так как у операторов-нотификаторов стоят разные условия запуска, работать будет только один. В Tree View всё выглядит несколько менее наглядно:



Скажу пару слов о макросах и их друзьях переменных.


Макросы это Jinja-плейсхолдеры, которые могут подставлять разную полезную информацию в аргументы операторов. Например, так:


SELECT    id,    payment_dtm,    payment_type,    client_idFROM orders.paymentsWHERE    payment_dtm::DATE = '{{ ds }}'::DATE

{{ ds }} развернется в содержимое переменной контекста execution_date в формате YYYY-MM-DD: 2020-07-14. Самое приятное, что переменные контекста прибиваются гвоздями к определенному инстансу таска (квадратику в Tree View), и при перезапуске плейсхолдеры раскроются в те же самые значения.


Присвоенные значения можно смотреть с помощью кнопки Rendered на каждом таск-инстансе. Вот так у таска с отправкой письма:



А так у таски с отправкой сообщения:



Полный список встроенных макросов для последней доступной версии доступен здесь: Macros Reference


Более того, с помощью плагинов, мы можем объявлять собственные макросы, но это уже совсем другая история.


Помимо предопределенных штук, мы можем подставлять значения своих переменных (выше в коде я уже этим воспользовался). Создадим в Admin/Variables пару штук:



Всё, можно пользоваться:


TelegramBotSendMessage(chat_id='{{ var.value.failures_chat }}')

В значении может быть скаляр, а может лежать и JSON. В случае JSON-а:


bot_config{    "bot": {        "token": 881hskdfASDA16641,        "name": "Verter"    },    "service": "TG"}

просто используем путь к нужному ключу: {{ var.json.bot_config.bot.token }}.


Скажу буквально одно слово и покажу один скриншот про соединения. Тут всё элементарно: на странице Admin/Connections создаем соединение, складываем туда наши логины/пароли и более специфичные параметры. Вот так:



Пароли можно шифровать (более тщательно, чем в варианте по умолчанию), а можно не указывать тип соединения (как я сделал для tg_main) дело в том, что список типов зашит в моделях Airflow и расширению без влезания в исходники не поддается (если вдруг я чего-то не догуглил прошу меня поправить), но получить креды просто по имени нам ничто не помешает.


А еще можно сделать несколько соединений с одним именем: в таком случае метод BaseHook.get_connection(), который достает нам соединения по имени, будет отдавать случайного из нескольких тёзок (было бы логичнее сделать Round Robin, но оставим это на совести разработчиков Airflow).


Variables и Connections, безусловно, классные средства, но важно не потерять баланс: какие части ваших потоков вы храните собственно в коде, а какие отдаете на хранение Airflow. C одной стороны быстро поменять значение, например, ящик рассылки, может быть удобно через UI. А с другой это всё-таки возврат к мышеклику, от которого мы (я) хотели избавиться.

Работа с соединениями это одна из задач хуков. Вообще хуки Airflow это точки подключения его к сторонним сервисам и библиотекам. К примеру, JiraHook откроет для нас клиент для взаимодействия с Jira (можно задачки подвигать туда-сюда), а с помощью SambaHook можно запушить локальный файл на smb-точку.


Разбираем кастомный оператор


И мы вплотную подобрались к тому, чтобы посмотреть на то, как сделан TelegramBotSendMessage


Код commons/operators.py с собственно оператором:


from typing import Unionfrom airflow.operators import BaseOperatorfrom commons.hooks import TelegramBotHook, TelegramBotclass TelegramBotSendMessage(BaseOperator):    """Send message to chat_id using TelegramBotHook    Example:        >>> TelegramBotSendMessage(        ...     task_id='telegram_fail', dag=dag,        ...     tg_bot_conn_id='tg_bot_default',        ...     chat_id='{{ var.value.all_the_young_dudes_chat }}',        ...     message='{{ dag.dag_id }} failed :(',        ...     trigger_rule=TriggerRule.ONE_FAILED)    """    template_fields = ['chat_id', 'message']    def __init__(self,                 chat_id: Union[int, str],                 message: str,                 tg_bot_conn_id: str = 'tg_bot_default',                 *args, **kwargs):        super().__init__(*args, **kwargs)        self._hook = TelegramBotHook(tg_bot_conn_id)        self.client: TelegramBot = self._hook.client        self.chat_id = chat_id        self.message = message    def execute(self, context):        print(f'Send "{self.message}" to the chat {self.chat_id}')        self.client.send_message(chat_id=self.chat_id,                                 message=self.message)

Здесь, как и остальное в Airflow, всё очень просто:


  • Отнаследовались от BaseOperator, который реализует довольно много Airflow-специфичных штук (посмотрите на досуге)
  • Объявили поля template_fields, в которых Jinja будет искать макросы для обработки.
  • Организовали правильные аргументы для __init__(), расставили умолчания, где надо.
  • Об инициализации предка тоже не забыли.
  • Открыли соответствующий хук TelegramBotHook, получили от него объект-клиент.
  • Оверрайднули (переопределили) метод BaseOperator.execute(), который Airfow будет подергивать, когда наступит время запускать оператор в нем мы и реализуем основное действие, на забыв залогироваться. (Логируемся, кстати, прямо в stdout и stderr Airflow всё перехватит, красиво обернет, разложит, куда надо.)

Давайте смотреть, что у нас в commons/hooks.py. Первая часть файлика, с самим хуком:


from typing import Unionfrom airflow.hooks.base_hook import BaseHookfrom requests_toolbelt.sessions import BaseUrlSessionclass TelegramBotHook(BaseHook):    """Telegram Bot API hook    Note: add a connection with empty Conn Type and don't forget    to fill Extra:        {"bot_token": "YOuRAwEsomeBOtToKen"}    """    def __init__(self,                 tg_bot_conn_id='tg_bot_default'):        super().__init__(tg_bot_conn_id)        self.tg_bot_conn_id = tg_bot_conn_id        self.tg_bot_token = None        self.client = None        self.get_conn()    def get_conn(self):        extra = self.get_connection(self.tg_bot_conn_id).extra_dejson        self.tg_bot_token = extra['bot_token']        self.client = TelegramBot(self.tg_bot_token)        return self.client

Я даже не знаю, что тут можно объяснять, просто отмечу важные моменты:


  • Наследуемся, думаем над аргументами в большинстве случаев он будет один: conn_id;
  • Переопределяем стандартные методы: я ограничился get_conn(), в котором я получаю параметры соединения по имени и всего-навсего достаю секцию extra (это поле для JSON), в которую я (по своей же инструкции!) положил токен Telegram-бота: {"bot_token": "YOuRAwEsomeBOtToKen"}.
  • Создаю экземпляр нашего TelegramBot, отдавая ему уже конкретный токен.

Вот и всё. Получить клиент из хука можно c помощью TelegramBotHook().clent или TelegramBotHook().get_conn().


И вторая часть файлика, в котором я сделать микрообёрточку для Telegram REST API, чтобы не тащить тот же python-telegram-bot ради одного метода sendMessage.


class TelegramBot:    """Telegram Bot API wrapper    Examples:        >>> TelegramBot('YOuRAwEsomeBOtToKen', '@myprettydebugchat').send_message('Hi, darling')        >>> TelegramBot('YOuRAwEsomeBOtToKen').send_message('Hi, darling', chat_id=-1762374628374)    """    API_ENDPOINT = 'https://api.telegram.org/bot{}/'    def __init__(self, tg_bot_token: str, chat_id: Union[int, str] = None):        self._base_url = TelegramBot.API_ENDPOINT.format(tg_bot_token)        self.session = BaseUrlSession(self._base_url)        self.chat_id = chat_id    def send_message(self, message: str, chat_id: Union[int, str] = None):        method = 'sendMessage'        payload = {'chat_id': chat_id or self.chat_id,                   'text': message,                   'parse_mode': 'MarkdownV2'}        response = self.session.post(method, data=payload).json()        if not response.get('ok'):            raise TelegramBotException(response)class TelegramBotException(Exception):    def __init__(self, *args, **kwargs):        super().__init__((args, kwargs))

Правильный путь сложить всё это: TelegramBotSendMessage, TelegramBotHook, TelegramBot в плагин, положить в общедоступный репозиторий, и отдать в Open Source.

Пока мы всё это изучали, наши обновления отчетов успели успешно завалиться и отправить мне в канал сообщение об ошибке. Пойду проверять, что опять не так...



В нашем даге что-то сломалось! А ни этого ли мы ждали? Именно!


Наливать-то будешь?


Чувствуете, что-то я пропустил? Вроде бы обещал данные из SQL Server в Vertica переливать, и тут взял и съехал с темы, негодяй!


Злодеяние это было намеренным, я просто обязан был расшифровать вам кое-какую терминологию. Теперь можно ехать дальше.


План у нас был такой:


  1. Сделать даг
  2. Нагенерить таски
  3. Посмотреть, как всё красиво
  4. Присваивать заливкам номера сессий
  5. Забрать данные из SQL Server
  6. Положить данные в Vertica
  7. Собрать статистику

Итак, чтобы всё это запустить, я сделал маленькое дополнение к нашему docker-compose.yml:


docker-compose.db.yml
version: '3.4'x-mssql-base: &mssql-base  image: mcr.microsoft.com/mssql/server:2017-CU21-ubuntu-16.04  restart: always  environment:    ACCEPT_EULA: Y    MSSQL_PID: Express    SA_PASSWORD: SayThanksToSatiaAt2020    MSSQL_MEMORY_LIMIT_MB: 1024services:  dwh:    image: jbfavre/vertica:9.2.0-7_ubuntu-16.04  mssql_0:    <<: *mssql-base  mssql_1:    <<: *mssql-base  mssql_2:    <<: *mssql-base  mssql_init:    image: mio101/py3-sql-db-client-base    command: python3 ./mssql_init.py    depends_on:      - mssql_0      - mssql_1      - mssql_2    environment:      SA_PASSWORD: SayThanksToSatiaAt2020    volumes:      - ./mssql_init.py:/mssql_init.py      - ./dags/commons/datasources.py:/commons/datasources.py

Там мы поднимаем:


  • Vertica как хост dwh с самыми дефолтными настройками,
  • три экземпляра SQL Server,
  • наполняем базы в последних кое-какими данными (ни в коем случае не заглядывайте в mssql_init.py!)

Запускаем всё добро с помощью чуть более сложной, чем в прошлый раз, команды:


$ docker-compose -f docker-compose.yml -f docker-compose.db.yml up --scale worker=3

Что нагенерировал наш чудорандомайзер, можно, воспользовавшись пунктом Data Profiling/Ad Hoc Query:



Главное, не показывать это аналитикам


Подробно останавливаться на ETL-сессиях я не буду, там всё тривиально: делаем базу, в ней табличку, оборачиваем всё менеджером контекста, и теперь делаем так:


with Session(task_name) as session:    print('Load', session.id, 'started')    # Load worflow    ...    session.successful = True    session.loaded_rows = 15

session.py
from sys import stderrclass Session:    """ETL workflow session    Example:        with Session(task_name) as session:            print(session.id)            session.successful = True            session.loaded_rows = 15            session.comment = 'Well done'    """    def __init__(self, connection, task_name):        self.connection = connection        self.connection.autocommit = True        self._task_name = task_name        self._id = None        self.loaded_rows = None        self.successful = None        self.comment = None    def __enter__(self):        return self.open()    def __exit__(self, exc_type, exc_val, exc_tb):        if any(exc_type, exc_val, exc_tb):            self.successful = False            self.comment = f'{exc_type}: {exc_val}\n{exc_tb}'            print(exc_type, exc_val, exc_tb, file=stderr)        self.close()    def __repr__(self):        return (f'<{self.__class__.__name__} '                 f'id={self.id} '                 f'task_name="{self.task_name}">')    @property    def task_name(self):        return self._task_name    @property    def id(self):        return self._id    def _execute(self, query, *args):        with self.connection.cursor() as cursor:            cursor.execute(query, args)            return cursor.fetchone()[0]    def _create(self):        query = """            CREATE TABLE IF NOT EXISTS sessions (                id          SERIAL       NOT NULL PRIMARY KEY,                task_name   VARCHAR(200) NOT NULL,                started     TIMESTAMPTZ  NOT NULL DEFAULT current_timestamp,                finished    TIMESTAMPTZ           DEFAULT current_timestamp,                successful  BOOL,                loaded_rows INT,                comment     VARCHAR(500)            );            """        self._execute(query)    def open(self):        query = """            INSERT INTO sessions (task_name, finished)            VALUES (%s, NULL)            RETURNING id;            """        self._id = self._execute(query, self.task_name)        print(self, 'opened')        return self    def close(self):        if not self._id:            raise SessionClosedError('Session is not open')        query = """            UPDATE sessions            SET                finished    = DEFAULT,                successful  = %s,                loaded_rows = %s,                comment     = %s            WHERE                id = %s            RETURNING id;            """        self._execute(query, self.successful, self.loaded_rows,                      self.comment, self.id)        print(self, 'closed',              ', successful: ', self.successful,              ', Loaded: ', self.loaded_rows,              ', comment:', self.comment)class SessionError(Exception):    passclass SessionClosedError(SessionError):    pass

Настала пора забрать наши данные из наших полутора сотен таблиц. Сделаем это с помощью очень незатейливых строчек:


source_conn = MsSqlHook(mssql_conn_id=src_conn_id, schema=src_schema).get_conn()query = f"""    SELECT         id, start_time, end_time, type, data    FROM dbo.Orders    WHERE        CONVERT(DATE, start_time) = '{dt}'    """df = pd.read_sql_query(query, source_conn)

  1. С помощью хука получим из Airflow pymssql-коннект
  2. В запрос подставим ограничение в виде даты в функцию её подбросит шаблонизатор.
  3. Скармливаем наш запрос pandas, который достанет для нас DataFrame он нам пригодится в дальнейшем.

Я использую подстановку {dt} вместо параметра запроса %s не потому, что я злобный Буратино, а потому что pandas не может совладать с pymssql и подсовывает последнему params: List, хотя тот очень хочет tuple.
Также обратите внимание, что разработчик pymssql решил больше его не поддерживать, и самое время съехать на pyodbc.

Посмотрим, чем Airflow нашпиговал аргументы наших функций:



Если данных не оказалось, то продолжать смысла нет. Но считать заливку успешной тоже странно. Но это и не ошибка. А-а-а, что делать?! А вот что:


if df.empty:    raise AirflowSkipException('No rows to load')

AirflowSkipException скажет Airflow, что ошибки, собственно нет, а таск мы пропускаем. В интерфейсе будет не зеленый и не красный квадратик, а цвета pink.


Подбросим нашим данным несколько колонок:


df['etl_source'] = src_schemadf['etl_id'] = session.iddf['hash_id'] = hash_pandas_object(df[['etl_source', 'id']])

А именно:


  • БД, из которой мы забрали заказы,
  • Идентификатор нашей заливающей сессии (она будет разной на каждый таск),
  • Хэш от источника и идентификатора заказа чтобы в конечной базе (где всё ссыпется в одну таблицу) у нас был уникальный идентификатор заказа.

Остался предпоследний шаг: залить всё в Vertica. А, как ни странно, один из самых эффектных эффективных способов сделать это через CSV!


# Export data to CSV bufferbuffer = StringIO()df.to_csv(buffer,          index=False, sep='|', na_rep='NUL', quoting=csv.QUOTE_MINIMAL,          header=False, float_format='%.8f', doublequote=False, escapechar='\\')buffer.seek(0)# Push CSVtarget_conn = VerticaHook(vertica_conn_id=target_conn_id).get_conn()copy_stmt = f"""    COPY {target_table}({df.columns.to_list()})     FROM STDIN     DELIMITER '|'     ENCLOSED '"'     ABORT ON ERROR     NULL 'NUL'    """cursor = target_conn.cursor()cursor.copy(copy_stmt, buffer)

  1. Мы делаем спецприёмник StringIO.
  2. pandas любезно сложит в него наш DataFrame в виде CSV-строк.
  3. Откроем соединение к нашей любимой Vertica хуком.
  4. А теперь с помощью copy() отправим наши данные прямо в Вертику!

Из драйвера заберем, сколько строчек засыпалось, и скажем менеджеру сессии, что всё ОК:


session.loaded_rows = cursor.rowcountsession.successful = True

Вот и всё.


На проде мы создаем целевую табличку вручную. Здесь же я позволил себе небольшой автомат:

create_schema_query = f'CREATE SCHEMA IF NOT EXISTS {target_schema};'create_table_query = f"""    CREATE TABLE IF NOT EXISTS {target_schema}.{target_table} (         id         INT,         start_time TIMESTAMP,         end_time   TIMESTAMP,         type       INT,         data       VARCHAR(32),         etl_source VARCHAR(200),         etl_id     INT,         hash_id    INT PRIMARY KEY     );"""create_table = VerticaOperator(    task_id='create_target',    sql=[create_schema_query,         create_table_query],    vertica_conn_id=target_conn_id,    task_concurrency=1,    dag=dag)

Я с помощью VerticaOperator() создаю схему БД и таблицу (если их еще нет, естественно). Главное, правильно расставить зависимости:

for conn_id, schema in sql_server_ds:    load = PythonOperator(        task_id=schema,        python_callable=workflow,        op_kwargs={            'src_conn_id': conn_id,            'src_schema': schema,            'dt': '{{ ds }}',            'target_conn_id': target_conn_id,            'target_table': f'{target_schema}.{target_table}'},        dag=dag)    create_table >> load

Подводим итоги


Ну вот, сказал мышонок, не правда ли, теперь
Ты убедился, что в лесу я самый страшный зверь?

Джулия Дональдсон, Груффало


Думаю, если бы мы с моими коллегами устроили соревнование: кто быстрее составит и запустит с нуля ETL-процесс: они со своими SSIS и мышкой и я с Airflow А потом бы мы еще сравнили удобство сопровождения Ух, думаю, вы согласитесь, что я обойду их по всем фронтам!


Если же чуть-чуть посерьезнее, то Apache Airflow за счет описания процессов в виде программного кода сделал мою работу гораздо удобнее и приятнее.


Его же неограниченная расширяемость: как в плане плагинов, так и предрасположенность к масштабируемости даёт вам возможность применять Airflow практически в любой области: хоть в полном цикле сбора, подготовки и обработки данных, хоть в запуске ракет (на Марс, конечно же).


Часть заключительная, справочно-информационная


Грабли, которые мы собрали за вас


  • start_date. Да, это уже локальный мемасик. Через главный аргумент дага start_date проходят все. Кратко, если указать в start_date текущую дату, а в schedule_interval один день, то DAG запустится завтра не раньше.


    start_date = datetime(2020, 7, 7, 0, 1, 2)
    

    И больше никаких проблем.


    С ним же связана и еще одна ошибка выполнения: Task is missing the start_date parameter, которая чаще всего говорит о том, что вы забыли привязать к оператору даг.


  • Всё на одной машине. Да, и базы (самого Airflow и нашей обмазки), и веб-сервер, и планировщик, и воркеры. И оно даже работало. Но со временем количество задач у сервисов росло, и когда PostgreSQL стал отдавать ответ по индексу за 20 с вместо 5 мс, мы его взяли и унесли.


  • LocalExecutor. Да, мы сидим на нём до сих пор, и мы уже подошли к краю пропасти. LocalExecutorа нам до сих пор хватало, но сейчас пришла пора расшириться минимум одним воркером, и придется поднапрячься, чтобы переехать на CeleryExecutor. А ввиду того, что с ним можно работать и на одной машиной, то ничего не останавливает от использования Celery даже не сервере, который естественно, никогда не пойдет в прод, чесслово!


  • Неиспользование встроенных средств:


    • Connections для хранения учетных данных сервисов,
    • SLA Misses для реагирования на таски, которые не отработали вовремя,
    • XCom для обмена метаданными (я сказал метаданными!) между тасками дага.

  • Злоупотребление почтой. Ну что тут сказать? Были настроены оповещения на все повторы упавших тасков. Теперь в моём рабочем Gmail >90k писем от Airflow, и веб-морда почты отказывается брать и удалять больше чем по 100 штук за раз.



Больше подводных камней: Apache Airflow Pitfails

Средства ещё большей автоматизации


Для того чтобы нам еще больше работать головой, а не руками, Airflow заготовила для нас вот что:


  • REST API он до сих пор имеет статус Experimental, что не мешает ему работать. С его помощью можно не только получать информацию о дагах и тасках, но остановить/запустить даг, создать DAG Run или пул.


  • CLI через командную строку доступны многие средства, которые не просто неудобны в обращении через WebUI, а вообще отсутствуют. Например:


    • backfill нужен для повторного запуска инстансов тасков.
      Например, пришли аналитики, говорят: А у вас, товарищ, ерунда в данных с 1 по 13 января! Чини-чини-чини-чини!. А ты такой хоба:
      airflow backfill -s '2020-01-01' -e '2020-01-13' orders
      
    • Обслуживание базы: initdb, resetdb, upgradedb, checkdb.
    • run, который позволяет запустить один инстанс таска, да еще и забить на всё зависимости. Более того, можно запустить его через LocalExecutor, даже если у вас Celery-кластер.
    • Примерно то же самое делает test, только еще и в баз ничего не пишет.
    • connections позволяет массово создавать подключения из шелла.

  • Python API довольно хардкорный способ взаимодействия, который предназначен для плагинов, а не копошения в нём ручёнками. Но кто ж нам помешает пойти в /home/airflow/dags, запустить ipython и начать беспредельничать? Можно, например, экспортировать все подключения таком кодом:


    from airflow import settingsfrom airflow.models import Connectionfields = 'conn_id conn_type host port schema login password extra'.split()session = settings.Session()for conn in session.query(Connection).order_by(Connection.conn_id):  d = {field: getattr(conn, field) for field in fields}  print(conn.conn_id, '=', d)
    

  • Подключение к базе метаданных Airflow. Писать в неё я не рекомендую, а вот доставать состояния тасков для различных специфических метрик можно значительно быстрее и проще, чем через любой из API.


    Скажем, далеко не все наши таски идемпотентны, а могут иногда падать и это нормально. Но несколько завалов это уже подозрительно, и надо бы проверить.


    Осторожно, SQL!
    WITH last_executions AS (SELECT    task_id,    dag_id,    execution_date,    state,        row_number()        OVER (            PARTITION BY task_id, dag_id            ORDER BY execution_date DESC) AS rnFROM public.task_instanceWHERE    execution_date > now() - INTERVAL '2' DAY),failed AS (    SELECT        task_id,        dag_id,        execution_date,        state,        CASE WHEN rn = row_number() OVER (            PARTITION BY task_id, dag_id            ORDER BY execution_date DESC)                 THEN TRUE END AS last_fail_seq    FROM last_executions    WHERE        state IN ('failed', 'up_for_retry'))SELECT    task_id,    dag_id,    count(last_fail_seq)                       AS unsuccessful,    count(CASE WHEN last_fail_seq        AND state = 'failed' THEN 1 END)       AS failed,    count(CASE WHEN last_fail_seq        AND state = 'up_for_retry' THEN 1 END) AS up_for_retryFROM failedGROUP BY    task_id,    dag_idHAVING    count(last_fail_seq) > 0
    



Ссылки


Ну и естественно первые десять ссылок из выдачи гугла содержимое папки Airflow из моих закладок.



И ссылки, задействованные в статье:


Подробнее..

Курсы валют и аналитика использование обменных курсов в Хранилище Данных

19.05.2021 16:06:46 | Автор: admin

Привет! На связи Артемий Analytics Engineer из Wheely.

Сегодня хотел бы поговорить о вопросах конвертирования финансовых показателей в разные валюты. Вопрос достаточно актуальный, так как большое количество компаний имеют мультинациональные зоны присутствия, строят аналитику глобального масштаба, готовят отчетность по международным стандартам.

Покажу как этот вопрос решается с помощью современных подходов на примере кейса Wheely:

  • Расширение списка базовых валют

  • Регулярное обновление и получения актуальных курсов

  • Обеспечение корректности исторических показателей

  • Максимальное удобство и простота использования в аналитических инструментах

Велком под кат для разбора решения проблемы учета мультивалютных метрик и показателей: Open Exchange Rate, Airflow, Redshift Spectrum, dbt.


Новые требования к сервису валютных курсов

В качестве legacy-источника использовался веб-сервис ЦБ РФ. Однако с изменяющимися требованиями и расширением зон присутствия компании его стало недостаточно. Например, по причине отсутствия котировки AED (дирхам ОАЭ). Для кого-то могут быть актуальны курсы криптовалют BTC, ETH, которые в веб-сервисе ЦБ РФ тоже отсутствуют.

Новые требования можно суммировать следующим образом:

  • Поддержка расширенного набора базовых валют, которые отсутствуют в API ЦБ РФ

  • Получение самых актуальных котировок, включая внутридневные курсы

  • Минимизация трансформаций данных вне Хранилища Данных (лучше если их вообще нет)

Матрица новых требований к работе с курсами валютМатрица новых требований к работе с курсами валют

Задачи, которые предстоит решить легко визуализировать в виде матрицы. Красным помечены области, поддержку которых предстоит добавить:

  • Интеграция нового API для уже использующихся курсов

  • Добавление новых базовых валют в выгрузку

  • Получение ретроспективных (исторических) данных по новым валютам за прошлые периоды

  • Архивирование курсов из legacy-источника

Легаси приложение по выгрузке курсов валют формировало pivot-таблицу с коэффициентом для каждой пары в отдельном столбце. Это удобно, когда у нас есть строго фиксированный набор валют и наименования колонок, но превращается в головную боль если список валют необходимо расширить.

Появилось желание уйти от всех трансформаций и формирований таблиц в pandas до того как данные попадают в Хранилище. Здесь я придерживаюсь принципа применения всех трансформаций (T в ELT) в одном месте, и помогает мне в этом замечательный инструмент dbt.

Интеграция с новым поставщиком данных

Как уже стало понятно, без внешнего поставщика данных обойтись не получится, поэтому предлагаю рассмотреть один из ряда провайдеров курсов валют https://openexchangerates.org/

Минимальный необходимый план Developer включает в себя:

  • 10.000 запросов ежемесячно (более чем достаточно)

  • Ежечасные внутридневные обновления курсов

  • Широкий набор базовых валют, включая криптовалюты

Доступные методы API:

Для получения актуальных курсов валют воспользуемся API endpoint /latest.json

Простой запрос-ответ может выглядеть следующим образом:

Установка на расписание в Airflow

Для регулярного получения актуальных курсов валют я воспользуюсь инструментом Airflow. Apache Airflow де-факто стандарт в области оркестрации данных, data engineering и управления пайплайнами.

Смысловая составляющая графа задачи (DAG):

  • Сделать запрос к API

  • Сохранить полученный ответ (например, в виде уникального ключа на S3)

  • Уведомить в Slack в случае ошибки

Конфигурация DAG:

  • Базовые валюты (base currency), от которых отсчитываем курсы

  • Синхронизация расписание запусков с расчетом витрин в Хранилище Данных

  • Токен доступа к сервису

Самый простой DAG состоит из одного таска с вызовом простого shell-скрипта:

TS=`date +"%Y-%m-%d-%H-%M-%S-%Z"` curl -H "Authorization: Token $OXR_TOKEN" \ "https://openexchangerates.org/api/historical/$BUSINESS_DT.json?base=$BASE_CURRENCY&symbols=$SYMBOLS" \ | aws s3 cp - s3://$BUCKET/$BUCKET_PATH/$BUSINESS_DT-$BASE_CURRENCY-$TS.json

Вот как выглядит результат регулярной работы скрипта в S3:

Сегодня в штатном режиме выполняется около 25 обращений к сервису в сутки, статистика выглядит следующим образом:

Выгрузка истории по новым валютам

После обеспечения регулярной выгрузки всех необходимых валют, можно приступить к формированию истории по новым базовым валютам (которой, очевидно, нет). Это позволит переводить в новые валюты суммы транзакций прошлых периодов.

К сожалению, план Developer не включает обращения к API endpoint /time-series.json, и только ради этой разовой задачи не имеет смысла делать upgrade на более дорогостоящую версию.

Воспользуемся методом /historical/*.json и простым опросом API в цикле для формирования исторической выгрузки:

#!/bin/bash d=2011-01-01while [ "$d" != 2021-02-19 ]; do echo $d curl -H "Authorization: Token $TOKEN" "https://openexchangerates.org/api/historical/$d.json?base=AED&symbols=AED,GBP,EUR,RUB,USD" > ./export/$d.json d=$(date -j -v +1d -f "%Y-%m-%d" $d +%Y-%m-%d)done

Пиковая нагрузка вызвала вопросы у коллег, которые тоже пользуются сервисом, но это была разовая акция:

Архивирование исторических курсов валют

Вся история обменных курсов полученная из legacy-источника ЦБ РФ до даты X (перехода на новый сервис-провайдер) подлежит архивированию в неизменном виде.

Я хочу сохранить все те курсы, которые мы показывали в своих аналитических инструментах без изменений. То есть чтобы суммы в дашбордах и отчетах бизнес-пользователей не были изменены ни на копейку.

Для этого я выполню выгрузку накопленных значений обменных курсов за весь исторический период в Data Lake. Более детально, я произведу:

  • Трансформацию legacy pivot-таблицы в двумерную

  • Запись в колоночный формат PARQUET в AWS S3

Формирование архива в S3 в формате PARQUET
CREATE EXTERNAL TABLE spectrum.currencies_cbrfSTORED AS PARQUETLOCATION 's3://<BUCKET>/dwh/currencies_cbrf/' ASWITH base AS (   SELECT 'EUR' AS base_currency   UNION ALL   SELECT 'GBP'   UNION ALL   SELECT 'RUB'   UNION ALL   SELECT 'USD')SELECT   "day" AS business_dt   ,b.base_currency   ,CASE b.base_currency       WHEN 'EUR' THEN 1       WHEN 'GBP' THEN gbp_to_eur       WHEN 'RUB' THEN rub_to_eur       WHEN 'USD' THEN usd_to_eur       ELSE NULL     END AS eur   ,CASE b.base_currency       WHEN 'EUR' THEN eur_to_gbp       WHEN 'GBP' THEN 1       WHEN 'RUB' THEN rub_to_gbp       WHEN 'USD' THEN usd_to_gbp       ELSE NULL     END AS gbp   ,CASE b.base_currency       WHEN 'EUR' THEN eur_to_rub       WHEN 'GBP' THEN gbp_to_rub       WHEN 'RUB' THEN 1       WHEN 'USD' THEN usd_to_rub       ELSE NULL     END AS rub   ,CASE b.base_currency       WHEN 'EUR' THEN eur_to_usd       WHEN 'GBP' THEN gbp_to_usd       WHEN 'RUB' THEN rub_to_usd       WHEN 'USD' THEN 1       ELSE NULL     END AS usd     FROM ext.currencies c   CROSS JOIN base b;

Таким образом, в хранилище S3 у меня теперь есть статический снимок всех обменных курсов, когда-либо использованных в аналитических приложениях, сериализованный в оптимизированный колоночный формат со сжатием. В случае необходимости пересчета витрин и исторических данных я запросто смогу воспользоваться этими курсами.

Доступ к данным из DWH через S3 External Table

А теперь самое интересное из своего аналитического движка Amazon Redshift я хочу иметь возможность просто и быстро обращаться к самым актуальным курсам валют, использовать их в своих трансформациях.

Оптимальное решение создание внешних таблиц EXTERNAL TABLE, которые обеспечивают SQL-доступ к данным, хранящимся в S3. При этом нам доступно чтение полуструктурированных данных в формате JSON, бинарных данных в форматах AVRO, ORC, PARQUET и другие опции. Продукт имеет название Redshift Spectrum и тесно связан с SQL-движком Amazon Athena, который имеет много общего с Presto.

CREATE EXTERNAL TABLE IF NOT EXISTS spectrum.currencies_oxr (   "timestamp" bigint   , base varchar(3)   , rates struct<aed:float8, eur:float8, gbp:float8, rub:float8, usd:float8>)ROW format serde 'org.openx.data.jsonserde.JsonSerDe'LOCATION 's3://<BUCKET>/dwh/currencies/';

Обратите внимание на обращение ко вложенному документу rates с помощью создания типа данных struct.

Теперь добавим к этой задаче секретную силу dbt. Модуль dbt-external-tables позволяет автоматизировать создание EXTERNAL TABLES и зарегистрировать их в качестве источников данных:

   - name: external     schema: spectrum     tags: ["spectrum"]     loader: S3     description: "External data stored in S3 accessed vith Redshift Spectrum"     tables:       - name: currencies_oxr         description: "Currency Exchange Rates fetched from OXR API https://openexchangerates.org"         freshness:           error_after: {count: 15, period: hour}         loaded_at_field: timestamp 'epoch' + "timestamp" * interval '1 second'         external:           location: "s3://<BUCKET>/dwh/currencies/"           row_format: "serde 'org.openx.data.jsonserde.JsonSerDe'"         columns:           - name: timestamp             data_type: bigint           - name: base             data_type: varchar(3)           - name: rates             data_type: struct<aed:float8, eur:float8, gbp:float8, rub:float8, usd:float8>

Немаловажным элементом является проверка своевременности данных source freshness test на курсы валют. Тем самым мы будем постоянно держать руку на пульсе поступления актуальных данных в Хранилище. Очень важно рассчитывать все финансовые метрики корректно и в срок, а без актуальных значений курсов задачу решить невозможно.

В случае отставания данных более 15 часов без свежих обменных курсов мы тут же получаем уведомление в Slack.

Для прозрачности и простоты пользователей объединим исторические данные (архив) и постоянно поступающие актуальные курсы (новый API) в одну модель currencies:

Объединение исторических и новых данных в единый справочник
{{   config(       materialized='table',       dist='all',       sort=["business_dt", "base_currency"]   )}} with cbrf as (  select      business_dt   , null as business_ts   , base_currency   , aed   , eur   , gbp   , rub   , usd  from {{ source('external', 'currencies_cbrf') }} where business_dt <= '2021-02-18' ), oxr_all as (    select      (timestamp 'epoch' + o."timestamp" * interval '1 second')::date as business_dt   , (timestamp 'epoch' + o."timestamp" * interval '1 second') as business_ts   , o.base as base_currency   , o.rates.aed::decimal(10,4) as aed   , o.rates.eur::decimal(10,4) as eur   , o.rates.gbp::decimal(10,4) as gbp   , o.rates.rub::decimal(10,4) as rub   , o.rates.usd::decimal(10,4) as usd   , row_number() over (partition by base_currency, business_dt order by business_ts desc) as rn    from {{ source('external', 'currencies_oxr') }} as o   where business_dt > '2021-02-18' ), oxr as (  select      business_dt   , business_ts   , base_currency   , aed   , eur   , gbp   , rub   , usd  from {{ ref('stg_currencies_oxr_all') }} where rn = 1 ), united as (  select      business_dt   , business_ts   , base_currency   , aed   , eur   , gbp   , rub   , usd  from cbrf  union all  select      business_dt   , business_ts   , base_currency   , aed   , eur   , gbp   , rub   , usd  from oxr ) select    business_dt , business_ts , base_currency , aed , eur , gbp , rub , usd from united

При этом физически справочник с курсами валют копируется на каждую ноду аналитического кластера Redshift и хранится в отсортированном по дате и базовой валюте виде для ускорения работы запросов.

Использование курсов в моделировании данных

В целом, работа с курсами валют для аналитиков и инженеров, которые развивают Хранилище Данных не изменилась и осталась весьма простой. Все детали использования нового API, обращения к внешним полу-структурированным документам JSON в S3, объединению с архивными данными скрыты . В своих трансформациях достаточно сделать простой джоин на таблицу с курсами валют:

   select        -- price_details       , r.currency       , {{ convert_currency('price', 'currency') }}       , {{ convert_currency('discount', 'currency') }}       , {{ convert_currency('insurance', 'currency') }}       , {{ convert_currency('tips', 'currency') }}       , {{ convert_currency('parking', 'currency') }}       , {{ convert_currency('toll_road', 'currency') }}    from {{ ref('requests') }} r       left join {{ ref('stg_currencies') }} currencies on r.completed_dt_utc = currencies.business_dt           and r.currency = currencies.base_currency

Сами метрики конвертируются при помощи простого макроса, который на вход принимает колонку с исходной суммой и колонку с исходным кодом валюты:

-- currency conversion macro{% macro convert_currency(convert_column, currency_code_column) -%}      ( {{ convert_column }} * aed )::decimal(18,4) as {{ convert_column }}_aed   , ( {{ convert_column }} * eur )::decimal(18,4) as {{ convert_column }}_eur   , ( {{ convert_column }} * gbp )::decimal(18,4) as {{ convert_column }}_gbp   , ( {{ convert_column }} * rub )::decimal(18,4) as {{ convert_column }}_rub   , ( {{ convert_column }} * usd )::decimal(18,4) as {{ convert_column }}_usd {%- endmacro %}

Практико-ориентированное развитие

Работа с данными одно из наиболее востребованных и бурно развивающихся направлений. Каждый день я нахожу новые интересные задачи и придумываю решения для них. Это захватывающий и интересный путь, расширяющий горизонты.

В конце мая состоится юбилейный запуск курса Data Engineer в ОТУС, в котором я принимаю участие в роли преподавателя.

По прошествии двух лет программа постоянно менялась, адаптировалась. Ближайший запуск принесет ряд нововведений и будет построен вокруг кейсов реальных прикладных проблем инженеров:

  • Data Architecture

  • Data Lake

  • Data Warehouse

  • NoSQL / NewSQL

  • MLOps

Детально с программой можно ознакомиться на лендинге курса.

Также я делюсь своими авторскими заметками и планами в телеграм-канале Technology Enthusiast.

Благодарю за внимание.

Подробнее..

Avito Analytics meetup

18.06.2020 14:14:33 | Автор: admin

Привет, Хабр! 30 июня в18:00 поМоскве мы проведём онлайн-митап дляаналитиков. Спикеры расскажут прорегиональные A/B-тесты, управление выдачей товаров винтернет-магазине, предсказание профита отновых фичей и data science вдоставке товаров.


Подкатом, как и всегда, тезисы докладов и все нужные ссылки.



Доклады


Региональные A/B-тесты. Зачем нужны и как устроены Игорь Красовский, Авито


image


Что делать, если тестовая группа вA/B-тесте точно неизвестна, а воздействие напользователя производится оффлайн, например, врегиональной ТВ-рекламе? Как сформировать контрольную группу длясмещённой тестовой? Как измерить эффект и отличить его отслучайной ошибки? Расскажу, как мы ответили наэти вопросы вАвито и скакими проблемами столкнулись.

О спикере: ВАвито чуть более 2лет, доэтого успел поработать вeCommerce и IT-консалтинге. Сейчас работаю вкоманде Core Analytics, которая отвечает затакие направления как Data Management, Strategic Analytics, Core Analytics Platform, Key Account Analytics.



Лучшие data-продукты рождаются вполях Марина Калабина, Леруа Мерлен


image


Большинство наших интернет-заказов собираются изторговых залов магазинов, а не соскладов. Это приводит кошибкам между тем, что отображается насайте и тем, что мы можем реально собрать.

Из-завысокой скорости оборота товаров вмагазинах и сложности систем управления стоком возникают ошибки, которые можно обнаруживать автоматически. Опираясь нанаши знания систем и используя социальный инжиниринг, мы предложили решение, которое позволяло бы автоматически находить проблемные товары и корректировать их сток перед публикацией насайте.

О спикере: 9лет работы вЛеруа Мерлен. Сначала открывала магазины, потом вних работала, а сейчас навожу порядок втоварных запасах. Собрала команду и запустила data-продукт за6недель.



Модель роста предсказываем профит от фич для приоритизации Павел Михайлов, Ostrovok.ru


image


  • Строим модель роста фреймворк наоснове когорт и ключевых метрик, который среднесрочно моделирует выручку.
  • Переводим продуктовые и бизнесовые метрики вденьги спомощью модели.
  • Оцениваем потенциальный профит отфичей напримерах.


О спикере: Head of growth вEmerging Travel Group (Ostrovok.ru) саналитическим бэкграундом. Генерирую, разрабатываю и тестирую гипотезы роста.



Как data science Авито Доставке помогал Дима Сергеев, Авито


image


Или история отом, как перестать предлагать пользователям купить Кабину Камаза сДоставкой. НаАвито уже более 60млн. товаров. Не для каждого изних удаётся слёгкостью определить, сможет ли продавец положить его вкоробку размером 1208050 и отправить покупателю вдругой город.

Временами мы такие ошибки допускаем: предлагаем доставку там, где её быть заведомо не должно и наоборот. Расскажу немного отом, как мы сэтой проблемой справляемся и каких результатов получилось достичь.

О спикере: Последний год занимаюсь аналитикой вАвито Доставке. Доэтого на протяжении трёх лет занимался аналитикой вOZON.



Задавайте вопросы вчате трансляции насамые интересные ответим вэфире. После каждого доклада можно будет отдельно пообщаться соспикером.


Пароли и явки


Трансляция нанашем ютуб-канале стартует вовторник 30июня в18:00. Планируем закончить к20:40. Натрансляции можно сразу нажать кнопку напомнить, чтобы ничего не пропустить.


Если хочется получить напоминание соссылкой наэфир наэлектронную почту, можно зарегистрироваться натаймпаде.


Увидимся в онлайне!

Подробнее..

Материалы с митапа для аналитиков модель роста, AB-тесты, управление стоком и доставкой товаров

03.07.2020 12:17:33 | Автор: admin

Хабр, привет! Впоследний день июня прошёл наш митап дляаналитиков. Нанём выступали спикеры изЛеруа Мерлен, Ostrovok.ru и, конечно же, Авито. Обсуждали региональные A/B-тесты, управление выдачей товаров вбольшом интернет-магазине, предсказание профита отновых фичей и data science вдоставке.


Под катом видеозаписи выступлений стаймкодами и ссылки напрезентации.



Региональные A/B-тесты. Зачем нужны и как устроены Игорь Красовский, Авито


На примере задач изАвито Игорь рассказал, что такое региональные А/В-тесты, когда они нужны аналитику, какие алгоритмы и математика лежат вих основе и как измерить точность этих алгоритмов.



00:00 Представление спикера и темы
00:44 Длякаких задач можно применять региональные A/B-тесты
04:21 Омодели региональных А/B-тестов: этапы проведения теста, метрика близости контрольной и тестовой групп
09:51 Алгоритм подбора тестовой группы и оценка его точности
18:03 Что можно улучшить впредложенном процессе: точки роста


Посмотреть презентацию Игоря

Модель роста предсказываем профит отфич дляприоритизации Павел Михайлов, Ostrovok.ru


Из доклада вы узнаете, как работает концепция модели роста, как она помогает перевести продуктовые метрики вденьги и оценить потенциальный профит отновых фичей.


Скачать эксельку спримером модели роста.



00:00 Представление спикера и темы
01:10 Фреймворк ICE (impact, confidence, ease)
02:26 Что такое модель роста и как с её помощью измерить влияние фичи
05:55 Как построить простую модель роста снуля
18:55 Примеры гипотез, которые можно оценить спомощью модели роста
25:22 Как можно улучшать базовую модель и зачем вообще этим заниматься


Посмотреть презентацию Павла

Лучшие data-продукты рождаются вполях Марина Калабина, Леруа Мерлен


Большая часть интернет-заказов вЛеруа Мерлен собираются вторговых залах магазинов. Из-завысокой скорости оборота товаров это нередко приводило ктому, что заказ не получалось собрать. Тогда команда Data Accelerator придумала инструмент, который позволил автоматически находить проблемные артикулы и корректировать их количество передпубликацией насайте.



00:00 Представление спикера и темы
01:37 Леруаизмы термины длялучшего погружения вконтекст доклада
02:40 Как собирают заказы изинтернет-магазина, и какие проблемы могут возникнуть усборщика
04:07 Запуск подразделения Data Accelerator, чтобы принимать data-driven решения
04:46 Продукт гарантированный сток: его цели и процесс реализации
13:34 Итоги внедрения гарантированного стока


Посмотреть презентацию Марины

Как data science Авито Доставке помогал Дима Сергеев, Авито


Ежедневно наАвито продаётся несколько миллионов самых разных товаров. Не длякаждого изних можно слёгкостью определить, сможет ли продавец положить его вкоробку размером 1208050 и отправить покупателю. Временами мы такие ошибки допускаем и предлагаем доставку там, где её быть заведомо не должно и наоборот. Дима рассказал отом, как мы сэтой проблемой справляемся и каких результатов получилось достичь.



00:00 Представление спикера и темы
01:11 История появления доставки вАвито и первые проблемы
06:32 Оценка масштабов неправильного определения возможности доставить товар
11:29 Классификация товаров как способ решить проблему: data science SWAT спешит напомощь
17:44 Первые успехи и побочные эффекты
25:47 Ближайшие планы


Посмотреть презентацию Димы

До встречи на новых митапах!

Подробнее..

Перевод AB Тестирование Основы

09.03.2021 16:05:49 | Автор: admin

Что, Зачем, Когда и Как

Какой из цветов, розовый или голубой, поспособствует тому, чтобы больше людей кликали на кнопку регистрации на сайте? Стоит ли использовать Gif вместо статичного изображения для Главной страницы? Сколько должно быть опций меню для привлечения клиентов, пять или семь? Какая версия больше способствует доведению клиентов до последнего этапа воронки?

Вы можете продолжать вносить изменения наугад или попробовать А/В тестирование!

А/В тестирование используется для определения наиболее эффективной версии продукта на рынке.

Компании самых разных масштабов от стартапов до крупных высокотехнологичных предприятий прибегают к А/В тестированию, чтобы сделать более осознанный выбор. Даже самое простое тестирование может помочь в принятии важных решений.

Что такое А/В тестирование?

А/В тестирование это метод исследования, при котором разным группам посетителей сайта одновременно показаны две версии одной и той же веб-страницы для определения, какая из них работает эффективнее.

Если вкратце, то исследование показывает, какая из версий (А) или (В) лучше?

A/B тестирование, также известное как сплит-тестирование (split testing) или групповое тестирование (bucket testing), по сути, представляет собой эксперимент, в котором пользователям случайным образом показываются два или более варианта рекламы, маркетингового емэйла или веб-страницы, а затем используются различные методы статистического анализа для определения какой вариант дает больше конверсий.

Как правило, при A/B тестировании вариант, который дает более высокую конверсию, является выигрышным, т.е. этот вариант может помочь вам оптимизировать ваш сайт для получения более высоких результатов.

Для чего нужно A/B тестирование?

При емэйл-рассылках пользователям отправляется два варианта письма, и таким образом, маркетинговая команда узнает, какое письмо дало больший эффект с точки зрения прочтения и кликабельности.

Но они не знают, что именно заставило пользователя открыть письмо: предварительный текст, тема, визуальные эффекты или содержание письма? Какой из элементов емэйла является наиболее привлекающим внимание можно определить с помощью A/B теста.

Он может помочь вам изучить поведение посетителей и клиентов на вашем сайте, прежде чем принимать важные решения об изменениях, и повысить ваши шансы на успех. Иными словами, A/B тестирование помогает избежать ненужных рисков, позволяя целенаправленно использовать свои ресурсы для достижения максимальной отдачи и эффективности.

В каких случаях следует использовать A/B тестирование?

Платформа для онлайн-обучения хочет внести изменения в свою главную страницу, чтобы новый, более привлекательный дизайн увеличил количество пользователей, проходящих их курсы. Или, например, они хотят сфокусировать внимание клиентов на более подробном описании карьеры на странице обзора курса, и это изменение потенциально может побудить большее количество пользователей зарегистрироваться и пройти курс A/B тестирование может проверить так ли это.

Все, что отображается на веб-странице и может повлиять на поведение посетителя при просмотре сайта, можно протестировать с помощью A/B тестирования.

Вот неполный список того, что можно оценить с помощью A/B тестирования - заголовок, контент, дизайн страницы, изображения, рекламные акции и предложения, упоминания в социальных сетях, навигация по сайту и пользовательский интерфейс, CTA-кнопка (Call to action - призыв к действию) и ее текст, варианты оплаты, варианты доставки и т. д.

При проведении A/B тестирования сравнения должны быть как можно проще. Например, не сравнивайте две совершенно разные версии своего веб-сайта, потому что вы не поймете, какие факторы действительно имели значение. Точно так же, если на сайт добавляется новый модуль или меню, он не может быть протестирован с помощью A/B тестирования, поскольку в этом случае у нас нет основ для сопоставления.

Как проводится A/B тестирование?

Идеальная воронка продаж для интернет-магазина выглядит следующим образом:

Домашняя страница -> Список категорий/Поиск продукта -> Просмотр страницы продукта -> Корзина -> Оформление заказа

Магазин теряет своих пользователей по мере того, как они проходят этапы этой воронки. Затем проводится A/B-тестирование, чтобы опробовать изменения, которые, как мы надеемся, повысят коэффициент конверсии от одного этапа к другому.

A/B тестирование проводится в четыре этапа:

Определение изменения и метрики

Для начала решите, какую информацию вы сможете собирать и анализировать. Мы не просто предполагаем, а используем соответствующие аналитические инструменты, чтобы в первую очередь удостовериться, что у вас действительно есть проблема, и определить, где именно она находится.

Аналитика часто дает представление о том, что вы можете попробовать усовершенствовать. Например, мы решили изменить кнопку с призывом к действию (CTA) на странице продукта с Buy Now на Shop Now, чтобы увеличить количество пользователей, которые добавляют товары в корзину.

Затем выбираются метрики для измерения уровня вовлеченности пользователей. В нашем примере метрикой будет служить кликабельность кнопки Buy Now. Коэффициент кликабельности (Click through rate - CTR) - это количество кликов уникальных пользователей, разделенное на количество просмотров уникальными пользователями. Вы можете выбрать столько метрик, сколько захотите, и чем больше вы оцениваете, тем выше вероятность того, что вы заметите существенные различия.

Определение гипотезы

Проверка гипотез в статистике - это способ проверить результаты исследования, чтобы понять, есть ли у вас какие-либо существенные результаты. Наиболее важные и запутанные аспекты проверки гипотез - это определение нулевой и альтернативной гипотез.

Проще говоря, утверждение, которое истинно до того, как мы собираем какие-либо данные, является нулевой гипотезой. При A/B тестировании основная нулевая гипотеза будет заключаться в том, что новая версия не лучше или даже хуже старой. Для нашего примера это утверждение, что новый коэффициент кликабельности (CTR) меньше или равен старому.

Альтернативой является конкурирующая гипотеза, зачастую являющаяся логическим отрицанием нулевой гипотезы. Утверждения, которые мы пытаемся доказать, всегда появляются альтернативой. Итак, при A/B тестировании альтернативная гипотеза состоит в том, что новая версия лучше старой. В нашем примере это утверждение о том, что новый CTR будет больше старого.

Создание эксперимента (размер выборки, факторы воздействия, время выполнения, участники)

Обычно пользователи выбираются случайным образом и распределяются либо в контрольную группу, либо в экспериментальную группу. Затем мы запускаем эксперимент, в котором контрольная группа видит старую версию, а экспериментальная группа - новую.

Каждый пользователь видит только один вариант дизайна (A или B), даже если обновляет интерфейс. Таким образом, каждую из версий будет просматривать одинаковое количество людей, и вы сможете проанализировать, какая версия обеспечивает улучшение метрики, которое вы считаете существенным.

Размер выборки, который вы выберете, позволяет приблизительно определить, сколько времени займет сбор достаточного количества данных.

Анализ результатов A/B тестирования

После завершения исследования мы можем проанализировать результаты. На этом этапе больше всего сконцентрировано внимание аналитиков. Мы рассчитываем значения показателей как для контрольной, так и для экспериментальной группы.

Затем применяются различные статистические методы, такие как распределение выборок (Sampling Distribution) с применением бутстреппинга, регрессии и различных других алгоритмов машинного обучения, для оценки метрики и демонстрации разницы между работой двух версий вашей веб-страницы и наличия статистически значимой разницы.

Трудности при A/B тестировании

При планировании A/B тестирования и составлении выводов по его результатам необходимо учитывать множество факторов. Перечислим несколько самых распространенных:

1. Эффект новизны и отторжение изменений, когда старые пользователи впервые сталкиваются с изменениями.

2. Достаточный трафик и конверсии для значимых и повторяемых результатов.

3. Последовательность среди испытуемых в контрольной и экспериментальной группе.

4. Выбор лучших показателей для принятия окончательного решения. Например, измерение дохода в сопоставлении с количеством кликов.

5. Практическая значимость коэффициента конверсии, стоимость запуска новой фичи по сравнению с прибылью от увеличения конверсии.

6. Продолжительность времени исследования, чтобы учесть такие факторы, как изменения в настроении людей в зависимости от времени дня/недели или сезонных событий.

При написании этой статьи я старалась выражать мысли как можно проще, чтобы люди, не знакомые с A/B тестированием, могли получить общее представление о нем. Существуют различные аналитические инструменты, которые используются для A/B тестирования в крупных отраслях, такие как Google Analytics и Google Optimize, Vwo, Optimizely, набор для A/B тестирования HubSpot, Crazy Egg и т. д.

A/B - это не роскошь для маркетологов, у которых есть свободное время; это - источник жизненной силы взлома роста.

Один из самых популярных примеров A/B тестирования - 41 оттенок синего, когда Google не могли решить, какой из двух синих цветов они предпочитают для определенного элемента дизайна. По некоторым данным, они использовали A/B тестирование для оценки привлекательности 41 различных оттенков синего. Кроме того, ознакомьтесь с 7 невероятными примерами A/B тестов, проводимых реальными компаниями примеры A/B тестирования промышленного уровня.

Точный подход к A/B тестированию может привести к огромным преимуществам и более эффективному взаимодействию с пользователями, повышению коэффициента конверсии, простоте анализа и увеличению продаж это беспроигрышный вариант!!


Перевод статьи подготовлен в преддверии старта курса "Промышленный ML на больших данных".

Приглашаем всех желающих записаться на бесплатный демо-урок, в рамках которого рассмотрим потоковую обработку больших данных. Подходы, архитектуры, фреймворки, сравнение, обзор и выбор. Основных игроков и перспективных новичков. А также реальные проекты, интересные связки и многое другое!

Подробнее..

Перевод Как BigQuery от Google демократизировал анализ данных. Часть 1

04.08.2020 16:15:07 | Автор: admin
Привет, хабр! Прямо сейчас в OTUS открыт набор на новый поток курса Data Engineer. В преддверии старта курса мы традиционно подготовили для вас перевод интересного материала.



Каждый день более ста миллионов человек посещают Twitter, чтобы узнать, что происходит в мире, и обсудить это. Каждый твит и любое другое действия пользователя генерируют событие, доступное для внутреннего анализа данных в Twitter. Сотни сотрудников анализируют и визуализируют эти данные, и улучшение их опыта является главным приоритетом для команды Twitter Data Platform.

Мы считаем, что пользователи с широким спектром технических навыков должны иметь возможность находить данные и иметь доступ к хорошо работающим инструментам анализа и визуализации на основе SQL. Это позволило бы целой новой группе пользователей с меньшим техническим уклоном, включая дата аналитиков и продакт менеджеров, извлекать информацию из данных, позволяя им лучше понимать и использовать возможности Twitter. Так мы демократизируем анализ данных в Twitter.

По мере совершенствования наших инструментов и возможностей для внутреннего анализа данных мы стали свидетелями улучшения сервиса Twitter. Тем не менее, еще есть куда расти. Текущие инструменты, такие как Scalding, требуют опыта программирования. Инструменты анализа на основе SQL, такие как Presto и Vertica, имеют проблемы с производительностью в большом масштабе. У нас также есть проблема с распространением данных по нескольким системам без постоянного доступа к ним.

В прошлом году мы объявили о новом сотрудничестве с Google, в рамках которого переносим части нашей инфраструктуры данных на Google Cloud Platform (GCP). Мы пришли к выводу, что инструменты Google Cloud Big Data могут помочь нам в наших инициативах по демократизации анализа, визуализации и машинного обучения в Twitter:

  • BigQuery: хранилище корпоративных данных с SQL движком на основе Dremel, который славится быстротой, простотой и справляется с машинным обучением.
  • Data Studio: инструмент для визуализации больших данных с функциями совместной работы, как в Google Docs.


Из этой статьи вы узнаете о нашем опыте работы с этими инструментами: что мы сделали, чему научились и что будем делать дальше. Сейчас мы сосредоточимся на пакетной и интерактивной аналитике. Аналитику в реальном времени мы обсудим в следующей статье.

История хранилищ данных в Twitter


Прежде чем углубляться в BigQuery, стоит кратко пересказать историю хранилищ данных в Twitter. В 2011 году анализ данных в Twitter производился в Vertica и Hadoop. Для создания MapReduce работ Hadoop мы использовали Pig. В 2012 году мы заменили Pig на Scalding, у которого был Scala API с такими преимуществами, как возможность создавать сложные конвейеры и простота тестирования. Тем не менее, для многих дата аналитиков и продакт менеджеров, которым было более комфортно работать с SQL, это была достаточно крутая кривая обучения. Примерно в 2016 году мы начали использовать Presto в качестве SQL интерфейса для данных Hadoop. Spark предлагал Python интерфейс, который делает его хорошим выбором для ad hoc исследований данных и машинного обучения.

Начиная с 2018 года мы использовали следующие инструменты для анализа и визуализации данных:

  • Scalding для производственных конвейеров
  • Scalding и Spark для ad hoc анализа данных и машинного обучения
  • Vertica и Presto для ad hoc и интерактивного SQL анализа
  • Druid для малой интерактивного, исследовательского и доступа с малой задержкой к метрикам временных рядов
  • Tableau, Zeppelin и Pivot для визуализации данных


Мы обнаружили, что, хотя эти инструменты предлагают очень мощные возможности, мы испытывали сложности в реализации доступности этих возможностей для более широкой аудитории в Twitter. Расширяя нашу платформу с помощью Google Cloud, мы концентрируемся на упрощении наших аналитических инструментов для всего Twitter.

Хранилище данных BigQuery от Google


Несколько команд в Twitter уже включили BigQuery в некоторые из своих производственных конвейеров. Используя их опыт, мы начали оценивать возможности BigQuery для всех сценариев использования Twitter. Нашей целью было предложить BigQuery всей компании, а также стандартизировать и поддерживать ее в рамках набора инструментов Data Platform. Это было затруднительно по многим причинам. Нам необходимо было разработать инфраструктуру для надежного приема больших объемов данных, поддержки управления данными в масштабах всей компании, обеспечения надлежащего контроля доступа и обеспечения конфиденциальности клиентов. Нам также пришлось создавать системы для распределения ресурсов, мониторинга и возврата платежей, чтобы команды могли эффективно использовать BigQuery.

В ноябре 2018 года мы выпустили альфа-релиз BigQuery и Data Studio для всей компании. Мы предложили сотрудникам Twitter некоторые из наших наиболее часто используемых таблиц с очищенными персональными данными. BigQuery использовали более 250 пользователей из различных команд, включая инженерные, финансовые и маркетинговые. Совсем недавно они выполняли около 8 тыс. запросов, обрабатывая около 100 ПБ в месяц, не считая запланированных запросов. Получив весьма положительные отзывы, мы решили двигаться вперед и предложить BigQuery в качестве основного ресурса для взаимодействия с данными в Twitter.

Вот схема высокоуровневой архитектуры нашего хранилища данных Google BigQuery.


Мы копируем данные из локальных кластеров Hadoop в Google Cloud Storage (GCS), используя внутренний инструмент Cloud Replicator. Затем мы используем Apache Airflow для создания конвейеров, которые используют bq_load для загрузки данных из GCS в BigQuery. Мы используем Presto для запроса наборов данных Parquet или Thrift-LZO в GCS. BQ Blaster это внутренний инструмент Scalding для загрузки наборов данных HDFS Vertica и Thrift-LZO в BigQuery.

В следующих разделах мы обсудим наш подход и знания в области простоты использования, производительности, управления данными, работоспособности системы и стоимости.

Простота использования


Мы обнаружили, что пользователям было легко начинать с BigQuery, поскольку он не требовал установки программного обеспечения и пользователи могли получать к нему доступ через интуитивно понятный веб-интерфейс. Тем не менее пользователям необходимо было ознакомиться с некоторыми функциями GCP и его концепциями, включая такие ресурсы, как проекты, наборы данных и таблицы. Мы разработали учебные материалы и туториалы, чтобы помочь пользователям начать работу. С приобретением базового понимания пользователям стало легко перемещаться по наборам данных, просматривать схему и данные таблиц, выполнять простые запросы и визуализировать результаты в Data Studio.

Наша цель касаемо ввода данных в BigQuery состояла в том, чтобы обеспечить плавную загрузку наборов данных HDFS или GCS одним щелчком мыши. Мы рассматривали Cloud Composer (управляемый Airflow), но не смогли его использовать из-за нашей модели безопасности Domain Restricted Sharing (подробнее об этом в разделе Управление данными ниже). Мы экспериментировали с использованием Google Data Transfer Service (DTS) для организации нагрузочных задач BigQuery. В то время как DTS быстро настраивался, он не был гибким для построения конвейеров с зависимостями. Для нашей альфа версии мы создали собственную среду Apache Airflow в GCE и готовим ее к работе в продакшене и возможности поддерживать больше источников данных, таких как Vertica.

Для преобразования данных в BigQuery пользователи создают простые конвейеры данных SQL, используя запланированные запросы. Для сложных многоступенчатых конвейеров с зависимостями мы планируем использовать либо нашу собственную инфраструктуру Airflow, либо Cloud Composer вместе с Cloud Dataflow.

Производительность


BigQuery создан для SQL запросов общего назначения, которые обрабатывают большие объемы данных. Он не предназначен для запросов с низкой задержкой, высокой пропускной способностью, необходимых для транзакционной базы данных, или для анализа временных рядов с низкой задержкой, реализованного Apache Druid. Для интерактивных аналитических запросов наши пользователи ожидают времени отклика менее одной минуты. Мы должны были спроектировать использование BigQuery таким образом, чтобы соответствовать этим ожиданиям. Чтобы обеспечить предсказуемую производительность для наших пользователей, мы использовали функционал BigQuery, доступный для клиентов на фиксированной оплате, которая позволяет владельцам проектов резервировать минимальные слоты для своих запросов. Слот BigQuery это единица вычислительной мощности, необходимой для выполнения SQL запросов.

Мы проанализировали более 800 запросов, обрабатывающих около 1 ТБ данных каждый, и обнаружили, что среднее время выполнения составило 30 секунд. Мы также узнали, что производительность сильно зависит от использования нашего слота в различных проектах и задачах. Мы должны были четко разграничить наши производственные и ad hoc резервы слотов, чтобы поддерживать производительность для производственных сценариев использования и интерактивного анализа. Это очень сильно повлияло на наш дизайн для резервирования слотов и иерархии проектов.

Про управление данными, функциональность и стоимость систем, поговорим уже в ближайшие дни во второй части перевода, а сейчас приглашаем всех желающих на бесплатный живой вебинар, в рамках которого можно будет подробно узнать о курсе, а также задать вопросы нашему эксперту Егору Матешуку (Senior Data Engineer, MaximaTelecom).



Читать ещё:


Подробнее..

Перевод Как BigQuery от Google демократизировал анализ данных. Часть 2

18.08.2020 16:06:31 | Автор: admin
Привет, Хабр! Прямо сейчас в OTUS открыт набор на новый поток курса Data Engineer. В преддверии старта курса продолжаем делиться с вами полезным материалом.

Читать первую часть





Управление данными


Сильное управление данными (Strong Data Governance) основной принцип Twitter Engineering. Поскольку мы внедряем BigQuery в нашу платформу, мы концентрируемся на обнаружении данных, контроле доступа, безопасности и конфиденциальности.

Для обнаружения данных и управления ими мы расширили наш уровень доступа к данным (Data Access Layer DAL), чтобы предоставлять инструменты как для локальных данных, так и для данных Google Cloud, предоставляя единый интерфейс и API для наших пользователей. По мере того как Google Data Catalog движется в сторону общедоступности, мы включим его в наши проекты, чтобы предоставить пользователям такие функции, как поиск по колонкам.

BigQuery позволяет легко обмениваться данными и получать к ним доступ, но нам нужно было до некоторой степени контролировать это, чтобы предотвратить эксфильтрацию данных. Среди других инструментов мы выбрали две функции:

  • Domain restricted sharing: бета-функция, запрещающая пользователям обмениваться наборами данных BigQuery с пользователями за пределами Twitter.
  • VPC service controls: элемент управления, который предотвращает эксфильтрацию данных и требует от пользователей доступ к BigQuery с известных диапазонов IP-адресов.


Мы реализовали требования аутентификации, авторизации и аудита (AAA) для обеспечения безопасности следующим образом:

  • Аутентификация: мы использовали учетные записи пользователей GCP для ad hoc запросов и учетные записи служб для рабочих запросов.
  • Авторизация: мы требовали, чтобы каждый набор данных имел учетную запись службы владельца и группу читателей.
  • Аудит: мы экспортировали журналы стекдрайверов BigQuery, в которых содержалась подробная информация о выполнении запросов, в набор данных BigQuery для удобства анализа.


Чтобы обеспечить надлежащую обработку личных данных пользователей Twitter, мы должны зарегистрировать все наборы данных BigQuery, аннотировать личные данные, поддерживать надлежащее хранение и удалять (вычищать) данные, которые были удалены пользователями.

Мы рассматривали Google Cloud Data Loss Prevention API, который использует машинное обучение для классификации и редактирования конфиденциальных данных, но приняли решение в пользу ручной аннотации набора данных из-за точности. Мы планируем использовать Data Loss Prevention API, чтобы дополнить пользовательскую аннотацию.

В Twitter мы создали четыре категории конфиденциальности для наборов данных в BigQuery, перечисленные здесь в порядке убывания чувствительности:

  • Высокочувствительные наборы данных доступны по мере необходимости на основе принципа наименьших привилегий. Каждый набор данных имеет отдельную группу читателей, и мы будем отслеживать использование отдельных учетных записей.
  • Наборы данных средней чувствительности (односторонние псевдонимы с использованием соленого хеширования) не содержат личной информации (Personally Identifiable Information PII) и доступны для большей группы сотрудников. Это хороший баланс между соображениями конфиденциальности и полезности данных. Это позволяет сотрудникам выполнять задачи анализа, такие как вычисление количества пользователей, которые использовали функцию, не зная, кто является реальными пользователями.
  • Наборы данных с низкой чувствительностью со всей информацией, идентифицирующей пользователя. Это хороший подход с точки зрения конфиденциальности, но его нельзя использовать для анализа на уровне пользователя.
  • Публичные наборы данных (выпущенные вне Twitter) доступны всем сотрудникам Twitter.


Что до регистрации, мы использовали запланированные задачи для перечисления наборов данных BigQuery и регистрации их в Data Access Layer (DAL), хранилище метаданных Twitter. Пользователи будут аннотировать наборы данных информацией о конфиденциальности, а также указывать срок хранения. Что до очистки, мы оцениваем производительность и стоимость двух вариантов: 1. Очистка наборов данных в GCS с помощью таких инструментов, как Scalding, и загрузка их в BigQuery; 2. Использование операторов BigQuery DML. Мы, вероятно, будем использовать комбинацию обоих методов для удовлетворения требований различных групп и данных.

Функциональность системы


Поскольку BigQuery является управляемой службой, не было необходимости подключать SRE команду Twitter к управлению системами или выполнению дежурных обязанностей. Было легко обеспечить большую емкость как для хранения, так и для вычислений. Мы могли бы изменить резервирование слотов, создавая тикеты в поддержке Google. Мы определили, что можно было бы улучшить, например, самообслуживание для распределения слотов и улучшение дашборда для мониторинга, и передали эти запросы в Google.

Стоимость


Наш предварительный анализ показал, что стоимость запросов для BigQuery и Presto была на одном уровне. Мы приобрели слоты по фиксированной цене, чтобы иметь стабильную ежемесячную стоимость вместо оплаты по требованию за ТБ обработанных данных. Это решение также было основано на отзывах пользователей, которые не хотели думать о затратах перед выполнением каждого запроса.

Хранение данных в BigQuery принесло расходы в дополнение к затратам на GCS. Такие инструменты, как Scalding, требуют наличия наборов данных в GCS, а для доступа к BigQuery нам пришлось загрузить те же наборы данных в формат BigQuery Capacitor. Мы работаем над соединением Scalding с наборами данных BigQuery, которое устранит необходимость хранения наборов данных как в GCS, так и в BigQuery.

Для редких случаев, которые требовали нечастых запросов на десятки петабайт, мы решили, что хранение наборов данных в BigQuery не является экономически эффективным, и использовали Presto для прямого доступа к наборам данных в GCS. Для этого мы присматриваемся к BigQuery External Data Sources.

Следующие шаги


Мы отметили большой интерес к BigQuery с момента выпуска альфы. Мы добавляем больше наборов данных и больше команд в BigQuery. Мы разрабатываем коннекторы для инструментов анализа данных, таких как Scalding, для чтения и записи в хранилище BigQuery. Мы рассматриваем такие инструменты, как Looker и Apache Zeppelin, для создания корпоративных отчетов о качестве и заметок с использованием наборов данных BigQuery.

Сотрудничество с Google было очень продуктивным, и мы рады продолжить и развивать это партнерство. Мы работали с Google, чтобы внедрить наш собственный Partner Issue Tracker, чтобы напрямую отправлять запросы в Google. Некоторые из них, такие как загрузчик BigQuery Parquet, уже реализованы Google.

Вот некоторые из наших высокоприоритетных запросов фич для Google:

  • Инструменты для удобного приема данных и поддержка формата LZO-Thrift.
  • Почасовое сегментирование
  • Улучшения в области контроля доступа, такие как разрешения на уровне таблиц, строк и столбцов.
  • BigQuery External Data Sources с интеграцией и поддержкой Hive Metastore для формата LZO-Thrift.
  • Улучшенная интеграция каталога данных в пользовательском интерфейсе BigQuery
  • Самообслуживания для распределения и мониторинга слотов.


Заключение


Демократизация анализа данных, визуализации и машинного обучения безопасным способом является высшим приоритетом для команды Data Platform. Мы определили Google BigQuery и Data Studio как инструменты, которые могут помочь в достижении этой цели, и зарелизили в прошлом году BigQuery Alpha для всей компании.

Мы обнаружили, что запросы в BigQuery были простыми и эффективными. Для приема и преобразования данных мы использовали инструменты Google для простых конвейеров, но для сложных конвейеров нам пришлось создать собственную инфраструктуру Airflow. В области управления данными службы BigQuery для аутентификации, авторизации и аудита удовлетворяют наши потребности. Для управления метаданными и соблюдения конфиденциальности нам требовалась большая гибкость и нам приходилось создавать собственные системы. BigQuery, будучи управляемым сервисом, был прост в эксплуатации. Затраты на запросы были аналогичны существующим инструментам. Хранение данных в BigQuery понесло расходы в дополнение к затратам на GCS.

В целом, BigQuery хорошо работает для общего SQL анализа. Мы отмечаем большой интерес к BigQuery, и мы работаем над переносом большего количества наборов данных, привлечением большего количества команд и созданием большего количества конвейеров с BigQuery. В Twitter используются разные данные, для которых потребуется сочетание таких инструментов, как Scalding, Spark, Presto и Druid. Мы намерены продолжать наращивать наши инструменты анализа данных и предоставлять четкие рекомендации нашим пользователям о том, как наилучшим образом использовать наши предложения.

Слова благодарности


Я хотел бы поблагодарить моих соавторов и товарищей по команде, Анжу Джа и Уилла Паскуччи, за их великолепное сотрудничество и тяжелую работу на этом проекте. Я также хотел бы поблагодарить инженеров и менеджеров из нескольких команд в Twitter и Google, которые помогли нам и пользователям BigQuery в Twitter, предоставившим ценную обратную связь.

Если вы заинтересованы в работе над этими задачами, ознакомьтесь с нашими вакансиями в команде Data Platform.



КАЧЕСТВО ДАННХ В DWH КОНСИСТЕНТНОСТЬ ХРАНИЛИЩА ДАННХ


Подробнее..

Как построить прогнозную модель для маркетолога в SAP Analytics Cloud без привлечения датасайнтистов

17.12.2020 14:07:58 | Автор: admin

Сегодня популярность проектов data science очень высока и бизнес понимает их важность и значимость. Рынок наполнен специалистами в этой области, которые умеют добиваться впечатляющих результатов. Но такие проекты нередко бывают дорогостоящими, и не всегда для простых задач нужно привлекать профессионалов в этой области. Создание некоторых прогнозов вполне по силам самим бизнес-пользователям. Например, специалисты из отдела маркетинга могут прогнозировать отклик на маркетинговые кампании. Это становится возможным при наличии инструмента, позволяющего за несколько минут построить прогноз и легко интерпретировать результаты с точки зрения бизнес-смысла.

SAP Analytics Cloud (SAC) облачный инструмент, совмещающий в себе функции BI, планирования и прогнозирования, он также оснащен множеством функций расширенной аналитики: интеллектуальными подсказками, автоматизированным анализом данных и возможностями автоматического прогнозирования.

В этой статье мы расскажем о том, как построено прогнозирование в SAP Analytics Cloud, какие сценарии доступны на сегодняшний день, а также как этот процесс может быть интегрирован с планированием.

Функциональность Smart Predict ориентирована на бизнес-пользователя и позволяет строить прогнозы высокой точности без привлечения специалистов Data Science. Со стороны пользователя системы прогноз происходит в черном ящике, но в реальности это, конечно же, не так. Алгоритмы прогнозирования в SAC идентичны алгоритмам модуляAutomated Analytics инструмента SAP Predictive Analytics. Об алгоритмах, лежащих в основе этого продукта, есть множество материалов, мы предлагаем прочитать эту статью. На вопрос: Получается, Automated Analytics переехал в SAP Analytics Cloud? - мы отвечаем - Да, но пока только частично. Вот какая разница и сходство в функциональных возможностях инструментов (рис.1)

SAP Analytic Cloud в настоящее время предлагает 3 прогнозных сценария:

Классификация. В данном случае предполагается бинарная классификация: предсказываются значения целевой переменной, которая может иметь только два значения -да и нет или 0 и 1. Примерами сценария классификации могут быть:

Прогноз оттока клиентов, где целевая переменная предсказывает, уйдет клиент или нет.

Прогноз склонности клиента к покупке с целевой переменной, прогнозирующей будет ли клиент покупать предложенный ему товар или нет.

Выявление мошенничества с целевой переменной, указывающей, была ли транзакция или требование мошенническим, или нет.

Регрессия. Предсказывает числовое значение целевой переменной в зависимости от переменных, описывающих ее. Примеры регрессионных сценариев:

Количество клиентов, посещающих магазин в обеденное время.

Выручка от каждого клиента в следующем квартале.

Цена продажи подержанных автомобилей.

Прогнозирование временного ряда. Предсказывает значение переменной с течением времени с учетом дополнительных описательных переменных. Примеры сценария прогнозирования временных рядов:

Выручка по каждой продуктовой линейке в течение следующих нескольких кварталов.

Количество велосипедов, арендованных в городе в течение следующих нескольких дней.

Командировочные расходы в ближайшие несколько месяцев.

В процессе прогнозирования мы будем работать с тремя типами датасетов: тренировочным, прикладным и результирующим (рис. 2)

Процесс прогнозирования в SAP Analytics Cloud осуществляется в несколько простых этапов. Мы рассмотрим его на примере прогнозирования оттока клиентов интернет-магазина. Для начала нам необходимо загрузить в систему тренировочный датасет из системы-источника или excel файла. Также SAP Analytics Cloud позволяет прогнозировать в режиме Live (избегая загрузки данных в облако) на таблицах SAP HANA. В этом случае для пользователя SAC процесс остается неизменным, но построение прогноза происходит на стороне SAP HANA с использованием библиотеки Automated Predictive Library (APL).

Наш тренировочный датасет имеет следующие поля:

Customer ID

Уникальный ID каждого клиента

Usage Category (Month)

Количество времени, проведенное клиентом на портале в текущем месяце

Average Usage (Year)

Среднее количество времени, проведенное клиентом на портале за прошедший год

Usage Category (previous Month)

Количество времени, проведенное клиентом на портале за прошедший месяц

Service Type

Флаговая переменная, указывающая тип сервиса клиента: премиум или стандарт

Product Category

Категория продукта интернет-магазина, наиболее часто заказываемая клиентом

Message Allowance

Флаговая переменная, указывающая, давал ли клиент согласие на получение сообщений

Average Marketing Activity (Bi-yearly)

Среднее число маркетинговых предложений для клиента за последние 2 года

Average Visit Time (min)

Среднее количество времени, проведенное клиентом на портале за каждый визит

Pages per Visit

Среднее число страниц интернет-магазина, посещаемых клиентом за один визит

Delta Revenue (Previous Month)

Разница между выручкой от данного клиента за предыдущий и текущий месяц

Revenue (Current Month)

Выручка от данного клиента в текущем месяце

Service Failure Rate (%)

Доля случаев, когда пользователь сталкивался с ошибками в работе портала

Customer Lifetime (days)

Количество дней с момента регистрации

Product Abandonment

Число продуктов, которые были помещены клиентом в корзину, но не оплачены за последний квартал

Contract Activity

Флаговая переменная, указывающая, отказался ли данный пользователь от услуг интернет-магазина. Целевая переменная, которую мы будем прогнозировать.

Следующим шагом построение прогноза будет выбор сценария прогнозирования.

Под каждой опцией есть описание, чтобы облегчить пользователю выбор правильного сценария для каждого варианта использования. Сейчас мы хотим создать модель, позволяющую предсказать, какие клиенты интернет-магазина наиболее склонны к оттоку. Исходя из описаний прогнозных типов сценариев, мы видим, что классификация сможет удовлетворить наши потребности. Итак, мы выбираем Классификация.

После этого мы создаем прогнозную модель и выбираем заранее подготовленный датасет, как источник данных для обучения. У пользователя также есть возможность редактировать метаданные. Далее мы выбираем целевую переменную, ее мы будем прогнозировать. В нашем случае это переменная Contract Activity, идентифицирующая отток клиентов. Также мы можем исключить некоторые переменные, если понимаем, что они никак не повлияют на формирование прогноза. Например, в нашем случае точно можно исключить Customer ID. Это может ускорить процесс выполнения прогноза, но их сохранение не мешает процессу моделирования.

После указания всех желаемых вводных мы отправляем модель на обучение. На основе исторических данных модель выявляет закономерности, то есть находит шаблоны по характеристикам клиента, который с большой вероятностью может отказаться от услуг интернет-магазина. При этом в наборе данных обучения должно быть достаточно целевых примеров, то есть данных об ушедших клиентах.

Во время обучения инструмент автоматически протестирует и попытается уменьшить количество переменных в модели, в зависимости от критериев качества, которые мы опишем ниже. Этот выбор выполняется последовательными итерациями, и сохраняются только те переменные, которые влияют на цель.

После обучения модели нам выдается подробный отчет о ее качестве, как показано на рисунке ниже. Мы видим два ключевых показателя эффективности: прогностическая сила и достоверность прогноза.

Прогностическая сила (KI) указывает на долю информации в целевой переменной, которую могут описать другие переменные модели (объясняющие переменные). Гипотетическая модель с прогностической силой 1,0 является идеальной, поскольку позволяет объяснить 100% информации целевой переменной, а модель с прогностической силой 0 является чисто случайной.

Достоверность прогноза (KR) показывает способность модели достичь той же производительности, когда она применяется к новому набору данных, имеющему те же характеристики, что и обучающий набор данных. Приемлемой считается достоверность прогноза не менее 95%.

Также отчет предоставляет информацию о переменных, которые в процессе генерации прогнозной модели были идентифицированы как ключевые, и упорядочивает их по степени влияния на целевую переменную.

Кроме этого, на рисунке ниже мы видим кривую производительности AUC ROC. Ось X показывает, какой процент от общей выборки составляют положительные цели; ось Y представляет их верно идентифицированный процент, который обнаружил алгоритм. Здесь необходимо пояснить, что положительными целями в данном случае считается целевая группа, а именно клиенты, отказавшиеся от услуг интернет-магазина.

Зеленая кривая олицетворяет некую идеальную модель с максимально возможным процентом обнаруженной цели. Например, если 25% общей выборки будут составлять ушедшие клиенты, то идеальная модель правильно классифицирует все 25% таких клиентов.

Красная кривая показывает минимальный процент обнаруженной цели, полученный случайной моделью. То есть случайно взяв 10% населения, вы бы определили 10% клиентов.

Синяя кривая показывает процент обнаруженной цели с использованием нашей модели. Например, если 10% выборки составляют ушедшие клиенты, мы обнаруживаем примерно 75%. Чем ближе синяя линия подходит к зеленой, тем лучше модель. Чем больше расстояние между красной и синей линиями, тем больше лифт модели.

Кроме того, SAC предоставляет возможность увидеть, как именно ключевые факторы влияют на целевую переменную.

Например, посмотрим более подробную аналитику по предиктору Average Visit Time, показывающему среднее количество времени, которое клиент проводит на портале интернет-магазина. По умолчанию предлагается отследить влияние этого фактора на столбчатой диаграмме, но также существует возможность настройки различных типов визуализации.

Мы видим, что клиенты, проводившие на страницах интернет-магазина от 10 до 22 и от 1 до 3 минут, имеют наибольшую склонность отказаться от услуг. Напротив, покупатели, находящиеся на портале от 3 до 7 и от 7 до 10 минут, показывают наименьшую вероятность уйти. Результаты выглядят волне логично: уходят те, кто проводили на сайте или слишком мало, или слишком много времени. Подобный анализ можно провести по всем ключевым предикторам.

Также для оценки качества получившейся модели SAC предлагает проанализировать матрицы неточностей, различные графики, иллюстрирующие эффективность модели (лифт, чувствительность, кривая Лоренца и плотность) и смоделировать возможные выигрыши и проигрыши от использования данного прогноза.

После того, как мы проанализировали модель и убедились в качестве, мы можем ее применить. Для этого мы находим датасет, который идентичен тренировочному по структуре, но содержит данные о новых клиентах, уход которых мы хотим предсказать. Далее мы выбираем переменные, которые надо увидеть в финальном датасете, а также указываем, какие характеристики получившегося прогноза мы хотим добавить.

На выходе мы получаем датасет с прогнозными значениями целевой переменной, на основе которого может быть легко сформирована модель данных. Она, в свою очередь, может являться источником данных для построения аналитической отчетности в SAC. Например, для этого кейса мы использовали интеллектуальную функцию Smart Discovery и получили автоматических дашборд на спрогнозированных данных.

Таким образом, прогнозирование легко вписывается в процесс анализа данных в SAC. С недавних пор функционал прогнозирования в решении также может быть легко интегрирован в процесс планирования.

Теперь SAC также позволяет использовать модели планирования, как данные для обучения предиктивных моделей. Даже если модель уже есть в отчетах и плановых формах ввода, можно создать частную версию прогноза или бюджета, и использовать алгоритм прогноза временного ряда для желаемого показателя. В качестве данных для тренировки модели может быть использован как факт, так и прогноз, бюджет или любая другая существующая версия данных. В дальнейшем процесс не будет отличаться от вышеописанного: пользователю будет предоставлен подробный отчет о качестве модели. В итоге спрогнозированные показатели будут записываться в созданную нами частную версию модели планирования.

Кроме того, в процессе создания отчетов пользователи также могут использовать прогностические функции, например, автоматический прогноз. Он позволяет продлить график временного ряда.

Алгоритм выполнения такого прогноза описан здесь, линейная регрессия или тройное экспоненциальное сглаживание. Результаты прогноза видны ниже, рядом с графиком факта появляется пунктирная линия прогноза и доверительный интервал.

Также прогноз можно добавлять в таблицы и использовать оттуда данные, например, как базу для будущего плана.

Таким образом, процесс прогнозирования в SAP Analytics Cloud достаточно прост и максимально ориентирован на бизнес-пользователя, но дает возможность получения прогностических моделей высокого качества для решения типовых бизнес-задач.

Данный инструмент является stand-alone решением и может стать частью мультивендорной архитектуры. Интеграция SAP Analytics Cloud с решениями SAP нативна. Кроме того, существует стандартный бизнес контент для различных индустрий и линий бизнеса. Отчеты могут быть дополнены прогнозными и плановыми данными, а также обогащены функциями расширенной аналитики.

Автор Анастасия Николаичева, архитектор бизнес-решений SAP CIS

Подробнее..

Подробный обзор Google Analytics 4 новые возможности, преимущества и недостатки

07.11.2020 12:07:53 | Автор: admin

Около года назад команда Google презентовала функционал App + Web, который позволяет объединять данные сайтов и мобильных приложений в одном ресурсе GA. Все это время новый тип ресурса тестировался, менялся, дорабатывался и вот наконец вышел из бета-версии под другим названием. Знакомьтесь, Google Analytics 4.

Аналитики OWOX подготовили полный обзор того, что такое Google Analytics 4, чем он отличается от Universal Analytics, какую ценность дает бизнесу и какие проблемы можно решить с его помощью. Также разберем, каким компаниям стоит использовать новый тип ресурса уже сейчас.

Что такое Google Analytics 4

Это новый тип ресурса в GA. Он выглядит немного по-другому, чем ресурс в Universal Analytics (UA), настраивается гораздо проще и быстрее. В презентационных материалах команда Google неоднократно называет новый тип ресурса будущим аналитики, аргументируя это следующими причинами:

  1. Масштабируемая кроссплатформенная аналитика, которая строится вокруг событий (events).

  2. В новом ресурсе всем пользователям GA доступны ML и NLP функции.

  3. В приоритете сохранение конфиденциальности, в том числе избавление от необходимости устанавливать cookies.

  4. Бесшовная интеграция со всеми продуктами Google.

  5. Межплатформенная идентификация пользователей. В новом ресурсе можно увидеть весь путь пользователя, то есть все его действия на разных девайсах и платформах.

Давайте рассмотрим эти преимущества подробнее.

Масштабируемая кроссплатформенная аналитика

Подход, основанный на событиях, позволяет собирать надежные и согласованные данные на нескольких устройствах и платформах.

В стандартной веб-версии GA все строится вокруг сессий пользователей, а в Firebase вокруг событий. Поэтому сложно анализировать переход пользователя между платформами, так как нет универсального мерила поведения пользователя. Даже имея сырые данные, нужно приложить немалые усилия, чтобы построить качественный user flow.

Google Analytics 4 предлагает объединить всю аналитику вокруг событий. Что позволит собирать одинаковые стандартизированные данные по всем устройствам и платформам. За счет этого повышается качество данных и можно будет получить единый отчет по всему пути пользователя.

Машинное обучение

Одним из главных преимуществ Google Analytics 4 являются машинное обучение и NLP функции, которые применяются для того, чтобы:

  • Предсказывать вероятность конверсии и создавать на основе этих прогнозов аудитории для Google Ads.

  • Предупреждать вас о важных тенденциях в данных. Например, о товарах, на которые растет спрос из-за того, что меняются потребности пользователей.

  • Находить аномалий в отчетах.

  • Предсказывать вероятность оттока клиентов, чтобы вы могли более эффективно инвестировать деньги в их удержание:

Источник: https://blog.google/ Источник: https://blog.google/

Команда Google планирует и дальше развивать это направление и добавлять новые прогнозные показатели, например ARPU, чтобы все пользователи нового ресурса могли скорректировать маркетинговую стратегию и повысить свой ROI с помощью ML-инсайтов.

Конфиденциальность главный приоритет

  1. Google Analytics 4 ориентирован на конфиденциальность. Для его внедрения используется библиотека gtag.js, которая работает без файлов cookies. Соответственно, можно ожидать, что в ближайшем будущем Google откажется от Client ID и будет полагаться только на внутренние идентификаторы устройства и браузера, а также кроссплатформенный идентификатор, который генерируется в CRM, то есть User ID.

  2. IP-анонимизация в GA 4 настроена по умолчанию, и ее нельзя изменить.

Бесшовная интеграция с инструментами Google

Пока что самая передовая фича в этом разделе это интеграция с YouTube. Google активно работает над тем, чтобы повысить качество оценки YouTube кампаний. К примеру, отслеживать view-through конверсии. Это позволит вам найти ответы на вопросы:

  • Как моя рекламная YouTube кампания влияет на достижение конкретных показателей вовлечения?

  • Как она влияет на показатель отказов (bounce rate), те или иные события на сайте (не обязательно конверсии) и т.д?

Благодаря более глубокой интеграции с Google Ads вы сможете создавать аудитории и запускать кампании, которые будут привлекать новых клиентов благодаря более релевантным и полезным предложениям, каким бы устройством они ни пользовались.

Кроме того, в Universal Analytics функция BigQuery Export доступна только пользователям платной версии, а в GA4 эта возможность бесплатна для всех. Активировать сбор данных в облачное хранилище Google BigQuery вы можете в настройках ресурса.

Межплатформенная идентификация пользователей

Google Analytics 4 считает реальных пользователей, которые взаимодействовали с вашей компанией, а не устройства и браузеры, которыми они пользовались.

В новом ресурсе используются 3 уровня идентификации:

  • User_id

  • Google Signals

  • Device_id

Благодаря внедрению аналитики на базе событий можно точнее отслеживать путь пользователя от первого касания до конверсии и повторных заказов. Причем, если пользователь на разных устройствах совершил одно и то же событие, эти данные сольются в единую точку касания. Например, если он положил товар в корзину и в смартфоне и на ноутбуке, то событие Добавить в корзину посчитается один раз.

Отличия Google Analytics 4 от Universal Analytics

Давайте сравним ключевые концепции отслеживания в Universal Analytics и Google Analytics 4:

Universal Analytics web

Google Analytics 4

Page Views / Screen Views

Events

Sessions

Hit Types: page, event, ecommerce, social

Custom Dimensions, Custom Metrics

Content Groupings

User ID

Client ID

Events

Event Parameters

User Properties

User ID

В Google Analytics 4:

  • Аналитика строится не вокруг сессий, а вокруг событий. Поскольку сессии это искусственный концепт, Google предлагает отказаться от него. Если вам нужны данные о сессиях, вы можете сформировать их самостоятельно, работая с сырыми данными в Google BigQuery.

  • Есть сквозные настройки сбора данных для всего сайта и те, которые меняются с каждым событием.

  • В GA 4 предусмотрены встроенные сквозные отчеты по userid, для использования userid не нужно создавать отдельное представление.

В GA 4 по аналогии с Firebase будет три типа событий и их параметров.

3 типа событий и параметров в Google Analytics 4

Рекомендуемые и кастомные события внедряются самостоятельно.

Каждое событие может иметь дополнительные параметры

Пользовательские определения (Custom Dimensions) это те параметры и показатели, которые являются сквозными для большинства отчетов и помогают уложиться в лимиты GA 4.

Нет категории, действия и ярлыка события

В Google Analytics 4 нет таких понятий как категория, действие и ярлык события.

Для существующих настроек и собираемых данных эти свойства сопоставляются с параметрами настраиваемого события. Если вы хотите видеть свойства в отчетах GA 4, то их необходимо зарегистрировать.

Просмотры страницы стали событием pageview

Это событие собирается автоматически, если у вас реализован фрагмент "config" gtag.js.

У события pageview есть предустановленные параметры:

  • page_location

  • page_path

  • page_title

  • page_referrer

Сессии и подсчет сессий в Google Analytics 4

В отчетах GA 4 сессии будут, но считаться они будут иначе, чем в Universal Analytics:

  1. Сессия инициируется автоматически собираемым событием session_start.

  2. Продолжительность сеанса это промежуток между первым и последним событиями.

  3. Взаимодействия распознаются автоматически (отправка события взаимодействия не требуется).

  4. Таймаут обработки поздних обращений составляет 72 часа (против 4 часов в UA Properties). Если вы сравните количество сессий в отчете GA 4 и Universal Analytics, то можете столкнуться с тем, что сессий в GA 4 меньше. Потому что хиты, которые были отправлены после завершения сессии, могут присваиваться к правильной сессии в течение 72 часов. Соответственно, отчеты по сессиям будут формироваться дольше.

  5. В настоящее время в ресурсе GA 4 невозможно настроить продолжительность сессии.

Пользовательские параметры и показатели

Чтобы Custom Dimensions & Metrics попали в отчеты GA 4, их необходимо перенести в новый ресурс по правилам, которые предлагает Google. Если параметры уровня хита и пользователя имеют аналоги в GA4, то для параметров уровня сессии эквивалента нет. Как вариант можно определять их на уровне хита.

Чтобы пользоваться кастомными определениями уровня продукта, их необходимо добавлять отдельно. Пока что непонятно, как это будет работать, потому что фича еще в разработке и нет отчетов по Ecommerce, которые бы содержали кастомные определения уровня продукта.

Universal Analytics

Google Analytics 4

Hit-scoped

User-scoped

Session-scoped

Product-scoped

Events or event parameters

User properties

No equivalent

E-commerce parameters (COMING SOON)

Свойства пользователя (New)

В GA 4 появляется новая фича Свойства пользователя (User Properties).

Это определения, которые соответствуют конкретной аудитории / конкретному пользователю. Например, пол, город, признак новый или вернувшийся покупатель, признак постоянный клиент и т.д.

Свойства, которые касаются конкретных пользователей, распространяются на все их поведение. На основании свойств пользователя формируются аудитории для персонализации рекламы.

Кто выиграет от перехода на Google Analytics 4 уже сейчас

Вам уже стоит внедрять Google Analytics 4, если:

  • Сбор данных на вашем сайте организован через Data Layer и Google Tag Manager.

  • Чем меньше у вас тегов, тем лучше нужно будет внести минимальные корректировки.

  • Вы активно используете YouTube Ads и ремаркетинг на базе User-ID.

  • Вы активно использует Firebase, ваша команда знакома с логикой сбора данных Firebase, а также со схемой данных таблиц экспорта App + Web (Firebase) в BigQuery.

Чем быстрее вы перейдете на GA 4, тем быстрее у вас начнут собираться исторические данные, тем больше будет информации для принятия решений и тем быстрее вы получите ценность от ML-инсайтов. Как мы уже убедились, структура данных и логика их сбора у GA 4 и Universal Analytics значительно отличаются. Поэтому объединить данные двух ресурсов будет проблематично.

Почему не стоит спешить с переходом

Вы можете столкнуться с проблемами при внедрении Google Analytics 4, если:

  • В качестве основного метода отслеживания используется насайтовый код.

  • В качестве основного метода отслеживания используется GTM, и при этом в контейнере много тегов. Особенно если теги привязаны к автособытиям.

  • У вас большой сайт, с множеством поддоменов, каждый из которых отслеживается отдельно.

  • Нет системы метрик унифицированных названий и значений событий и их параметров, единого подхода к иерархии событий. В этом случае непонятно, какие события важнее добавить в интерфейс GA в первую очередь, а с чем лучше повременить.

  • У вас есть сайт и приложения, вы планируете внедрить по ним сквозную аналитику, при этом у сайта с приложениями нет общей иерархии событий.

  • Ваша команда еще не работала с сырыми данными в BigQuery, не знакома с принципами работы Firebase Analytics / App + Web и со схемой выгрузки.

Поэтому мы рекомендуем сначала выстроить общую логику сбора данных и только потом заниматься внедрением, иначе можно очень быстро исчерпать свободные слоты для пользовательских параметров.

Если у вас нет выстроенной схемы сбора данных, можно насобирать в BigQuery бесполезные события и столкнуться с ограничением экспорта + платить за хранение мусорных данных, которые никому не пригодятся. Например: события скролла, просмотры баннеров и др.

Главный недостаток GA 4, на наш взгляд схема экспорта данных в GBQ, в которой ключевые параметры событий и пользователей хранятся во вложенных полях. Это значит, что для получения нужной информации из таблиц GA 4 потребуется обработать в разы больший объем данных по сравнению со стримингом OWOX BI или BigQuery экспортом в стандартном Google Analytics 360.

В каких еще случаях вам не подойдет Google Analytics 4

  • Если новым ресурсом должны пользоваться несколько команд одновременно. На данный момент в GA 4 нет представлений, а управление доступами еще не внедрено.

  • Если вы хотите анализировать расходы и ROAS по не-Google кампаниям в новом ресурсе пока нет импорта данных.

  • Если нужно экспортировать конверсии в Search Ads 360 и Display & Video 360 интеграция с другими продуктами Google еще не работает на полную мощность.

Как перейти на Google Analytics 4

Google и аналитики OWOX рекомендуюет пока что использовать обе версии ресурсов GA. Для этого:

  1. Создайте и настройте ресурс GA 4.

  2. Добавьте код отслеживания вручную или через GTM. Рекомендуем использовать Менеджер тегов, потому что это удобнее и быстрее.

  3. Продумайте, какие события и параметры вы хотите собирать в новый тип ресурса.

  4. Используйте два типа ресурса одновременно, чтобы сравнивать, как собираются данные.

  5. Пожалуйста, обратите внимание:

    1. в один ресурс GA 4 можно добавить ТОЛЬКО один проект Firebase.

    2. при этом в один ресурс GA 4 можно настроить несколько потоков из разных приложений.

Резюме

  1. Это самый кардинальный update логики GA: теперь все строится вокруг событий (events), параметров событий и пользователей (users), а не вокруг сессий как раньше.

  2. Кроссплатформенная аналитика между сайтом и приложениями из коробки одна из ключевых фишек и драйверов GA 4.

  3. Для настройки нового ресурса GA4 можно использовать уже настроенный GA через gtag.js или GTM.

  4. При настройке GA 4 автоматически создается новый ресурс WP и только с момента настройки в него начнут собираться данные. Из старых WP данные не мигрируют.

  5. Команда Google не призывает всех отказываться от старого GA и переходить на новый. Они рекомендуют запускать параллельно новый Google Analytics 4 и начать собирать в него данные. Источником исторических данных пока остается стандартный GA.

  6. Пока в новом GA 4 есть недочеты и еще не все фичи доступны, разработчики выкатывают их постепенно.

  7. Возможности импортировать в Google Analytics 4 расходы из не-Google источников пока что нет.

  8. Можно настроить бесплатно выгрузку данных из GA 4 в GBQ. Схема экспорта та же, что и в Firebase.

  9. Вы уже можете настраивать ресурсы GA 4 и начинать собирать данные. Чем раньше настроить, тем больше исторических данных будет собрано.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru