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

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

В рамках этого проекта — программы Udacity Data Analyst для наностепеней — вы будете работать над тем, чтобы понять результаты A/B-тестирования, проведенного веб-сайтом электронной коммерции. Компания разработала новую веб-страницу, чтобы попытаться увеличить количество пользователей, которые «конвертируют», то есть количество пользователей, которые решили заплатить за продукт компании. Цель состоит в том, чтобы помочь компании понять, должны ли они внедрить эту новую страницу, оставить старую страницу или, возможно, провести эксперимент дольше, чтобы принять решение.

Оглавление:

  • Часть I. Вероятность
  • Часть II — A/B-тестирование
  • Часть III. Регрессионный подход
  • Часть IV. Заключение

Данные: ab_data.csv

#импортировать библиотеки

import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt
%matplotlib inline
random.seed(42)

Часть I. Вероятность

#Прочитайте данные `ab_data.csv`. Сохраните его в `df`.

df = pd.read_csv('ab_data.csv')
df.head()

#Количество строк в наборе данных.

df.shape

#Количество уникальных пользователей в наборе данных.

df.nunique()

#Доля конверсионных пользователей.

df.converted.mean()

#количество раз, когда new_page и обработка не совпадают.

first_df = len(df.query(‘group == “treatment” and landing_page == “old_page”’))
second_df = len(df.query(‘group == “control” and landing_page == “new_page”’))
e = first_df + second_df
print(e)

#проверка отсутствия значения.

missing = df.isnull().sum()

Для строк, в которых обработка не соответствует new_page или control не соответствует old_page, мы нельзя быть уверенным, действительно ли эта строка получила новую или старую страницу.

#создайте новый набор данных, соответствующий спецификациям, и сохраните новый фрейм данных в df2.

df2 = df.drop(df.query(‘group == “treatment” and landing_page != “new_page” | group == “control” and landing_page != “old_page”’).index)

#тест

df2[((df2[‘group’] == ‘treatment’) == (df2[‘landing_page’] == ‘new_page’)) == False].shape[0]

#Уникальные user_id в df2.

df2.user_id.nunique()

#повторяется user_id в df2 иинформации о строке

duplicated_user = df2[df2['user_id'].duplicated()]
df2[df2['user_id'].duplicated(keep=False)]

#удалить одну строку с повторяющимся user_id.

df2 = df2.drop([2893])

#тест

df2[df2['user_id'].duplicated()].any()

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

df2.converted.mean()

#вероятность того, что человек совершит конверсию так, как в контрольной группе.

p_control = df2.query('group == "control"').converted.mean()
p_control

#вероятность того, что индивидуум обратится в другую группу.

p_treatment = df2.query('group == "treatment"').converted.mean()
p_treatment

#вероятность того, что пользователь получил новую страницу.

p_newpage = df2.query('landing_page == "new_page"').shape[0]/df2.shape[0]
p_newpage

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

obs_diff = p_treatment - p_control
print('Observed difference is: {}'.format(obs_diff))

Пока нельзя сказать, что новая страница лечения приводит к большему количеству конверсий, у меня нет достаточных доказательств в поддержку этого утверждения. Данные показывают, что p_control = 12% и p_treatment = 11%. Другими словами, коэффициенты конверсии экспериментальных и контрольных групп слишком близки друг к другу, чтобы иметь четкое представление. Разница между ними составляет всего 0,001, как видно из obs_diff выше. Вероятность получения новой страницы или старой страницы выходит 50–50%. Чтобы принять решение об обработке страницы, мне нужно проверить нулевую гипотезу и попытаться получить больше доказательств.

Часть II — A/B-тестирование

#если вы хотите предположить, что старая страница лучше, если новая страница не окажется определенно лучше при частоте ошибок типа I, равной 5%, ваши нулевая и альтернативная гипотезы должны быть

Односторонний Т-тест:

H_0: 𝑝𝑜𝑙𝑑 ≥ 𝑝𝑛𝑒𝑤

H_1: 𝑝𝑜𝑙𝑑 < 𝑝𝑛𝑒𝑤

(𝑝𝑜𝑙𝑑 и 𝑝𝑛𝑒𝑤 — коэффициенты конверсии для старых и новых страниц)

#предположим, что согласно нулевой гипотезе, 𝑝𝑛𝑒𝑤 и 𝑝𝑜𝑙𝑑 имеют «истинные» показатели успешности, равные конверсиям, независимо от страницы

(Предположим, что pnew = pold и размер выборки = размер выборки ab_data)

#коэффициент конвертации для 𝑝𝑛𝑒𝑤 и 𝑝𝑜𝑙𝑑 при нулевом значении

p_new = df2.converted.mean()
p_old = df2.converted.mean()
print('p_new:' , p_new , 'p_old:', p_old)

#nnewи nold

n_new = len(df2.query('landing_page == "new_page"'))
n_old = len(df2.query('landing_page == "old_page"'))
print('n_new:' , n_new , 'n_old:', n_old)

#Смоделируйте nnew транзакции с коэффициентом конверсии 𝑝𝑛𝑒𝑤 при нулевом значении. Сохраните эти nnew 1 и 0 в new_page_converted.

new_page_converted = np.random.binomial(1, p_new, n_new)
new_page_converted.mean()

#Смоделируйте nold транзакции с коэффициентом конверсии 𝑝𝑜𝑙𝑑 при нулевом значении. Сохраните эти nold 1 и 0 в old_page_converted.

old_page_converted = np.random.binomial(1, p_old, n_old)
old_page_converted.mean()

#𝑝𝑛𝑒𝑤-𝑝старый

difference = new_page_converted.mean() — old_page_converted.mean()
print(‘Simulated difference is: {}’.format(difference))

#p_diffs

p_diffs = []
new_converted_simulation = np.random.binomial(n_new, p_new, 10000)/n_new
old_converted_simulation = np.random.binomial(n_old, p_old, 10000)/n_old
p_diffs = new_converted_simulation — old_converted_simulation
p_diffs = np.array(p_diffs)

#построить гистограмму

plt.hist(p_diffs)
plt.title('New-old probability diffs simulation')
plt.xlabel('p_diffs');
null_vals = np.random.normal(0, p_diffs.std(), p_diffs.size)
plt.hist(null_vals)
plt.axvline(x=obs_diff, c=’red’);

Создаваемый график должен иметь нормальное распределение. Также статистика, которую я вычислил выше, называется в научных исследованиях «p-value». Это произошло из нулевого распределения, p-значение = 0,91 > 0,05. Так что не отвергайте нулевую гипотезу 𝑝𝑛𝑒𝑤 = 𝑝𝑜𝑙𝑑.

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

import statsmodels.api as sm
convert_old = df2.query("landing_page == 'old_page' and converted == 1").shape[0]
convert_new = df2.query("landing_page == 'new_page' and converted == 1").shape[0]
n_old = df2.query("landing_page == 'old_page'").shape[0]
n_new = df2.query("landing_page == 'new_page'").shape[0]
z_score, p_value = sm.stats.proportions_ztest([convert_old, convert_new], [n_old, n_new], alternative = “smaller”)
z_score, p_value
print(‘Z-score: {}’.format(z_score))
print(‘P-value of Z-Test: {}’.format(p_value))

Вычисления z-показателя и p-значения из тестовой статистики показывают, что результаты не опровергают нулевую гипотезу. Кроме того, оба p-значения равны 0,189 при расчете без вычисления «альтернативы». Они согласны с выводами, сделанными в предыдущей части.

Часть III. Регрессионный подход

Я хочу предсказывать категоричные ответы, поэтому буду использовать логистическую регрессию.

df2[[‘a_page’, ‘ab_page’]] = pd.get_dummies(df2[‘group’]) #create dummies
#logistic regression model
df2['intercept']= 1
logit_mod =sm.Logit(df2['converted'],df2[['intercept', 'a_page']])
#fit the model
results = logit_mod.fit()

p-значение ~ 0,19 ›› ошибка типа 1 (Статистически отклонение несущественно.)

np.exp(0.015) #to exponentiate

Люди в 1,015 раза чаще конвертируют страницу, если все остальное остается неизменным.

Я предположил, что 𝑝𝑛𝑒𝑤 = 𝑝𝑜𝑙𝑑 в этом двустороннем Т-тесте в части III.

Предыдущий анализ представлял собой односторонний Т-тест и предполагал 𝑝𝑛𝑒𝑤 › 𝑝𝑜𝑙𝑑 в части II.

Нулевая и альтернативная гипотезы для двустороннего Т-теста:

H_0: 𝑝𝑛𝑒𝑤 — 𝑝𝑜𝑙𝑑 = 0

H_1: 𝑝𝑛𝑒𝑤 — 𝑝𝑜𝑙𝑑 != 0

Нулевая и альтернативная гипотезы для одностороннего Т-теста:

H_0: 𝑝𝑛𝑒𝑤 — 𝑝𝑜𝑙𝑑 ≤ 0

H_1: 𝑝𝑛𝑒𝑤 — 𝑝𝑜𝑙𝑑 > 0

P-значение для обоих из них составляет 0,1899 при двустороннем тестировании без указания альтернативы.

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

#индивидуальные факторы страны и страницы при переходе

#merging datasets
countries_df = pd.read_csv(‘./countries.csv’)
df_new=countries_df.set_index(‘user_id’).join(df2.set_index(‘user_id’), how=’inner’)#creating dummy variables
df_new[[‘UK’, ‘US’, ‘CA’]] = pd.get_dummies(df_new.country)

#логит-модель

model = sm.Logit(df_new.converted, df_new[[‘intercept’, ‘ab_page’, ‘UK’, ‘CA’]]) #logit model
results_new = model.fit() #fitting the model

Значения p указывают на отсутствие влияния страны на конверсию.

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

df_new[‘ab_UK’] = df_new[‘ab_page’] * df_new[‘UK’]
df_new[‘ab_US’] = df_new[‘ab_page’] * df_new[‘US’]
df_new[‘ab_CA’] = df_new[‘ab_page’] * df_new[‘CA’]

#Логит-модель

lmodel = sm.Logit(df_new[“converted”], df_new[[“intercept”, “ab_page”, “UK”, “CA”, “ab_UK”, “ab_CA”]])

#Подбор логит-модели

results_factor = lmodel.fit()
1/np.exp(results_factor.params) #to exponentiate

Согласно статистическим результатам, p-значения ab_UK и ab_CA превышают ошибку типа I (0,05). Если считать все остальные постоянными, вероятность конверсии у пользователя из Великобритании в 1,08 раза выше, а у пользователя из ЦА вероятность конверсии выше в 1,03 раза. Их влияние действительно мало. Более того, результаты недостаточно надежны, чтобы говорить о значительном влиянии на конверсию отдельных факторов страны и страницы.

Заключение

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

Подробности и полная версия:



Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.