Анализ результатов AB-тестирования контрольной и тестовой групп, в которых проверялось внедрение новой рекомендательной системы для приложения знакомств
Команда разработки приложения разработала новый алгоритм для поиска наиболее подходящих анкет. Для проверки работы алгоритма был проведен A/Б-тест, в котором все пользователи были разделены на 2 группы (контрольную и тестовую). Пользователи в контрольной группе (группа 0) пользовались приложением со старым алгоритмом, а пользователи тестовой группы (группа 1) - с новым.
Правда ли, что новый алгоритм улучшил качество сервиса?
Давайте узнаем!
Перед проведением анализа необходимо "предварительно" посмотреть результирующий набор данных, чтобы сформировать первое представление о том, с какими вообще данными мы имеем дело.
- Количество строк - 14514
- Количество столбцов - 4
- NULL-значения: отсутствует
- Типы данных: Int64
- Размер контрольной и тестовой групп: ~500 человек
- Измеряемая метрика: is_match (бинарная)
В первую очередь, для интерпретации результатов, необходимо понять, каким образом можно оценить изменение качества работы сервиса, т.е какая метрику необходимо измерить до и после введения новой рекомендательной системы
В данном случае наше нововведение - это алгоритм для поиска наиболее подходящих анкет - поиск тех анкет, которые отвечают взаимностью (взаимные лайки). Выделим для себя несколько потенциальных метрик, на которые потенциально может влиять новый алгоритм
- Общее количество взаимных лайков по продукту
- Доля взаимных лайков от общего количества лайков на 1 пользователя
Можно начать анализ с проверки того, как введение нового алгоритма могло повлиять на общее количество взаимных лайков по продукту. Можно ли с помощью нового алгоритма увеличить общее количество взаимных лайков?
Визуализация распределения общего количества лайков показала, что в группе с новым алгоритмом, количество взаимных лайков выросло в сравнении с контрольной группой, которая использует старый алгоритм. Однако, это может быть связано с тем, что у нас в целом выросло количество лайков и относительный показатель количества взаимных лайков мог остаться прежним.
Сформируем таблицу сопряженности для 2-х категориальных переменных (group, is_match) с 2-мя градациями (0, 1) и проведем анализ с помощью X^2-критерия, убедившись перед этим, что у нас соблюдаются все условия для его проведения, и проверим гипотезу о том, что новый алгоритм увеличивает количество взаимных лайков по продукту
- Все наблюдения независимы
- Минимальное количество наблюдений в каждой ячейке больше 5
При расчете статистики X^2 получили следующие результаты:
stat, p, dof, freqs = stats.chi2_contingency(matches_by_groups)
- X^2-статистика: 618,7
- p_value: 1.44e-136
Ожидаемая таблица сопряженности:
Группа | Невзаимные лайки | Взаимные лайки |
---|---|---|
Контрольная | 3193 | 1599 |
Тестовая | 6478 | 3244 |
Наблюдаемая таблица сопряженности:
Группа | Невзаимные лайки | Взаимные лайки |
---|---|---|
Контрольная | 3858 | 934 |
Тестовая | 5813 | 3909 |
Видим, что наше p_value сильно меньше заявленного 0.05, из чего можем сделать вывод, что распределение по группам действительно влияет на количество лайков. Но пока что это нам не говорит о том, какие именно показатели сильно отличаются между собой. Чтобы это узнать нужно провести анализ остатков (хотя бы визуальный)
После визуализации стало видно, что на самом деле, количество взаимных лайков в тестовой группе (с новым алгоритмом) имеет более высокое значение, нежели при распределении, в котором фактор группы никак не оказывает влияние на распределение частот лайков.
Теперь нужно проверить, насколько сильно мы можем доверять результатам статистической проверки. Для этого необходимо рассчитать размер эффекта - степень отклонения полученных наблюдений от наблюдений, в которых размер эффекта равен нулю. Это позволит нам сравнить полученный в результате внедрения нового алгоритма эффект с наименьшим истинным эффектом, который может быть обнаружен при заданных уровне значимости и размере выборки с вероятностью 80%. То есть по факту, мы проверяем насколько большая у нас вероятность совершить ошибку II рода при используемых параметрах эксперимента
Важно понимать, что минимальный детектируемый эффект определяется перед проведением A/Б-тестирования, на этапе расчета длительности проведения эксперимента (длительность зависит от размера выборки, который необходимо собрать, чтобы с большой вероятностью правильно задетектировать изменения там, где они действительно присутствуют). Однако, так как мы имеем уже результаты A/Б-тестирования, то мы рассчитаем его уже по факту, предполагая, что именно такой MDE хотели обнаружить аналитики при определенных значениях мощности теста, уровня значимости и размера выборки.
Рассчитаем размер эффекта для таблицы сопряженности 2 X 2. Это позволит нам понять, насколько значительная степень отклонения наблюдаемых значений от значений, размер эффекта который равняется 0. Для такой таблицы сопряженности рассчитаем Phi.
effect_size = np.sqrt(stat / 1000)
Полученный размер стандартизированного эффекта = 0.79
Данный размер эффекта - это значительный эффект. Можем сказать, что степень отклонения от значений, где размер эффекта равен 0, внушительная. Однако, теперь нужно сравнить стандартизированный размер эффекта, полученный в результате эксперимента с MDE для критерия X^2 с используемыми параметрами. Это позволит нам сказать, можем ли мы доверять полученным результатам проверки X^2 критерием или вероятность ошибки II рода слишком высока.
power_analysis = GofChisquarePower()
mde = power_analysis.solve_power(nobs=500, alpha=0.05, power=0.8, n_bins=4)
Минимальный детектируемый эффект для данных параметров = 0.15
Видно, что полученный размер эффекта между метриками в контрольной и тестовой группах превышает минимальный детектируемый эффект для используемых параметров. Следовательно, при интерпретации полученных результатов, можем быть уверены в их достоверности
Теперь необходимо посмотреть, как влияет введение нового алгоритма на долю взаимных лайков 1 пользователя.
Перед проведением сравнения 2-х средних значениях критерием Стъюдента, также, как и при расчете общего количества лайков, необходимо рассчитать минимальный детектируемый эффект - наименьший истинный эффект, который может быть определен критерием Стъюдента с определенной вероятностью при определенном уровне значимости. В нашем случае у нас есть следующие параметры:
- Уровень значимости
- Мощность теста
- Размер выборки
power_analysis = TTestIndPower()
mde = power_analysis.solve_power(nobs1=nobs1, alpha=0.05, power=0.8)
Минимальный детектируемый эффект для данных параметров: 0.18
Можем сказать, что данный размер эффекта является довольно-таки слабым по шкале интерпретации Коэна. Однако, даже незначительные размеры могут оказывать огромное влияние, особенно когда-речь заходит о крупных абсолютных величинах.
При отборе выборок для интерпретации результатов важно понимать, что у нас в данных присутствуют строки для 2-х пользователей и результат взаимности между ними. Однако, мы не знаем о том, присутствует ли у нас та же "взаимная" пара, но уже в виде не "X - Y", a "Z - X" или Y - X. Мы должны учитывать, что один и тот же пользователь может быть в разных столбцах, так как взаимный лайк для "X" в паре "X - Y" и в паре "Z - X" или Y - X - это разные взаимные лайки. Здесь мы обходим зависимость между наблюдениями, из-за неучёта которой у нас может завышаться вероятность ошибки I рода.
X - рассматриваемый пользователь Y - анкета, которую он лайкнул Z - другая анкета, которую он лайкнул
У нас в данных есть пользовательские пары, которые взаимодействовали друг с другом несколько раз (пример: группа 1, пользователь 79 - пользователь 890) - такие случаи исключать не будем, так как в одном случае не было match, а в другом - был.
Сперва мы найдем средние значения доли лайков и потом сравним степень отклонения - effect size - между исследуемыми данными и MDE для того, чтобы понять можем ли мы доверять дальнейшим полученным результатам или вероятность того, что мы совершим ошибку при интерпретации результатов будет велика.
- Среднее контрольной группы = 0.194
- Среднее тестовой группы = 0.403
lift = (test_sample.mean() / control_sample.mean()) - 1
Важно не путать размер эффекта и относительное изменение значения одной метрики от другой. Второй показатель называется lift, который в нашем случае составил 107.82% - именно такой прирост во взаимных лайках на пользователя произошел в тестовой группе относительно контрольной группы
pooled_var = calc_pooled_var(control_var, test_var, nobs1, nobs2)
effect_size = (test_sample.mean() - control_sample.mean()) / pooled_var
Стандартизированный размер эффекта = 2.43
Можем сказать, что полученный размер эффекта (2.43) намного превышает MDE (0.18), из чего следует, что при данных используемых параметрах, таких как уровень значимости, мощность теста и размер выборки, очень большая вероятность того, что полученные различия (или их отсутствие) будут интерпретированы верно
На самом деле, видно невооруженным глазом, что в группе, в которой применялся новый алгоритм, средняя доля взаимных лайков выросла почти в 2 раза (0.19 - контрольная, 0.40 - тестовая). Однако, все равно необходимо подтвердить статистически значимое различие между 2-мя средними с помощью статистических критериев
Однако перед тем, как начать интерпретировать результаты необходимо понять, какой статистический критерий будем использовать для оценки среднего. У нас есть 2 варианта: параметрический или непараметрический. Для того, чтобы использовать параметрический тест необходимо, чтобы распределение данных соответствовало нескольким условиям:
- Независимость данных
- Нормальность распределения данных
- Отсутствие выбросов
- Гомогеннодость дисперсий
Давайте проверим наши данные! Сперва посмотрим на распределение.
Видно, что в обеих выборках распределение выглядит нормальным, однако сделаем несколько более "профессиональных" проверок на нормальность распределения. Теперь посмотрим на qq-plots.
В данном случае смущают отклонения в контрольной выборке, поэтому попробуем проверить еще несколькими расчетными статистическими критериями. Уровень значимости выбран - 0.05. Можем воспользоваться несколькими статистическими критериями для проверки гипотезы о нормальности распределения выборочных данных, такие как:
- Шапиро-Уилка (нормально работает только на выборках до 1000)
- Харке-Бера
- Д'агостино-Пирсона
В нашем случае будем пользоваться критерием Шапиро-Уилка, так как он наиболее чувствителен среди прочих и размер выборки удовлетворяет условиям его проведения. Критерий Шапиро-Уилка показал, что в случае контрольной группы распределение выборочных данных является ненормальным, а в тестовой группе - нормальное.
shapiro_results = { 'control_res' : stats.shapiro(control_sample), 'test_res' : stats.shapiro(test_sample) }
Результаты проверки данных контрольной группы: stat = 0.993, p_value = 0.027
Результаты проверки данных тестовой группы: stat = 0.997, p_value = 0.593
Так как у нас одно из распределений является ненормальным (если было бы 2 распределения, то алгоритм был был тот же), у нас есть несколько вариантов развития событий: мы можем попробовать "нормализовать данные" путем логарифмирования или преобразования Бокса-Кокса, можем использовать непараметрический тест Манна-Уитни, а также можем использовать обычный t-критерий Стъюдента, так как наши распределения выглядят неплохо + у нас довольно-таки большой размер выборки (часто в таких ситуациях используют t-тест).
Мы выбираем 3-ий вариант, так как он допустим и прост в восприятии и оценки результата проверки. Однако, нужно посмотреть на гомогенность (или ее отсутствие) в изменчивости данных в разных группах, чтобы понять, будем ли мы использовать стандартный t-тест или тест Уэлча для данных с неодноростью дисперсий. Используем тест Левена, так как он более робастный к отклонениям от нормальности, нежели тест Бартлетта. Он проверяет гипотезу о том, что в наших выборках дисперсия является одинаковой.
stats.levene(control_sample, test_sample)
Проверка гомогенности дисперсии критерием Левена показала следующие результаты: stat = 7.374, pvalue = 0.006
У нас есть все основания отклонить нулевую гипотезу о равенстве дисперсий. Поэтому для сравнения средних мы будем использовать T-Test Уэлча
stats.ttest_ind(control_sample, test_sample, equal_var = False)
Проверка равенства 2-х средних значений критерием Уэлча показала следующие результаты: stat = -38.368, pvalue = 7.166e-198
Проверка t-критерием Уэлча продемонстрировала, различия в тестовой и контрольной группе - статистически значимы, и можем с большой долей вероятности отклонить нулевую гипотезу об отсутствии различий между средними значениями и утверждать, что среднее значение доли взаимных лайков выросло почти в 2 раза.
На основании полученных результатов можно сделать следующие выводы.
Проверка статистическими критериями (Кси-квадрат Пирсона и t-критерий Уэлча) показали, что различия между контрольной и тестовой группой являются статистическими значимыми, как для общего количества взаимных лайков по продукту, так и для доли взаимных лайков на пользователя. Более того, после проверки размеров эффекта для 2-х метрик, выяснили, что полученным результатам сравнения метрик можно доверять, так как размеры эффекта больше минимальных детектируемых эффектов при используемых параметрах (уровня значимости, мощности теста и размеров выборки).
В обоих случаях, количество взаимных лайков в тестовой группе выше. Это может означать то, что новый алгоритм отлично справляется со своей целью, так как его основная задача - поиск наиболее подходящих анкет (другими словами поиск взаимных лайков) и улучшил качество сервиса, основная цель которого - найти подходящую анкету.