{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "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.7.6" }, "colab": { "name": "2021-06-28-step-by-step-ab-testing-guide.ipynb", "provenance": [], "toc_visible": true } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "-44lu3y8xbZz" }, "source": [ "# A/B testing step-by-step guide in Python\n", "> In this notebook we'll go over the process of analysing an A/B test, from formulating a hypothesis, testing it, and finally interpreting results.\n", "\n", "- toc: true\n", "- badges: true\n", "- comments: true\n", "- categories: [ABTest]\n", "- author: \"Renato Fillinich\"\n", "- image:" ] }, { "cell_type": "markdown", "metadata": { "id": "D3XBuKL-xbZ3" }, "source": [ "In this notebook we'll go over the process of analysing an A/B test, from formulating a hypothesis, testing it, and finally interpreting results. For our data, we'll use a dataset from Kaggle which contains the results of an A/B test on what seems to be 2 different designs of a website page (old_page vs. new_page). Here's what we'll do:\n", "\n", "1. Designing our experiment\n", "2. Collecting and preparing the data\n", "3. Visualising the results\n", "4. Testing the hypothesis\n", "5. Drawing conclusions\n", "\n", "To make it a bit more realistic, here's a potential **scenario** for our study:\n", "\n", "> Let's imagine you work on the product team at a medium-sized **online e-commerce business**. The UX designer worked really hard on a new version of the product page, with the hope that it will lead to a higher conversion rate. The product manager (PM) told you that the **current conversion rate** is about **13%** on average throughout the year, and that the team would be happy with an **increase of 2%**, meaning that the new design will be considered a success if it raises the conversion rate to 15%.\n", "\n", "Before rolling out the change, the team would be more comfortable testing it on a small number of users to see how it performs, so you suggest running an **A/B test** on a subset of your user base users." ] }, { "cell_type": "markdown", "metadata": { "id": "mSR2XKnOxbZ5" }, "source": [ "***\n", "## 1. Designing our experiment" ] }, { "cell_type": "markdown", "metadata": { "id": "UddxvZVjxbZ6" }, "source": [ "### Formulating a hypothesis\n", "\n", "First things first, we want to make sure we formulate a hypothesis at the start of our project. This will make sure our interpretation of the results is correct as well as rigorous.\n", "\n", "Given we don't know if the new design will perform better or worse (or the same?) as our current design, we'll choose a **two-tailed test**:\n", "\n", "$$H_0: p = p_0$$\n", "$$H_a: p \\ne p_0$$\n", "\n", "where $p$ and $p_0$ stand for the conversion rate of the new and old design, respectively. We'll also set a **confidence level of 95%**:\n", "\n", "$$\\alpha = 0.05$$\n", "\n", "The $\\alpha$ value is a threshold we set, by which we say \"if the probability of observing a result as extreme or more ($p$-value) is lower than $\\alpha$, then we reject the null hypothesis\". Since our $\\alpha=0.05$ (indicating 5% probability), our confidence (1 - $\\alpha$) is 95%.\n", "\n", "Don't worry if you are not familiar with the above, all this really means is that whatever conversion rate we observe for our new design in our test, we want to be 95% confident it is statistically different from the conversion rate of our old design, before we decide to reject the Null hypothesis $H_0$. " ] }, { "cell_type": "markdown", "metadata": { "id": "23b3NwD9xbZ7" }, "source": [ "### Choosing the variables\n", "\n", "For our test we'll need **two groups**:\n", "* A `control` group - They'll be shown the old design\n", "* A `treatment` (or experimental) group - They'll be shown the new design\n", "\n", "This will be our *Independent Variable*. The reason we have two groups even though we know the baseline conversion rate is that we want to control for other variables that could have an effect on our results, such as seasonality: by having a `control` group we can directly compare their results to the `treatment` group, because the only systematic difference between the groups is the design of the product page, and we can therefore attribute any differences in results to the designs.\n", "\n", "For our *Dependent Variable* (i.e. what we are trying to measure), we are interested in capturing the `conversion rate`. A way we can code this is by each user session with a binary variable:\n", "* `0` - The user did not buy the product during this user session\n", "* `1` - The user bought the product during this user session\n", "\n", "This way, we can easily calculate the mean for each group to get the conversion rate of each design." ] }, { "cell_type": "markdown", "metadata": { "id": "yKqt1-cOxbZ-" }, "source": [ "### Choosing a sample size\n", "\n", "It is important to note that since we won't test the whole user base (our population), the conversion rates that we'll get will inevitably be only *estimates* of the true rates.\n", "\n", "The number of people (or user sessions) we decide to capture in each group will have an effect on the precision of our estimated conversion rates: **the larger the sample size**, the more precise our estimates (i.e. the smaller our confidence intervals), **the higher the chance to detect a difference** in the two groups, if present.\n", "\n", "On the other hand, the larger our sample gets, the more expensive (and impractical) our study becomes.\n", "\n", "*So how many people should we have in each group?*\n", "\n", "The sample size we need is estimated through something called *Power analysis*, and it depends on a few factors:\n", "* **Power of the test** ($1 - \\beta$) - This represents the probability of finding a statistical difference between the groups in our test when a difference is actually present. This is usually set at 0.8 as a convention (here's more info on statistical power, if you are curious)\n", "* **Alpha value** ($\\alpha$) - The critical value we set earlier to 0.05\n", "* **Effect size** - How big of a difference we expect there to be between the conversion rates\n", "\n", "Since our team would be happy with a difference of 2%, we can use 13% and 15% to calculate the effect size we expect. \n", "\n", "Luckily, **Python takes care of all these calculations for us**:" ] }, { "cell_type": "code", "metadata": { "id": "J0Bq-G8DxbaA" }, "source": [ "# Packages imports\n", "import numpy as np\n", "import pandas as pd\n", "import scipy.stats as stats\n", "import statsmodels.stats.api as sms\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "from math import ceil\n", "\n", "%matplotlib inline\n", "\n", "import warnings\n", "warnings.filterwarnings(\"ignore\")\n", "\n", "# Some plot styling preferences\n", "plt.style.use('seaborn-whitegrid')\n", "font = {'family' : 'Helvetica',\n", " 'weight' : 'bold',\n", " 'size' : 14}\n", "\n", "mpl.rc('font', **font)" ], "execution_count": 5, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "eiaZhk-vxbaC", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "8c4fc745-a881-4d6d-f6f3-6e25308ff967" }, "source": [ "effect_size = sms.proportion_effectsize(0.13, 0.15) # Calculating effect size based on our expected rates\n", "\n", "required_n = sms.NormalIndPower().solve_power(\n", " effect_size, \n", " power=0.8, \n", " alpha=0.05, \n", " ratio=1\n", " ) # Calculating sample size needed\n", "\n", "required_n = ceil(required_n) # Rounding up to next whole number \n", "\n", "print(required_n)" ], "execution_count": 6, "outputs": [ { "output_type": "stream", "text": [ "4720\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "eZl2r4v7xbaF" }, "source": [ "We'd need **at least 4720 observations for each group**. \n", "\n", "Having set the `power` parameter to 0.8 in practice means that if there exists an actual difference in conversion rate between our designs, assuming the difference is the one we estimated (13% vs. 15%), we have about 80% chance to detect it as statistically significant in our test with the sample size we calculated." ] }, { "cell_type": "markdown", "metadata": { "id": "CURpYxEZxbaG" }, "source": [ "***\n", "## 2. Collecting and preparing the data" ] }, { "cell_type": "markdown", "metadata": { "id": "QnEwYEjhxbaH" }, "source": [ "Great stuff! So now that we have our required sample size, we need to collect the data. Usually at this point you would work with your team to set up the experiment, likely with the help of the Engineering team, and make sure that you collect enough data based on the sample size needed.\n", "\n", "However, since we'll use a dataset that we found online, in order to simulate this situation we'll:\n", "1. Download the dataset from Kaggle\n", "2. Read the data into a pandas DataFrame\n", "3. Check and clean the data as needed\n", "4. Randomly sample `n=4720` rows from the DataFrame for each group *****\n", "\n", "***Note**: Normally, we would not need to perform step 4, this is just for the sake of the exercise\n", "\n", "Since I already downloaded the dataset, I'll go straight to number 2." ] }, { "cell_type": "code", "metadata": { "id": "Z4s5ICJQxbaH", "colab": { "base_uri": "https://localhost:8080/", "height": 204 }, "outputId": "36350c16-000d-4124-f4be-38115ca5b396" }, "source": [ "df = pd.read_csv('https://github.com/sparsh-ai/reco-data/raw/master/ab-testing.zip')\n", "\n", "df.head()" ], "execution_count": 7, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
user_idtimestampgrouplanding_pageconverted
08511042017-01-21 22:11:48.556739controlold_page0
18042282017-01-12 08:01:45.159739controlold_page0
26615902017-01-11 16:55:06.154213treatmentnew_page0
38535412017-01-08 18:28:03.143765treatmentnew_page0
48649752017-01-21 01:52:26.210827controlold_page1
\n", "
" ], "text/plain": [ " user_id timestamp group landing_page converted\n", "0 851104 2017-01-21 22:11:48.556739 control old_page 0\n", "1 804228 2017-01-12 08:01:45.159739 control old_page 0\n", "2 661590 2017-01-11 16:55:06.154213 treatment new_page 0\n", "3 853541 2017-01-08 18:28:03.143765 treatment new_page 0\n", "4 864975 2017-01-21 01:52:26.210827 control old_page 1" ] }, "metadata": { "tags": [] }, "execution_count": 7 } ] }, { "cell_type": "code", "metadata": { "id": "Z3RiYHMBxbaI", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "d912411a-3e0c-4bbe-dba2-d67f074a1238" }, "source": [ "df.info()" ], "execution_count": 8, "outputs": [ { "output_type": "stream", "text": [ "\n", "RangeIndex: 294478 entries, 0 to 294477\n", "Data columns (total 5 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 user_id 294478 non-null int64 \n", " 1 timestamp 294478 non-null object\n", " 2 group 294478 non-null object\n", " 3 landing_page 294478 non-null object\n", " 4 converted 294478 non-null int64 \n", "dtypes: int64(2), object(3)\n", "memory usage: 11.2+ MB\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "id": "3jGYR9EJxbaI", "colab": { "base_uri": "https://localhost:8080/", "height": 142 }, "outputId": "dd8c7044-844f-4f04-a0bd-8a6b535e2117" }, "source": [ "# To make sure all the control group are seeing the old page and viceversa\n", "\n", "pd.crosstab(df['group'], df['landing_page'])" ], "execution_count": 9, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
landing_pagenew_pageold_page
group
control1928145274
treatment1453111965
\n", "
" ], "text/plain": [ "landing_page new_page old_page\n", "group \n", "control 1928 145274\n", "treatment 145311 1965" ] }, "metadata": { "tags": [] }, "execution_count": 9 } ] }, { "cell_type": "markdown", "metadata": { "id": "v4EyzPcPxbaJ" }, "source": [ "There are **294478 rows** in the DataFrame, each representing a user session, as well as **5 columns** :\n", "* `user_id` - The user ID of each session\n", "* `timestamp` - Timestamp for the session\n", "* `group` - Which group the user was assigned to for that session {`control`, `treatment`}\n", "* `landing_page` - Which design each user saw on that session {`old_page`, `new_page`}\n", "* `converted` - Whether the session ended in a conversion or not (binary, `0`=not converted, `1`=converted)\n", "\n", "We'll actually only use the `group` and `converted` columns for the analysis.\n", "\n", "Before we go ahead and sample the data to get our subset, let's make sure there are no users that have been sampled multiple times." ] }, { "cell_type": "code", "metadata": { "id": "32x1ywhYxbaJ", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "cb57b90a-00eb-4144-ec79-c88774bd58c8" }, "source": [ "session_counts = df['user_id'].value_counts(ascending=False)\n", "multi_users = session_counts[session_counts > 1].count()\n", "\n", "print(f'There are {multi_users} users that appear multiple times in the dataset')" ], "execution_count": 10, "outputs": [ { "output_type": "stream", "text": [ "There are 3894 users that appear multiple times in the dataset\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "yI1v4QCrxbaK" }, "source": [ "There are, in fact, users that appear more than once. Since the number is pretty low, we'll go ahead and remove them from the DataFrame to avoid sampling the same users twice." ] }, { "cell_type": "code", "metadata": { "id": "1VTCNR5SxbaK", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "298a791b-4df8-4ea2-d2f1-293cae82b43b" }, "source": [ "users_to_drop = session_counts[session_counts > 1].index\n", "\n", "df = df[~df['user_id'].isin(users_to_drop)]\n", "print(f'The updated dataset now has {df.shape[0]} entries')" ], "execution_count": 11, "outputs": [ { "output_type": "stream", "text": [ "The updated dataset now has 286690 entries\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "VYalyUeJxbaK" }, "source": [ "### Sampling\n", "\n", "Now that our DataFrame is nice and clean, we can proceed and sample `n=4720` entries for each of the groups. We can use pandas' `DataFrame.sample()` method to do this, which will perform Simple Random Sampling for us. \n", "\n", "**Note**: I've set `random_state=22` so that the results are reproducible if you feel like following on your own Notebook: just use `random_state=22` in your function and you should get the same sample as I did." ] }, { "cell_type": "code", "metadata": { "id": "6EXbtFaexbaL" }, "source": [ "control_sample = df[df['group'] == 'control'].sample(n=required_n, random_state=22)\n", "treatment_sample = df[df['group'] == 'treatment'].sample(n=required_n, random_state=22)\n", "\n", "ab_test = pd.concat([control_sample, treatment_sample], axis=0)\n", "ab_test.reset_index(drop=True, inplace=True)" ], "execution_count": 12, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "ORf0Hlv2xbaL", "colab": { "base_uri": "https://localhost:8080/", "height": 419 }, "outputId": "0edd346b-7612-4ef1-bb54-7578893a3326" }, "source": [ "ab_test" ], "execution_count": 13, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
user_idtimestampgrouplanding_pageconverted
07638542017-01-21 03:43:17.188315controlold_page0
16905552017-01-18 06:38:13.079449controlold_page0
28615202017-01-06 21:13:40.044766controlold_page0
36307782017-01-05 16:42:36.995204controlold_page0
46566342017-01-04 15:31:21.676130controlold_page0
..................
94359085122017-01-14 22:02:29.922674treatmentnew_page0
94368732112017-01-05 00:57:16.167151treatmentnew_page0
94376312762017-01-20 18:56:58.167809treatmentnew_page0
94386623012017-01-03 08:10:57.768806treatmentnew_page0
94399446232017-01-19 10:56:01.648653treatmentnew_page1
\n", "

9440 rows × 5 columns

\n", "
" ], "text/plain": [ " user_id timestamp group landing_page converted\n", "0 763854 2017-01-21 03:43:17.188315 control old_page 0\n", "1 690555 2017-01-18 06:38:13.079449 control old_page 0\n", "2 861520 2017-01-06 21:13:40.044766 control old_page 0\n", "3 630778 2017-01-05 16:42:36.995204 control old_page 0\n", "4 656634 2017-01-04 15:31:21.676130 control old_page 0\n", "... ... ... ... ... ...\n", "9435 908512 2017-01-14 22:02:29.922674 treatment new_page 0\n", "9436 873211 2017-01-05 00:57:16.167151 treatment new_page 0\n", "9437 631276 2017-01-20 18:56:58.167809 treatment new_page 0\n", "9438 662301 2017-01-03 08:10:57.768806 treatment new_page 0\n", "9439 944623 2017-01-19 10:56:01.648653 treatment new_page 1\n", "\n", "[9440 rows x 5 columns]" ] }, "metadata": { "tags": [] }, "execution_count": 13 } ] }, { "cell_type": "code", "metadata": { "id": "fIFKXUmBxbaN", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "cdecfb42-bbf6-4e21-ab13-778bb1b5af33" }, "source": [ "ab_test.info()" ], "execution_count": 14, "outputs": [ { "output_type": "stream", "text": [ "\n", "RangeIndex: 9440 entries, 0 to 9439\n", "Data columns (total 5 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 user_id 9440 non-null int64 \n", " 1 timestamp 9440 non-null object\n", " 2 group 9440 non-null object\n", " 3 landing_page 9440 non-null object\n", " 4 converted 9440 non-null int64 \n", "dtypes: int64(2), object(3)\n", "memory usage: 368.9+ KB\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "id": "5mGQRVnSxbaO", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "9c440cdc-37ac-4233-eb7f-b824768b18b1" }, "source": [ "ab_test['group'].value_counts()" ], "execution_count": 15, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "control 4720\n", "treatment 4720\n", "Name: group, dtype: int64" ] }, "metadata": { "tags": [] }, "execution_count": 15 } ] }, { "cell_type": "markdown", "metadata": { "id": "HPUc1bznxbaO" }, "source": [ "Great, looks like everything went as planned, and we are now ready to analyse our results." ] }, { "cell_type": "markdown", "metadata": { "id": "g1PoRb9KxbaP" }, "source": [ "***\n", "## 3. Visualising the results" ] }, { "cell_type": "markdown", "metadata": { "id": "fmwnE9rXxbaP" }, "source": [ "The first thing we can do is to calculate some **basic statistics** to get an idea of what our samples look like." ] }, { "cell_type": "code", "metadata": { "id": "OrfD9yUyxbaQ", "colab": { "base_uri": "https://localhost:8080/", "height": 103 }, "outputId": "2e3fc958-aaf7-49c1-aed5-a8bd53ac2b68" }, "source": [ "conversion_rates = ab_test.groupby('group')['converted']\n", "\n", "std_p = lambda x: np.std(x, ddof=0) # Std. deviation of the proportion\n", "se_p = lambda x: stats.sem(x, ddof=0) # Std. error of the proportion (std / sqrt(n))\n", "\n", "conversion_rates = conversion_rates.agg([np.mean, std_p, se_p])\n", "conversion_rates.columns = ['conversion_rate', 'std_deviation', 'std_error']\n", "\n", "\n", "conversion_rates.style.format('{:.3f}')" ], "execution_count": 16, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
conversion_rate std_deviation std_error
group
control0.1230.3290.005
treatment0.1260.3310.005
" ], "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 16 } ] }, { "cell_type": "markdown", "metadata": { "id": "MPADWAVexbaR" }, "source": [ "Judging by the stats above, it does look like **our two designs performed very similarly**, with our new design performing slightly better, approx. **12.3% vs. 12.6% conversion rate**.\n", "\n", "Plotting the data will make these results easier to grasp:" ] }, { "cell_type": "code", "metadata": { "id": "gcasaVmXxbaR", "colab": { "base_uri": "https://localhost:8080/", "height": 489 }, "outputId": "11dcf7af-5468-43f0-959e-a7e4577fb1bd" }, "source": [ "plt.figure(figsize=(8,6))\n", "\n", "sns.barplot(x=ab_test['group'], y=ab_test['converted'], ci=False)\n", "\n", "plt.ylim(0, 0.17)\n", "plt.title('Conversion rate by group', pad=20)\n", "plt.xlabel('Group', labelpad=15)\n", "plt.ylabel('Converted (proportion)', labelpad=15);" ], "execution_count": 17, "outputs": [ { "output_type": "stream", "text": [ "findfont: Font family ['Helvetica'] not found. Falling back to DejaVu Sans.\n", "findfont: Font family ['Helvetica'] not found. Falling back to DejaVu Sans.\n", "findfont: Font family ['Helvetica'] not found. Falling back to DejaVu Sans.\n" ], "name": "stderr" }, { "output_type": "display_data", "data": { "image/png": "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jz32mM3GJSIi8rBzMFhrFmIRFxsbi7+/f2GXISIi8kAU9HvPLi9biIiIiP1SeBARERGLKDyIiIiIRRQeRERExCIKDyIiImIRhQcRERGxiMKDiIiIWEThQURERCyi8CAiIiIWUXgQERERiyg8iIiIiEUUHkRERMQiCg8iIiJiEYUHERERsYjCg4iIiFhE4UFEREQsovAgIiIiFlF4EBEREYsoPIiIiIhFFB5ERETEIgoPIiIiYhGFBxEREbGIwoOIiIhYROFBRERELKLwICIiIhZReBARERGLKDyIiIiIRew6PGzatImePXvSsGFDmjdvzpgxYzh79myBbaKjoxk8eDDNmzfHx8cHHx8f1qxZk+/+ycnJJvt+99131h6GiIhIkWK34WHVqlWEhoZy/PhxKlasSFZWFlFRUfTr149Lly7l2y4mJoaYmBjc3NzueYzs7Gxee+01rl27Zs3SRUREijS7DA8ZGRnMnDkTgM6dO7N9+3Y2b96Mq6srqampLFiwIN+2w4cP59ChQ8ydO/eex1m0aBE//vgjXbp0sVrtIiIiRZ1dhoejR49y5coVAIKCggDw8PCgcePGAOzevTvfthUqVMDR0fGexzh27Bhz5syhffv29OvXzwpVi4iIPBrsMjwkJycbl93d3XMtJyUl3Vf/t27dIiwsjHLlyjF16tT76ktERORRU6KwC7CEwWCwSj8zZ87kzJkzfPbZZ5QvX97sdidOnLDK8UVERB5mdhkeKleubFxOTU01LqelpQHg6el5X/2fOnUKgFGjRgGQlZVl3DZ27Fg6derErFmzcrWrX7/+fR1XRETkYREbG5vvNru8bOHn50e5cuWAu7deAqSkpHD48GEAAgICAAgODiY4OJhly5ZZfAyDwcDNmze5efMmd+7cMa6/c+cOt2/fvt8hiIiIFFl2eebB0dGR0NBQwsPDiYqKomPHjly9epX09HTc3NwYNmwYAPHx8QDGyZUAS5YsYenSpWRmZhrXzZgxg48//pigoCDGjx/P0qVLTY73448/8sILLwDwySef0L59e1sPUURE5KFll+EBoE+fPri4uBAREUFcXBxOTk4EBgYSFhaGh4dHvu2uXbtGQkKCybrU1FTjHxEREbk/DgZrzUIs4mJjY/H39y/sMkRERB6Ign7v2eWcBxEREbFfCg8iIiJiEYUHERERsYjCg4iIiFhE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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] } } ] }, { "cell_type": "markdown", "metadata": { "id": "9s8WH7MoxbaS" }, "source": [ "The conversion rates for our groups are indeed very close. Also note that the conversion rate of the `control` group is lower than what we would have expected given what we knew about our avg. conversion rate (12.3% vs. 13%). This goes to show that there is some variation in results when sampling from a population.\n", "\n", "So... the `treatment` group's value is higher. **Is this difference *statistically significant***?" ] }, { "cell_type": "markdown", "metadata": { "id": "Cz0hF-gXxbaS" }, "source": [ "***\n", "## 4. Testing the hypothesis" ] }, { "cell_type": "markdown", "metadata": { "id": "np2wDTn4xbaS" }, "source": [ "The last step of our analysis is testing our hypothesis. Since we have a very large sample, we can use the normal approximation for calculating our $p$-value (i.e. z-test). \n", "\n", "Again, Python makes all the calculations very easy. We can use the `statsmodels.stats.proportion` module to get the $p$-value and confidence intervals:" ] }, { "cell_type": "code", "metadata": { "id": "WRFRW8NpxbaT" }, "source": [ "from statsmodels.stats.proportion import proportions_ztest, proportion_confint" ], "execution_count": 18, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "dbjM4L56xbaU" }, "source": [ "control_results = ab_test[ab_test['group'] == 'control']['converted']\n", "treatment_results = ab_test[ab_test['group'] == 'treatment']['converted']" ], "execution_count": 19, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "HUm9Alv-xbaV", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "bd092939-cc8c-4401-97f0-73e4ac35d3c4" }, "source": [ "n_con = control_results.count()\n", "n_treat = treatment_results.count()\n", "successes = [control_results.sum(), treatment_results.sum()]\n", "nobs = [n_con, n_treat]\n", "\n", "z_stat, pval = proportions_ztest(successes, nobs=nobs)\n", "(lower_con, lower_treat), (upper_con, upper_treat) = proportion_confint(successes, nobs=nobs, alpha=0.05)\n", "\n", "print(f'z statistic: {z_stat:.2f}')\n", "print(f'p-value: {pval:.3f}')\n", "print(f'ci 95% for control group: [{lower_con:.3f}, {upper_con:.3f}]')\n", "print(f'ci 95% for treatment group: [{lower_treat:.3f}, {upper_treat:.3f}]')" ], "execution_count": 20, "outputs": [ { "output_type": "stream", "text": [ "z statistic: -0.34\n", "p-value: 0.732\n", "ci 95% for control group: [0.114, 0.133]\n", "ci 95% for treatment group: [0.116, 0.135]\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "wY-zEPrLxbaV" }, "source": [ "***\n", "## 5. Drawing conclusions" ] }, { "cell_type": "markdown", "metadata": { "id": "AqesRmhrxbaW" }, "source": [ "Since our $p$-value=0.732 is way above our $\\alpha$=0.05, we cannot reject the null hypothesis $H_0$, which means that our new design did not perform significantly different (let alone better) than our old one :(\n", "\n", "Additionally, if we look at the confidence interval for the `treatment` group ([0.116, 0.135], i.e. 11.6-13.5%) we notice that:\n", "1. It includes our baseline value of 13% conversion rate\n", "2. It does not include our target value of 15% (the 2% uplift we were aiming for)\n", "\n", "What this means is that it is more likely that the true conversion rate of the new design is similar to our baseline, rather than the 15% target we had hoped for. This is further proof that our new design is not likely to be an improvement on our old design, and that unfortunately we are back to the drawing board! " ] } ] }