Created
September 24, 2018 07:03
-
-
Save dmitriyshashkin/a91d61d4e11ed4d99c6a44b87e8aad59 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import numpy as np\n", | |
"from statsmodels.stats.proportion import proportions_ztest\n", | |
"import statsmodels.stats.power as smp\n", | |
"from statsmodels.stats.proportion import proportion_effectsize" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Параметры симуляции\n", | |
"\n", | |
"Базовая конверси 0.10%, конверсия в улучшенной версии 0.11%. Число пользователей подобрал так, чтобы получить стат мощность 80% (при заданном учлушении до 0.11% обычный z-test покажет стат значимый результат в 80% случае). N рассчитал калькулятором от evan miller" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"conversion_a = 0.0010\n", | |
"conversion_b = 0.0011\n", | |
"max_vistors = 1300000\n", | |
"N = 2922 # https://www.evanmiller.org/ab-testing/sequential.html" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"0.8010077236088784" | |
] | |
}, | |
"execution_count": 10, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Проверяю стат мощность при таких параметрах\n", | |
"smp.NormalIndPower().solve_power(proportion_effectsize(conversion_b, conversion_a), nobs1=max_vistors, alpha=0.05, alternative='larger')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# эмуляция одного эксперимента\n", | |
"\n", | |
"# генерирую результаты для максимально возможного числа пользователей. затем прохожусь по результатам каждого пользователя (как будто он только поучаствовал в эксперименте).\n", | |
"# после каждого нового пользователя проверяю выполняется ли одно из условий остановки по правилам evan miller'а. \n", | |
"# в зависимости от того какое из условий выполне засчитываю или нет победу вариации B \n", | |
"\n", | |
"def run_seq(hyp): \n", | |
" group_a = np.random.binomial(1, conversion_a, max_vistors*2)\n", | |
" \n", | |
" # для нулевой гипотез конверсия такая как в группе A, для альтернативной - 0.11%\n", | |
" group_b = np.random.binomial(1, conversion_b if hyp == 'alternative' else conversion_a, max_vistors*2)\n", | |
"\n", | |
" C = T = 0\n", | |
" t_wins = None\n", | |
"\n", | |
" for i in range(len(group_a)):\n", | |
" C = C + group_a[i]\n", | |
" T = T + group_b[i]\n", | |
"\n", | |
" # условие остановки для победы вариации B\n", | |
" if T - C >= 2 * np.math.sqrt(N):\n", | |
" t_wins = True\n", | |
" break\n", | |
"\n", | |
" # условие остановки для не-победы вариации B\n", | |
" if T + C >= N:\n", | |
" t_wins = False\n", | |
" break\n", | |
"\n", | |
" return [t_wins, i]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 17, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"0.76800000000000002" | |
] | |
}, | |
"execution_count": 17, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# как часто я присуждаю победу вариации B если она действительно лучше?\n", | |
"# в идеале должно быть 80%, тк именно это значение необходимой стат мощности я внёс в калькулятор от evan miller\n", | |
"rA = [run_seq('alternative') for i in range(1000)]\n", | |
"np.mean([ri[0] for ri in rA])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 52, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"1004162.7439999999" | |
] | |
}, | |
"execution_count": 52, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Как средняя продолжительность эксперимента?\n", | |
"np.mean([ri[1] for ri in rA])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 53, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"0.77243287999999999" | |
] | |
}, | |
"execution_count": 53, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# На сколько меньше пользователей мне нужно при таком подходе?\n", | |
"np.mean([ri[1] for ri in rA]) / max_vistors" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Type 1 error (null is true)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 54, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# как часто я присуждаю победу вариации B если она ничем не отличается от вариации A?\n", | |
"# в идеале должно быть 5%, тк именно это значение alpha я внёс в калькулятор от evan miller\n", | |
"r0 = [run_seq('null') for i in range(1000)]\n", | |
"np.mean([ri[0] for ri in r0])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 56, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"1446983.767" | |
] | |
}, | |
"execution_count": 56, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Как средняя продолжительность эксперимента?\n", | |
"np.mean([ri[1] for ri in r0])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 57, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"1.1130644361538462" | |
] | |
}, | |
"execution_count": 57, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# На сколько больше пользователей мне нужно при таком подходе?\n", | |
"np.mean([ri[1] for ri in r0]) / max_vistors" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Вывод\n", | |
"В варианте evan miller нужно меньше пользователей если альтернативная гипотеза верна и больше пользователей если нулевая гипотеза верна. Точность примерно соответствует заданным показателям" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 2", | |
"language": "python", | |
"name": "python2" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.5" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment