ABTest_Lab / src /app.py
HardWorkingStation
Initial commit
7f1317b
raw
history blame contribute delete
No virus
25.8 kB
import pandas as pd
import numpy as np
import streamlit as st
import tools
STEP_2 = STEP_3 = STEP_4 = STEP_5 = STEP_6 = False
st.set_page_config(
page_title="A/B Tests", page_icon="📈", initial_sidebar_state="expanded"
)
hide_menu_style = """
<style>
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_menu_style, unsafe_allow_html=True)
st.title('A/B tests lab')
st.image('images/main.jpg')
st.write(
"""
*Внедрять компании новый сервис или нет? Как принять правильное решение?*
*Поможет А/В-тестирование.*
A/B-тестирование, или сплит-тестирование (англ. A/B testing; Split testing, от англ. «разделять») —
техника проверки гипотез. Позволяет оценить, как изменение сервиса или продукта повлияет на пользователей.
Проводится так: аудиторию делят на две группы — контрольную (A) и тестовую (В). Группа A видит начальный сервис,
без изменений. Группа B получает новую версию, которую и нужно протестировать.
Эксперимент длится фиксированное время или по количеству пользователей.
В ходе тестирования собираются данные о поведении пользователей в разных группах.
Если ключевая метрика в тестовой группе выросла по сравнению с контрольной, новую функциональность внедряют.
"""
)
st.image('images/ab-structure.png', width=700)
st.write(
"""
Кому нужно A/B-тестирование
1. _Продакт-менеджеры_ могут тестировать изменения ценовых моделей, направленные на повышение доходов, или оптимизацию части воронки продаж для увеличения конверсии.
2. _Маркетологи_ могут тестировать изображения, призывы к действию (call-to-action) или практически любые другие элементы маркетинговой кампании или рекламы с точки зрения улучшения метрик.
3. _Продуктовые дизайнеры_ могут тестировать дизайнерские решения (например, цвет кнопки оформления заказа) или использовать результаты тестирования для того, чтобы перед внедрением определить, будет ли удобно пользоваться новой функцией.
"""
)
st.markdown(
"""
Вот шесть шагов, которые нужно пройти, чтобы провести тестирование.
В некоторые из пунктов включены примеры тестирования страницы регистрации выдуманного стартапа.
"""
)
with st.expander('Шаг 1. Определите цели', expanded=True):
st.write(
"""
Определите основные бизнес-задачи вашей компании и убедитесь, что цели A/B-тестирования с ними совпадают.
Например, можем выпустить обновление приложения и проверить на маленькой группе,
что обновление не портит пользовательский опыт. Если метрики не падают, можем выкатывать обновление на всех.
"""
)
purpose = st.radio(
'Цели',
options=[
'Занять делом скучающих сотрудников',
'Решить проблему пользователей',
'Снизить риски при значительных изменениях',
'Обеспечить статистически значимые улучшения'
]
)
if 'Занять делом скучающих сотрудников' in purpose:
st.error(
"""
Этой цели мы безусловно добьемся, но бизнесу от этого легче не станет.
"""
)
if 'Решить проблему пользователей' in purpose:
st.info(
"""
Посетители приходят на сайт с конкретной целью: больше узнать о продукте или услуге, что-то купить,
изучить тему или просто поглазеть. При этом пользователи с разными целями сталкиваются с
общими проблемами. Например, кнопка «Купить» расположена неудобно и её сложно найти.
Такие нюансы формируют негативный пользовательский опыт (пользоваться сайтом неудобно)
и влияют на конверсию.
Это актуально для всех сфер: будь то электронная коммерция, туризм, SaaS, образование,
СМИ или издательский бизнес.
"""
)
st.error('Да, но сегодня мы будем добиваться другой цели. Выберите другую.')
if 'Снизить риски при значительных изменениях' in purpose:
st.info(
"""
Рекомендуем вносить небольшие и последовательные изменения вместо того, чтобы одновременно делать
редизайн всей страницы. Так снизится вероятность ухудшения коэффициента конверсии.
A/B-тесты позволяют получать хороший результат и при этом вносить лишь небольшие изменения,
что приводит к увеличению ROI.
В качестве примера приведём изменения в описании продукта. Вы можете сделать A/B-тест,
когда нужно удалить или обновить описание продукта, но при этом не знаете, как посетители будут
реагировать на это.
Другой пример модификации с низким риском — добавление новой функции. A/B-тест поможет
сделать результат внедрения более предсказуемым.
"""
)
st.error('Да, но сегодня мы будем добиваться другой цели. Выберите другую.')
if 'Обеспечить статистически значимые улучшения' in purpose:
st.info(
"""
A/B-тестирование полностью основано на данных и не оставляет места для догадок.
Поэтому можно легко определить «победителя» и «проигравшего» на основе статистически значимых
улучшений: показателей времени на странице, число запросов пробников, количество
брошенных корзин, CTR.
"""
)
st.success('Да, попробуем добиться статистически значимого улучшения метрики.')
STEP_2 = True
if STEP_2:
with st.expander('Шаг 2. Определите метрику', expanded=True):
st.write(
"""
На данном этапе необходимо определить метрику, на которую вы будете смотреть, чтобы понять, является ли
новая версия сайта более успешной, чем изначальная. Обычно в качестве такой метрики берут
коэффициент конверсии, но можно выбрать и промежуточную метрику вроде показателя кликабельности (CTR).
"""
)
metrick = st.radio(
'Цели',
options=[
'Обеспечить лучшую окупаемость инвестиций (ROI)',
'Уменьшить показатель отказов',
'Повысить конверсию',
]
)
if 'Обеспечить лучшую окупаемость инвестиций (ROI)' in metrick:
st.info(
"""
Маркетологи знают, каким дорогим бывает качественный трафик. A/B-тестирование позволяет эффективно
использовать существующий трафик и помогает повысить конверсию без затрат на привлечение нового.
Иногда даже незначительные изменения влияют на конверсию.
"""
)
st.error('Сегодня мы будем тестировать не эту метрику. Выберите другую.')
if 'Уменьшить показатель отказов' in metrick:
st.info(
"""
Для оценки эффективности сайта важно отслеживать показатель отказов.
Люди покидают сайт по разным причинам: слишком много вариантов товара, несоответствие ожиданиям
и другие. Поскольку сайты различаются по аудиториям и целям, нет универсального надёжного способа
определения показателя отказов.
Но решение есть: в каждом случае поможет A/B-тестирование. Можно протестировать несколько вариантов расположения
элементов на сайте и найти оптимальное решение.
"""
)
st.error('Сегодня мы будем тестировать не эту метрику. Выберите другую.')
if 'Повысить конверсию' in metrick:
st.info(
"""
Конверсия — один из главных терминов в маркетинге. Не считая конверсию, сложно
оценить эффективность маркетинга и работать с воронкой продаж.
Конверсия показывает, какой процент пользователей или потенциальных клиентов совершили
целевое действие: оставили заявку, купили товар, подписались на рассылку и так далее.
"""
)
st.success('Правильно! Именно эту метрику мы и будем оптимизировать')
STEP_3 = True
if STEP_3:
with st.expander('Шаг 3. Разработайте гипотезу', expanded=True):
st.write(
"""
Затем нужно разработать гипотезу о том, что именно поменяется, и, соответственно, что вы хотите проверить.
Нужно понять, каких результатов вы ожидаете и какие у них могут быть обоснования.
Нужно определить две гипотезы, которые помогут понять, является ли наблюдаемая разница между версией
A (изначальной) и версией B (новой, которую вы хотите проверить) случайностью или результатом изменений,
которые вы произвели.
* _Нулевая гипотеза_ предполагает, что результаты, А и В на самом деле не отличаются и что наблюдаемые различия случайны. Мы надеемся опровергнуть эту гипотезу.
* _Альтернативная гипотеза_ — это гипотеза о том, что B отличается от A, и вы хотите сделать вывод об её истинности.
Решите, будет ли это односторонний или двусторонний тест.
Односторонний тест позволяет обнаружить изменение в одном направлении,
в то время как двусторонний тест позволяет обнаружить изменение по двум направлениям
(как положительное, так и отрицательное).
"""
)
st.radio(
"Тип теста",
options=["Односторонний", "Двусторонний"],
index=0,
key="hypothesis",
help="Односторонний тест позволяет обнаружить изменение в одном направлении, в то время как двусторонний тест позволяет обнаружить изменение по двум направлениям (как положительное, так и отрицательное). ",
)
STEP_4 = True
if STEP_4:
with st.expander('Шаг 4. Подготовьте эксперимент', expanded=True):
st.write(
"""
1. _Создайте новую версию (B)_, отражающую изменения, которые вы хотите протестировать.
2. _Определите контрольную и экспериментальную группы_.
Каких пользователей вы хотите протестировать:
всех пользователей на всех платформах или только пользователей из одной страны? Определите группу испытуемых,
отобрав их по типам пользователей, платформе, географическим показателям и т.п.
Затем определите, какой процент исследуемой группы составляет контрольная группа (группа, видящая версию A),
а какой процент — экспериментальная группа (группа, видящая версию B). Обычно эти группы одинакового размера.
3. _Убедитесь, что пользователи будут видеть версии A и B в случайном порядке_.
Это значит, у каждого пользователя будет равный шанс получить ту или иную версию.
4. _Определите уровень статистической значимости (α)_.
Это уровень риска, который вы принимаете при ошибках первого рода (отклонение нулевой гипотезы, если она верна), обычно α = 0.05.
Это означает, что в 5% случаев вы будете обнаруживать разницу между A и B,
которая на самом деле обусловлена случайностью. Чем ниже выбранный вами уровень значимости,
тем ниже риск того, что вы обнаружите разницу, вызванную случайностью.
5. _Определите минимальный размер выборки_. Калькулятор есть [здесь](https://vwo.com/tools/ab-test-sample-size-calculator/).
Он рассчитывают размер выборки, необходимый для каждой версии. На размер выборки влияют разные параметры и ваши предпочтения.
Наличие достаточно большого размера выборки важно для обеспечения статистически значимых результатов.
6. _Определите временные рамки_. Калькулятор есть [здесь](https://vwo.com/tools/ab-test-duration-calculator/).
Возьмите общий размер выборки, необходимый вам для тестирования каждой версии,
и разделите его на ваш ежедневный трафик. Так вы получите количество дней,
необходимое для проведения теста. Как правило, это одна или две недели.
У A/B-теста есть проблема подглядывания (англ. peeking problem): общий результат искажается, если новые данные
поступают в начале эксперимента. Каждый, даже небольшой фрагмент новых данных, велик относительно уже
накопленных — статистическая значимость достигается за короткий срок.
"""
)
st.image('images/peeking_problem.png', width=670)
st.write(
"""
На графике разница конверсии между сегментами, полученная в результате смоделированного A/B-теста.
Данные собирали из одной генеральной совокупности, и различий в выборочных средних быть не должно.
Но из-за флуктуаций (от лат. fluctuatio, колебание) в первые дни тестирования была достигнута
статистическая значимость. Если бы это был реальный, а не смоделированный тест, принятое по достижении
статистической значимости решение было бы неверным.
Чтобы избежать проблемы подглядывания, размер выборки определяют ещё до начала теста.
"""
)
st.slider(
"Уровень значимости (α)",
min_value=0.01,
max_value=0.10,
value=0.05,
step=0.01,
key="alpha",
help="Это уровень риска, который вы принимаете при ошибках первого рода (отклонение нулевой гипотезы, если она верна), обычно α = 0.05.",
)
ab_test_duration = st.select_slider(label='Выберите длительность A/B теста в днях', options=range(3, 31))
mean_traff = st.number_input(label='Укажите, среднюю посещаемость сайта в сутки', min_value=150)
ab_test_sample_size = st.select_slider(label=f'Укажите размер выборки для группы B (при 20% от средней посещаемости в день, максимальный размер выборки для группы B - {int(mean_traff * 0.2 * ab_test_duration)})', options=range(60, int(mean_traff * 0.2 * ab_test_duration) + 1))
st.write(f'Выбрано ~{int((ab_test_sample_size / ab_test_duration) / mean_traff * 100)}% от средней посещаемости в сутки.')
STEP_5 = True
if STEP_5:
with st.expander('Шаг 5. Проведите эксперимент', expanded=True):
st.write(
"""
Помните о важных шагах, которые необходимо выполнить:
1. Обсудите параметры эксперимента с исполнителями.
2. Выполните запрос на тестовой закрытой площадке, если она у вас есть. Это поможет проверить данные. Если ее нет, проверьте данные, полученные в первый день эксперимента.
3. В самом начале проведения тестирования проверьте, действительно ли оно работает.
4. И наконец, не смотрите на результаты!
Преждевременный просмотр результатов может испортить статистическую значимость.
"""
)
with st.form(key='start_ab'):
start_test = st.form_submit_button('Провести тест')
if start_test:
st.write("Посмотрим на проведенный тест")
df = tools.get_dataset(ab_test_sample_size, ab_test_duration)
visitors_a = df[df['group'] == 'old_version'].shape[0]
visitors_b = df[df['group'] == 'new_version'].shape[0]
conversions_a = df.groupby(['group', 'converted']).agg('count')['user_id'][3]
conversions_b = df.groupby(['group', 'converted']).agg('count')['user_id'][1]
st.write(df.sample(7))
st.plotly_chart(tools.get_plotly_converted_hist(df), use_container_width=True)
STEP_6 = True
if STEP_6:
with st.expander('Шаг 6. Проанализируйте результаты', expanded=True):
st.write(
"""
Вам нужно получить данные и рассчитать значения выбранной ранее метрики успеха для обеих версий
(A и B) и разницу между этими значениями.
Если не было никакой разницы в целом, вы также можете сегментировать выборку по платформам, типам источников,
географическим параметрам и т.п., если это применимо. Вы можете обнаружить,
что версия B работает лучше или хуже для определенных сегментов.
Проверьте статистическую значимость. Статистическая теория, лежащая в основе этого подхода, объясняется здесь,
но основная идея в том, чтобы выяснить, была ли разница в результатах между A и B связана с изменениями
или это результат случайности либо естественных изменений. Это определяется путем сравнения
тестовых статистических данных (и полученного p-значения) с вашим уровнем значимости.
Если p-значение меньше уровня значимости, то можно отвергнуть нулевую гипотезу, если имеются
доказательства для альтернативы.
Если p-значение больше или равно уровню значимости, мы не можем отвергнуть нулевую гипотезу о том,
что A и B не отличаются друг от друга.
"""
)
tools.calculate_significance(
conversions_a,
conversions_b,
visitors_a,
visitors_b
)
mcol1, mcol2 = st.columns(2)
with mcol1:
st.metric(
"Разница",
value=f"{(st.session_state.crb - st.session_state.cra):.3g}%",
delta=f"{(st.session_state.crb - st.session_state.cra):.3g}%",
)
with mcol2:
st.metric("Различие статзначимо?", value=st.session_state.significant)
results_df = pd.DataFrame(
{
"Group": ["A", "B"],
"Conversion": [st.session_state.cra, st.session_state.crb],
}
)
tools.plot_chart(results_df)
table = pd.DataFrame(
{
"Converted": [conversions_a, conversions_b],
"Total": [visitors_a, visitors_b],
"% Converted": [st.session_state.cra, st.session_state.crb],
},
index=pd.Index(["A", "B"]),
)
st.write(table.style.format(formatter={("% Converted"): "{:.3g}%"}))
metrics = pd.DataFrame(
{
"p-value": [st.session_state.p],
"z-score": [st.session_state.z],
"uplift": [st.session_state.uplift],
},
index=pd.Index(["Metrics"]),
)
st.write(
metrics.style.format(
formatter={("p-value", "z-score"): "{:.3g}", ("uplift"): "{:.3g}%"}
)
.applymap(tools.style_negative, props="color:red;")
.apply(tools.style_p_value, props="color:red;", axis=1, subset=["p-value"])
)
st.plotly_chart(tools.get_fig(df), use_container_width=True)