import numpy as np
import pandas as pd
from itertools import product
from scipy.stats import norm, binom, mannwhitneyu
# Сгенерированные данные. В вашем случае здесь нужно загрузить данные за продолжительный
# исторический период. Это необходимо, чтобы учесть поведение клиента и распределение
# оригинальной метрики
np.random.seed(1)
mu = 42.
sd = 10.
data = norm.rvs(loc=mu, scale=sd, size=10000)
alpha = 0.05 # 1 - уровень значимости
simulations = 1000 # количество симуляций
lifts = np.arange(1, 1.1, 0.01) # последовательность шагов по эффекту
# Последовательность шагов по увеличению выборки. Я бы не рекомендовал использовать в живом проекте такой метод,
# т.к. не наследуется информация об окне метрики. Чтобы его учесть, лучше брать даты в качестве шага
sizes = np.arange(1000, 10001, 1000)
sim_res = pd.DataFrame() # сюда кладем результат расчетов
for lift, n in product(lifts, sizes):
control = data[0:n]
# В этом примере равномерно распределяем эффект по всему распределению
test = control * lift
for _ in range(0, simulations):
# Рандомное присвоение групп A/B
is_control = binom.rvs(1, 0.5, size=n)
# Считаем p-value
_, p = mannwhitneyu(control[is_control == True], test[is_control == False])
# Кладем результат
sim_res = sim_res.append({"lift": lift, "n": n, "pvalue": p}, ignore_index=True)
res = sim_res.groupby(["lift", "n"]).apply(lambda x: sum(x['pvalue'] < 0.05) / simulations)
import numpy as np
import pandas as pd
from scipy.stats import binom, ttest_ind
# Сгенерированные данные. Представим, что моделируем экономику A/B в подписочном SaaS
pricing = np.array([0.99, 4.99, 9.99]) # цены в подписочной модели продукта
# Доля покупок по каждой цене. Представим гипотезу, в которой мы хотим увеличить долю
# платящих клиентов по самой дорогой цене, немного снизив долю по низкой
proportions_control = np.array([0.5, 0.4, 0.1])
proportions_test = np.array([0.49, 0.4, 0.11])
N = 10000
sizes = np.arange(1000, 10001, 1000)
simulations = 1000
sim_res = pd.DataFrame()
np.random.seed(1)
control_pop = np.random.choice(pricing, N, p=proportions_control)
test_pop = np.random.choice(pricing, N, p=proportions_test)
for n in sizes:
control = control_pop[0:n]
test = test_pop[0:n]
for _ in range(0, simulations):
# Рандомное присвоение групп A/B
is_control = binom.rvs(1, 0.5, size=n)
# Считаем p-value
_, p = ttest_ind(control[is_control == True], test[is_control == False])
# Кладем результат
sim_res = sim_res.append({"n": n, "pvalue": p}, ignore_index=True)
res = sim_res.groupby(["n"]).apply(lambda x: sum(x['pvalue'] < 0.05) / simulations)