Якщо зростає сумарне значення \(\rightarrow\) зростає середнє значення. Тому це завдання можна переформулювати як зростання середнього чека, або ARPU.
Збільшення числа покупок.
Зменшення відтоку користувачів.
Далі, для виведення всіх критеріїв нам потрібен нормальний розподіл. Потому що саме цьому розподілу підпорядковується середнє вибірок.
Нормальний розподіл
Нормальний розподіл\(\xi \sim \mathcal{N}(\mu, \sigma^2)\) — неперервний розподіл, у якому щільність спадає зі збільшенням відстані від \(\mu\) за експонентою квадрата.
Випадкові величини можуть бути слабко залежні одна від одної і злегка по-різному розподілені. Центральна гранична теорема все ще буде правильною.
Візуалізація ЦГТ
Генеруємо \(N\) вибірок по \(M\) елементів у кожній:
По кожній вибірці треба порахувати нормовану середню за \(M\) елементами.
У підсумку ми отримаємо вибірку з N елементів.
Вона і має бути з нормального розподілу.
Код
def visualize_CLT(sample_generator, expected_value, variance):""" Функція-візуалізатор ЦГТ. Будує: - гісторгаму статистики ЦГТ, - емпіричну щільність, - щільність нормального розподілу. Параметри: - sample_generator: функція-генератор вибірки. Не приймає нічого на вхід - expected_value: мат. сподівання вибірки - variance: дисперсія вибірки """ numpy.random.seed(2024) N =5000 clt_sample = []for _ inrange(N):# генеруємо N разів вибірку sample = sample_generator()# рахуємо статистику з ЦГТ sample_size =len(sample) statistic = numpy.sqrt(sample_size) * (numpy.mean(sample) - expected_value) / numpy.sqrt(variance)#зберігаємо clt_sample.append(statistic) x = numpy.linspace(-4, 4, 1000) pyplot.figure(figsize=(10, 5)) pyplot.title('Розподіл середніх значень', fontsize=12) seaborn.distplot(clt_sample, label='Емпіричний розподіл') pyplot.plot(x, norm().pdf(x), label='$\mathcal{N}(0, 1)$') pyplot.legend(fontsize=12) pyplot.xlabel('X', fontsize=12) pyplot.ylabel('Щільність', fontsize=12) pyplot.show()
Візуалізація ЦГТ: біноміальний розподіл
def binom_generator(p, n, size):return binom(p=p, n=n).rvs(size)p =0.01n =20size =5000# ми обертаємо binom_generator в lambda, щоб функція не приймала параметри на вхідvisualize_CLT(lambda: binom_generator(p, n, size), expected_value = p * n, variance = n * p * (1- p) )
😊
def binom_generator(p, n, size):return binom(p=p, n=n).rvs(size)p =0.01n =20size =40# ми обертаємо binom_generator в lambda, щоб функція не приймала параметри на вхідvisualize_CLT(lambda: binom_generator(p, n, size), expected_value = p * n, variance = n * p * (1- p) )
Або p-value = 1 - norm(loc=sum_mu, scale=sum_std).cdf(t).
При цьому цього разу ми дивимося статистику не в точці t-1, як робили раніше, а в точці t. Так як у нас неперервний розподіл, то нам не потрібно віднімати 1:
у разі нормального розподілу: \(P(T(X^n) \geq t) = P(T(X^n) > t) = 1 - P(T(X^n) \leq t)\);
у разі біноміального розподілу: \(P(T(X^n) \geq t) = 1 - P(T(X^n) \leq t - 1)\).
а також p-value(t) = 1 - binom.cdf(t-a, n, mu0) = p-value(t-a), де 0 < a < 1. Наприклад, p-value(19) = p-value(18.9).
Тобто, щоб зобразити біноміальне p-value, треба зобразити функцію p-value = 1 - binom.cdf(t, n, mu0). Значення p-value у цілих точках лежатимуть на правих кінцях вертикальних відрізків.
Поправка на неперервність
Код
def cmp_pvalue_binom_and_norm(n, mu0, t, add_to_x=0):""" Функція для порівняння функцій pvalue у біноміального критерію і нормальної апроксимації. Будує графіки: - binom pvalue - norm pvalue - великими крапками позначено pvalue у точці t, отримані 2 способами. Параметри: - n: кількість значень у вибірці - mu0: конверсія за умови коректності нульової гіпотези - t: кількість доставок з оплатою - add_to_x: параметр для додавання до T(X) у нормальному розподілі. """ x_axis = numpy.linspace(0, n, 1000) dots_to_show = numpy.arange(0, n +1, 1)# параметри нормального розподілу sum_mu = n * mu0 sum_variance = n * mu0 * (1- mu0) sum_std = numpy.sqrt(sum_variance)# самі розподіли binom_dist = binom(n=n, p=mu0) norm_dist = norm(loc=sum_mu, scale=sum_std) pyplot.figure(figsize=(20, 7)) pyplot.title('Порівняння p-value: біноміального і нормального', fontsize=12)# будуємо красивий дискретний бернулліївський розподіл# спочатку горизонтальні лінії pyplot.hlines(1- binom_dist.cdf(x_axis[:-1]), x_axis[:-1], x_axis[1:], color=slate, linestyle='-')# вертикальні лінії, яких насправді немає pyplot.vlines(x_axis[:-1], 1- binom_dist.cdf(x_axis[:-1]), 1- binom_dist.cdf(x_axis[1:]), color=slate, linestyle=':')# дискретні точки в розподілі pyplot.scatter(dots_to_show, 1- binom_dist.cdf(dots_to_show-1), color=slate, alpha=1, linewidths=0.5, label=f'Binom pvalue = 1-binom.cdf(x-1)')# pvalue через біноміальний розподіл pyplot.scatter(t, 1- binom_dist.cdf(t -1), color=slate, marker='o', alpha=1, s=100, label=f'binom p-value({t})')# нормальний розподіл, що апроксимує, нормальний розподіл add_str =""if add_to_x ==0elsef"{add_to_x}" pyplot.plot(x_axis, 1- norm_dist.cdf(x_axis + add_to_x), color=red_pink, alpha=0.5, label=f'Normal pvalue = 1-norm.cdf(x{add_str})')# pvalue через нормальний розподіл pyplot.scatter(t, 1- norm_dist.cdf(t + add_to_x), color=red_pink, alpha=1, marker='o', s=100, label=f'norm p-value({t})') pyplot.legend(fontsize=12) pyplot.xlabel('t', fontsize=12) pyplot.ylabel('p-value', fontsize=12) pyplot.show()cmp_pvalue_binom_and_norm(30, 0.5, 15)
\(p_{\text{binom}} > p_{\text{norm}}\)
При збільшенні розміру вибірки ці значення збігаються.
Поправка на неперервність
n =20t =10old_p_value = get_pvalue_by_old_logic(n, mu0, t)print(f"p-value, отримане за старою, коректною формулою: {old_p_value}")normal_dist_p_value = get_pvalue_by_normal_approx(n, mu0, t)print(f"p-value, отримане з наближення нормальним розподілом: {normal_dist_p_value}")print(f"Різниця: {round(abs(old_p_value - normal_dist_p_value), 3)}")
p-value, отримане за старою, коректною формулою: 0.5880985260009766
p-value, отримане з наближення нормальним розподілом: 0.5
Різниця: 0.088
# При зростанні tn =20t =14old_p_value = get_pvalue_by_old_logic(n, mu0, t)print(f"p-value, отримане за старою, коректною формулою: {old_p_value}")normal_dist_p_value = get_pvalue_by_normal_approx(n, mu0, t)print(f"p-value, отримане з наближення нормальним розподілом: {normal_dist_p_value}")print(f"Різниця: {round(abs(old_p_value - normal_dist_p_value), 3)}")
p-value, отримане за старою, коректною формулою: 0.057659149169921875
p-value, отримане з наближення нормальним розподілом: 0.03681913506015133
Різниця: 0.021
# При зростанні nn =200t =100old_p_value = get_pvalue_by_old_logic(n, mu0, t)print(f"p-value, отримане за старою, коректною формулою: {old_p_value}")normal_dist_p_value = get_pvalue_by_normal_approx(n, mu0, t)print(f"p-value, отримане з наближення нормальним розподілом: {normal_dist_p_value}")print(f"Різниця: {round(abs(old_p_value - normal_dist_p_value), 3)}")
p-value, отримане за старою, коректною формулою: 0.5281742395046283
p-value, отримане з наближення нормальним розподілом: 0.5
Різниця: 0.028