Last active
January 17, 2017 22:15
-
-
Save hildensia/0bc1442129bc85ff8ffc999b7c65f11c 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": 161, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "import scipy.stats\n", | |
| "import numpy as np" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Um das Problem mit AB Tests und mehreren Variablen zu zeigen, erzeugen wir erstmal Daten. Wir erzeugen `num_tests` Datensätze mit `num_params` Parametern, die wir speichern. Also z.b. Clickrate oder Donationvalue etc. Wir erzeugen diese Werte alle IID, also independently identical distributed. Die Werte sind also unabhängig voneinander und gleich verteilt. In diesem Beispiel ziehe ich alle Werte von einer Gauss Verteilung um 0 mit einer Standardabweichung von 1. Hinterher teile ich die Testdaten in Gruppe A und Gruppe B. Es sollte klar sein, dass Gruppe A/B keinerlei Auswirkung auf die Daten haben sollte, schließlich sind sie von der gleichen Verteilung gezogen. \n", | |
| "\n", | |
| "Als Testset erstellen wir 100.000 (!) Datensätze mit 25 Parametern" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 169, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def generate_ab_test_data(num_tests, num_params):\n", | |
| " tests = np.ndarray((num_tests, num_params))\n", | |
| " for i in range(num_params):\n", | |
| " tests[:, i] = scipy.stats.norm.rvs(size=(num_tests,))\n", | |
| " np.random.shuffle(tests)\n", | |
| " return tests[:int(num_tests/2), :], tests[int(num_tests/2):, :]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 184, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "num_params = 25\n", | |
| "num_tests = 100000\n", | |
| "a, b = generate_ab_test_data(num_tests, num_params)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Jetzt setzen wir unser Signifikanzlevel auf 5% und führen einen Welshen T-Test durch. Nacheinander für jeden Parameter. Das ist ein typischer Test um herauszufinden ob ein Wert in Gruppe A oder Gruppe B Signifikant höher ist. Signifikanzniveau 5% ist ziemlich üblich." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 185, | |
| "metadata": { | |
| "collapsed": false, | |
| "scrolled": true | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Ttest_indResult(statistic=array([ 0.18194954, -1.71092382, -0.56806697, -2.00933705, 0.61981658,\n", | |
| " 0.58308198, 0.1055243 , -1.4634974 , -0.23462331, -0.40745417,\n", | |
| " 0.13325866, -1.0125528 , 0.22912441, 0.66770396, 0.60381898,\n", | |
| " -1.82598919, -0.2229945 , -0.20177699, -1.05251348, 1.9603169 ,\n", | |
| " -0.01403652, -0.3990179 , 0.71166771, 0.05193864, 0.00280229]), pvalue=array([ 0.85562269, 0.08709828, 0.56999077, 0.04450408, 0.53537996,\n", | |
| " 0.55983943, 0.91596 , 0.14333447, 0.8145016 , 0.68367532,\n", | |
| " 0.89398909, 0.31127627, 0.81877269, 0.50432412, 0.54596537,\n", | |
| " 0.06785487, 0.82354024, 0.84009148, 0.29256657, 0.04996153,\n", | |
| " 0.98880088, 0.68988087, 0.47667223, 0.95857769, 0.9977641 ]))\n", | |
| "SIGNIFICANT!\n", | |
| "B is better\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "significance_level = 0.05 # p-value 5%, this is very common\n", | |
| "result = scipy.stats.ttest_ind(a, b, equal_var=False)\n", | |
| " \n", | |
| "print(result)\n", | |
| "if any(result.pvalue < significance_level):\n", | |
| " print(\"SIGNIFICANT!\")\n", | |
| " if result.statistic[np.where(result.pvalue < significance_level)][0] > 0:\n", | |
| " print(\"A is better\")\n", | |
| " else:\n", | |
| " print(\"B is better\")\n", | |
| " " | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Hm. Nun sagt unser Test, dass B signifikant besser ist als A. Woran liegt das? Signifikanz bedeutet, dass die Wahrscheinlichkeit ein gleiches oder extremeres Ergebnis zu erhalten, wenn die Nullhypothese stimmt 5% ist. Wir führen aber durch die unterschiedlichen Parameter 20 verschiedene Experimente durch, es ist also sehr wahrscheinlich, dass eines dieser Experimente zufällig signifikant ist. Das lässt sich auch nicht durch höheres `n` verhindern, es ist nämlich unabhängig von `n`. Es gibt Methoden das zu korrigieren, z.b. kann man das Signifikanzniveau auf `5%/25` setzen. Ein anderer Weg, der sehr sinnvoll ist und auch viele andere Vorteile hat ist Bayesian A/B Testing (siehe: http://engineering.richrelevance.com/bayesian-ab-testing-with-a-log-normal-model/ -- Achtung Mathe :) ) " | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [] | |
| } | |
| ], | |
| "metadata": { | |
| "anaconda-cloud": {}, | |
| "kernelspec": { | |
| "display_name": "Python [conda root]", | |
| "language": "python", | |
| "name": "conda-root-py" | |
| }, | |
| "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.5.2" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 1 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment