722 lines
73 KiB
Text
722 lines
73 KiB
Text
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# A/B test monitoring\n",
|
|
"\n",
|
|
"## Initial setup\n",
|
|
"This first section just ensures that the connection to DWH works correctly."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 37,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import pathlib\n",
|
|
"import yaml\n",
|
|
"import pandas as pd\n",
|
|
"import numpy as np\n",
|
|
"from sqlalchemy import create_engine\n",
|
|
"import seaborn as sns\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"from statsmodels.stats.proportion import proportions_ztest\n",
|
|
"from scipy import stats\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 38,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"/home/joaquin/.superhog-dwh/credentials.yml\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"CREDS_FILEPATH = pathlib.Path.home() / \".superhog-dwh\" / \"credentials.yml\"\n",
|
|
"print(CREDS_FILEPATH)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 39,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Prepare connection to DWH\n",
|
|
"# Function to read credentials from the YAML file\n",
|
|
"def read_credentials(yaml_path: str, env: str = \"prd\"):\n",
|
|
" with open(yaml_path, \"r\") as file:\n",
|
|
" credentials = yaml.safe_load(file)\n",
|
|
" return credentials[\"envs\"][env]\n",
|
|
"# Function to create a PostgreSQL connection string\n",
|
|
"def create_postgres_engine(creds: dict):\n",
|
|
" user = creds[\"user\"]\n",
|
|
" password = creds[\"password\"]\n",
|
|
" host = creds[\"host\"]\n",
|
|
" port = creds[\"port\"]\n",
|
|
" database = creds[\"database\"]\n",
|
|
" # Create the connection string for SQLAlchemy\n",
|
|
" connection_string = f\"postgresql://{user}:{password}@{host}:{port}/{database}\"\n",
|
|
" engine = create_engine(connection_string)\n",
|
|
" return engine\n",
|
|
"# Function to execute a query and return the result as a pandas DataFrame\n",
|
|
"def query_to_dataframe(engine, query: str):\n",
|
|
" with engine.connect() as connection:\n",
|
|
" df = pd.read_sql(query, connection)\n",
|
|
" return df\n",
|
|
"dwh_creds = read_credentials(yaml_path=CREDS_FILEPATH, env=\"prd\")\n",
|
|
"dwh_pg_engine = create_postgres_engine(creds=dwh_creds)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 40,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" ?column?\n",
|
|
"0 1\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Silly query to test things out\n",
|
|
"test_df = query_to_dataframe(engine=dwh_pg_engine, query=\"SELECT 1;\")\n",
|
|
"print(test_df.head())"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## A/B test configuration\n",
|
|
"In this section we configure the parameters for the A/B test. Likely you do NOT need to change anything else than this, unless of course you want to create new metrics and so on.\n",
|
|
"\n",
|
|
"The parameters to be specified are:\n",
|
|
"* **ab_test_name**: this should be the name of the feature flag corresponding to the A/B test. If you don't know the name, ask Guest Squad\n",
|
|
"* **var_A** and **var_B**: these correspond to the name of the variants. At this moment, we can only handle univariant testing (though updating the code to include multivariant testing should not be extremely difficult). In general, choose var_A to be the Control group."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# A/B test name to measure\n",
|
|
"#ab_test_name = \"AAVariantTest\"\n",
|
|
"ab_test_name = \"WelcomePageDestinationContext\"\n",
|
|
"\n",
|
|
"# Define the variations in which we want to run the tests\n",
|
|
"var_A = 'GenericImageAndCopy' # Ideally, this should be the control group\n",
|
|
"var_B = 'ContextSpecificImageAndCopy' # Ideally, this should be the study group\n",
|
|
"\n",
|
|
"variations = [var_A, var_B]\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Data Extraction\n",
|
|
"In this section we extract the data from the Guest Journey monitoring within DWH by configuring which A/B test we want to measure. Here we already handle the basic aggregations that will be needed in the future, directly in SQL."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" ab_test_name variation last_update guest_journeys_count \\\n",
|
|
"0 ShowNewIllustrations NewIllustrations 2025-04-16 20916 \n",
|
|
"1 ShowNewIllustrations OldIllustrations 2025-04-16 21018 \n",
|
|
"\n",
|
|
" guest_journey_started_count guest_journey_completed_count \\\n",
|
|
"0 20916 16155 \n",
|
|
"1 21018 16187 \n",
|
|
"\n",
|
|
" guest_journey_with_responses_count guest_journey_with_payment_count \\\n",
|
|
"0 5820 8677 \n",
|
|
"1 6084 8580 \n",
|
|
"\n",
|
|
" guest_revenue_count deposit_count ... \\\n",
|
|
"0 8677 1439 ... \n",
|
|
"1 8580 1393 ... \n",
|
|
"\n",
|
|
" guest_revenue_avg_per_guest_journey guest_revenue_sdv_per_guest_journey \\\n",
|
|
"0 10.703579 16.931697 \n",
|
|
"1 10.488324 16.978108 \n",
|
|
"\n",
|
|
" deposit_avg_per_guest_journey deposit_sdv_per_guest_journey \\\n",
|
|
"0 0.488697 2.369654 \n",
|
|
"1 0.462561 1.947974 \n",
|
|
"\n",
|
|
" waiver_avg_per_guest_journey waiver_sdv_per_guest_journey \\\n",
|
|
"0 10.042173 16.872259 \n",
|
|
"1 9.869315 16.968309 \n",
|
|
"\n",
|
|
" check_in_cover_avg_per_guest_journey check_in_cover_sdv_per_guest_journey \\\n",
|
|
"0 0.172710 1.222131 \n",
|
|
"1 0.156449 1.162772 \n",
|
|
"\n",
|
|
" csat_avg_per_guest_journey_with_response \\\n",
|
|
"0 3.778179 \n",
|
|
"1 3.797337 \n",
|
|
"\n",
|
|
" csat_sdv_per_guest_journey_with_response \n",
|
|
"0 1.018528 \n",
|
|
"1 1.025494 \n",
|
|
"\n",
|
|
"[2 rows x 26 columns]\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Query to extract data\n",
|
|
"data_extraction_query = \"\"\"\n",
|
|
"select \n",
|
|
"\tab_test_name,\n",
|
|
"\tvariation,\n",
|
|
"\tmax(first_appearance_date_utc) as last_update,\n",
|
|
" \n",
|
|
" -- SIMPLE COUNTS --\n",
|
|
"\tcount(id_verification_request) as guest_journeys_count,\n",
|
|
"\tcount(verification_started_date_utc) as guest_journey_started_count,\n",
|
|
"\tcount(verification_completed_date_utc) as guest_journey_completed_count,\n",
|
|
"\tcount(experience_rating) as guest_journey_with_responses_count,\n",
|
|
"\tcount(last_payment_paid_date_utc) as guest_journey_with_payment_count,\n",
|
|
"\tcount(guest_revenue_without_taxes_in_gbp) as guest_revenue_count,\n",
|
|
"\tcount(deposit_fees_without_taxes_in_gbp) as deposit_count,\n",
|
|
"\tcount(waiver_fees_without_taxes_in_gbp) as waiver_count,\n",
|
|
"\tcount(check_in_cover_fees_without_taxes_in_gbp) as check_in_cover_count,\n",
|
|
" \n",
|
|
" -- SIMPLE SUMS --\n",
|
|
"\tsum(guest_revenue_without_taxes_in_gbp) as guest_revenue_sum,\n",
|
|
"\tsum(deposit_fees_without_taxes_in_gbp) as deposit_sum,\n",
|
|
"\tsum(waiver_fees_without_taxes_in_gbp) as waiver_sum,\n",
|
|
"\tsum(check_in_cover_fees_without_taxes_in_gbp) as check_in_cover_sum,\n",
|
|
" \n",
|
|
" -- AVGs/SDVs PER GUEST JOURNEY (ANY GJ APPEARING IN THE A/B TEST) --\n",
|
|
" -- NOTE THE COALESCE HERE. THIS IS IMPORTANT FOR THE T-TEST COMPUTATION --\n",
|
|
" avg(coalesce(guest_revenue_without_taxes_in_gbp,0)) as guest_revenue_avg_per_guest_journey,\n",
|
|
" stddev(coalesce(guest_revenue_without_taxes_in_gbp,0)) as guest_revenue_sdv_per_guest_journey,\n",
|
|
" avg(coalesce(deposit_fees_without_taxes_in_gbp,0)) as deposit_avg_per_guest_journey,\n",
|
|
" stddev(coalesce(deposit_fees_without_taxes_in_gbp,0)) as deposit_sdv_per_guest_journey,\n",
|
|
" avg(coalesce(waiver_fees_without_taxes_in_gbp,0)) as waiver_avg_per_guest_journey,\n",
|
|
" stddev(coalesce(waiver_fees_without_taxes_in_gbp,0)) as waiver_sdv_per_guest_journey,\n",
|
|
" avg(coalesce(check_in_cover_fees_without_taxes_in_gbp,0)) as check_in_cover_avg_per_guest_journey,\n",
|
|
" stddev(coalesce(check_in_cover_fees_without_taxes_in_gbp,0)) as check_in_cover_sdv_per_guest_journey,\n",
|
|
" \n",
|
|
" -- AVGs/SDVs PER GUEST JOURNEY WITH CSAT RESPONSE --\n",
|
|
" -- NOTE THAT THERE'S NO COALESCE HERE. THIS IS IMPORTANT FOR THE T-TEST COMPUTATION --\n",
|
|
" avg(experience_rating) as csat_avg_per_guest_journey_with_response,\n",
|
|
" stddev(experience_rating) as csat_sdv_per_guest_journey_with_response\n",
|
|
" \n",
|
|
"from\n",
|
|
"\tintermediate.int_core__ab_test_monitoring_guest_journey\n",
|
|
"where\n",
|
|
"\tab_test_name = '{}'\n",
|
|
" and first_appearance_at_utc >= '2025-04-23 12:50:00'\n",
|
|
"group by\n",
|
|
"\t1,2\n",
|
|
"\"\"\".format(ab_test_name)\n",
|
|
"\n",
|
|
"# Retrieve Data from Query\n",
|
|
"df = query_to_dataframe(engine=dwh_pg_engine, query=data_extraction_query)\n",
|
|
"print(df.head())"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Check A/B test Allocation to Variation"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 43,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAf0AAAIYCAYAAABnrTUkAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAgbtJREFUeJzt3Xd4FNX+BvB3W3rvvZIEEgi9d6SJSLFiAbxguwrq1ftTvHqx12tHESuKCCJdEZDeayAQShJKKumF9GTr/P5YsrCkkECS2fJ+nicPZHZ29zu7m33nnDlzRiIIggAiIiKyeFKxCyAiIqKOwdAnIiKyEgx9IiIiK8HQJyIishIMfSIiIivB0CciIrISDH0iIiIrwdAnIiKyEgx9IiIiK8HQb6XDhw/j1VdfxYQJE9C3b1/ExcWhf//+uOeee/Dmm2/iwIEDsLZJDtesWYOYmBiMGjVK7FKoCQ899BBiYmLw0UcftWj9t99+GzExMXjsscfata558+YhJiYGa9asadfnuVZMTAxiYmI67PnawqRJkxATE4OuXbvi8uXLza7b1Gta/3c6b9689ixVFKNGjUJMTAwuXbokdikmj6HfQqWlpZg9ezZmzJiBlStXoqqqCr169cL48ePRo0cPlJaW4tdff8U//vEP3HXXXWKX2yqHDx9GTEwMpk+fLnYp1E7uueceAMC6deug1WqbXVelUuHPP/80up+5mD59OmJiYnD48GGxS2kzSUlJSE1NBQCo1Wr88ccfIlfUscTYMbRkcrELMAcVFRV48MEHkZ6ejoiICLz22msYMGBAg/XOnTuHn376CRs3bhShSqKmjR8/Hm+//TaKioqwZ88ejBw5ssl1t2/fjrKyMnh4eLR7783zzz+Pxx57DD4+Pu36PNcyt7/PVatWAQB8fX1RUFCAVatWYebMmSJXZVp++uknqNVq+Pr6il2KyWNLvwXeeustpKenIzg4GL/99lujgQ8A0dHRePfdd7FkyZIOrpCoefb29rjjjjsA4IYtpvrbJ02aBIVC0a51+fj4IDIyEs7Ozu36PNeKjIxEZGRkhz3fraitrcVff/0FAPjwww/h4OCAc+fOISkpSeTKTEtISAgiIyPb/fNqCRj6N5CVlYUNGzYAAF5++WW4urre8D7x8fENlt3omNONurAOHjyIOXPmYMiQIejatSsGDhyIp59+GomJiY2un5GRgZdffhmjRo1C165d0bNnT4wcORKPP/44Vq9ebVhv+vTpmDFjBgDgyJEjhuOdbXmMPj8/H2+99RbGjh2Lbt26oXfv3pg2bRp+++23RruaFyxYgJiYGCxYsKDRx2vqcMS1y2tra/H555/j9ttvR/fu3Q3bcu1xzZqaGnz88ccYM2YMunbtisGDB+Oll15CQUFBk9tSUFCA9957z/C4PXv2xN13342lS5dCo9EYrfvwww8jJibG8PlpzHfffYeYmBg8++yzTa7TVuq76nfu3InS0tJG1ykoKMD+/fuN1i8tLcWSJUvw2GOPYdSoUYiPj0evXr1w11134dtvv4VSqWz0sa49dr569Wrcf//96N27t9HfQVOf+6qqKvz++++YM2cOxo4dix49eqBHjx6488478emnn6KiosJo/fr3/siRIwCAGTNmGH2Wr3385o7pl5WV4ZNPPsEdd9xheH/vuusufPfdd6irq2uw/rWfObVajW+//RZ33HEH4uPj0b9/f8yZMwcXL15s9LlaYvPmzaiqqkJ0dDQGDBiACRMmALja+m9LSUlJePbZZ42+Y5588knD56EpBw8exDPPPINhw4aha9euGDBgAO6++2588cUXRuMP1Go11q9fjxdeeAHjx49Hr169EB8fj3HjxuHtt99u8Hd36dIlxMTEYO3atQD037/XvqfXfj809/1aW1uLb7/9FlOnTkXPnj3RvXt33HHHHfj0009RXl7eYP365x01ahQEQcCKFStw1113oUePHujduzdmzZrV5PeuOWD3/g3s3LkTOp0Orq6uzXaJtqcPPvgAP/74I6RSKbp27YrevXsjLy8P27dvx86dO/HWW2/h7rvvNqx/7tw5PPDAA6iqqkJ4eDhGjhwJqVSKgoICHD16FAUFBYb1hw4dChsbG+zbtw9eXl4YOnSo4XHc3d1vufakpCQ89thjKCsrQ0BAAEaPHo3KykocOXIEiYmJ2Lp1K77++mvY2Njc8nPVUyqVmD59Oi5evIg+ffqgc+fOKCsrM1qnsrIS06ZNQ15eHnr37o2oqCicOHEC69atw9GjR7F+/foGrc+jR4/i6aefRnl5OQIDAzFo0CCoVCqcOnUKb731Fnbu3IlFixYZWhszZszA0aNHsXTpUkycOLFBnTqdDsuXLweg30Fob/Hx8YiOjsa5c+fwxx9/4JFHHmmw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p59+wo8//tjoMdrs7GysX7++0fsPHz4coaGh2Lt3L1JSUhASEoJhw4a1d9lNCggIwKBBg6DT6fDvf/8bADBs2LAGgxvDwsIgk8lw7ty5BvPZ79ixAz/99FOb1+bl5QVXV1dUVFQYBrPVO3HiBD755JMm71u/w9janeQ+ffqge/fuqKurw/z581FbW2u4rbS0FPPnzwcATJgwocnj922lvtfl+h2w69XfvmvXribHHLXEjBkzIJfLsW3btgaf33379hnODJo1a5ZhuVwux5NPPgkA+O9//4ujR482eNykpCSj78P6QbL15/XXS0tLa/Zw482+pwEBARg/fjwEQcD8+fONdipqamowf/58KJVK9OzZs8EgPkvG7v0WcHV1xfLly/HCCy/g4MGDmD59Ovz8/NClSxc4OztDqVQiIyMD586dgyAIiI6ObjDa/qGHHsLKlStx5swZw0V6amtrcerUKfj7+2P06NGN7jE//PDDyM3NxQ8//ICHHnoIUVFRCAkJgZ2dHYqKipCSkoKKigq8/vrrhtOifv/9d7z55psICgpCVFQUnJyccPnyZSQkJKCurg4DBgwwmm0vICAAXbt2xenTp3HnnXeia9eusLW1hbu7uyEQWuL6Lj4bGxt8/vnneOyxx7B8+XLs2bMH3bt3R3V1NQ4dOgSlUokhQ4bg6aefNrqfv78/Hn74YSxZsgSPPPIIevfuDTc3N6SkpCAvLw+PP/44vv322xbX1Rb8/PywcOFCzJ07Fx988AG+//57REVFwdvbG1VVVbh48SKysrLQvXt3TJ48ucH9pVIpHnroIbz77rsAgAcffLDFA6Dayz333IN9+/YZAqOxi+t4eHjgoYceMrwXffr0gY+PD9LT03HmzBn885//xNdff92mdclkMjz11FN47733DIejgoODkZubi8TEREyaNAkJCQmGwWTXGjduHNasWYP//e9/OHjwIDw8PCCRSHD33Xff8Iv9448/xsyZM7F9+3bcdttt6NOnDzQaDQ4fPoyqqirExcUZwr+9HDlyBJmZmbCxsTFMm9yUqKgoxMXF4cyZM1i3bp1RKLdGTEwM5s+fj9dffx0vvvgifv75Z4SHhxteb0EQMHfu3AbjimbOnIn09HT89ttvePjhhxEbG4vw8HBUVVUhLS0N2dnZWLJkiaFRMWfOHDzzzDP4/PPPsWnTJkRFRaGk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",
|
|
"text/plain": [
|
|
"<Figure size 800x600 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"# Ensure Seaborn styling\n",
|
|
"sns.set_theme(style=\"whitegrid\")\n",
|
|
"\n",
|
|
"# Calculate the total guest_journeys_count per variation\n",
|
|
"grouped_data = df.groupby('variation')['guest_journeys_count'].sum()\n",
|
|
"\n",
|
|
"# Find the total count and other metadata\n",
|
|
"total_count = grouped_data.sum()\n",
|
|
"ab_test_name = df['ab_test_name'].iloc[0] # Assuming all rows are for the same A/B test\n",
|
|
"last_update = df['last_update'].max()\n",
|
|
"\n",
|
|
"# Create a pie chart using Seaborn styling\n",
|
|
"plt.figure(figsize=(8, 6))\n",
|
|
"colors = sns.color_palette(\"pastel\") # Seaborn pastel colors\n",
|
|
"\n",
|
|
"# Pie chart with labels inside each sector\n",
|
|
"plt.pie(\n",
|
|
" grouped_data, \n",
|
|
" labels=[f\"{var}\\n{count} ({count/total_count:.1%})\" for var, count in grouped_data.items()],\n",
|
|
" autopct=None, \n",
|
|
" colors=colors, \n",
|
|
" startangle=90,\n",
|
|
" wedgeprops={'edgecolor': 'none'}, # Remove edges around sectors\n",
|
|
" pctdistance=0.70, # Places the labels closer to the center (inside)\n",
|
|
" labeldistance=0.2 # Ensure labels are positioned inside the sectors\n",
|
|
")\n",
|
|
"\n",
|
|
"# Add title\n",
|
|
"plt.title(\"Guest Journey - Variation Allocation\", fontsize=16)\n",
|
|
"\n",
|
|
"# Add total count to the bottom-left\n",
|
|
"plt.text(-1.4, -1.3, f\"Total Count: {total_count}\", fontsize=10, ha='left', color='black')\n",
|
|
"\n",
|
|
"# Add A/B test name and last update to the bottom-right\n",
|
|
"plt.text(1.2, -1.3, f\"A/B Test: {ab_test_name}\", fontsize=8, ha='right', color='gray')\n",
|
|
"plt.text(1.2, -1.4, f\"Last Update: {last_update}\", fontsize=8, ha='right', color='gray')\n",
|
|
"\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Statistical Analysis\n",
|
|
"In this section we compute the metrics needed for monitoring as well as check if there's any statistical difference between the different variations."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Z-test for Proportion Metrics (Rates)\n",
|
|
"This section defines the functions used to compute Z-test Proportion analysis"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 44,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Generalized function to calculate Z-test for any metric\n",
|
|
"def calculate_z_test(df, metric_name, variation_a, variation_b, success_counts, total_counts):\n",
|
|
"\n",
|
|
" # Aggregate the success counts (numerator) and total counts (denominator) for each variation\n",
|
|
" success_a = df[df['variation'] == variation_a][success_counts].sum()\n",
|
|
" success_b = df[df['variation'] == variation_b][success_counts].sum()\n",
|
|
"\n",
|
|
" total_a = df[df['variation'] == variation_a][total_counts].sum()\n",
|
|
" total_b = df[df['variation'] == variation_b][total_counts].sum()\n",
|
|
"\n",
|
|
" # Calculate conversion rates for each variation\n",
|
|
" value_A = success_a / total_a if total_a != 0 else 0\n",
|
|
" value_B = success_b / total_b if total_b != 0 else 0\n",
|
|
"\n",
|
|
" # Absolute difference (B - A)\n",
|
|
" abs_diff = value_B - value_A\n",
|
|
"\n",
|
|
" # Relative difference (B - A) / A\n",
|
|
" rel_diff = (value_B - value_A) / value_A if value_A != 0 else 0\n",
|
|
"\n",
|
|
" # Perform the z-test for proportions\n",
|
|
" count = [success_a, success_b] # Success counts for A and B\n",
|
|
" nobs = [total_a, total_b] # Total counts for A and B\n",
|
|
" \n",
|
|
" # Calculate z-stat and p-value\n",
|
|
" z_stat, p_value = proportions_ztest(count, nobs)\n",
|
|
" \n",
|
|
" # Flag for significance at 95% level (p-value < 0.05)\n",
|
|
" is_significant = p_value < 0.05\n",
|
|
"\n",
|
|
" # Return the result as a dictionary\n",
|
|
" return {\n",
|
|
" 'metric': metric_name,\n",
|
|
" 'variation_A_name': variation_a,\n",
|
|
" 'variation_B_name': variation_b,\n",
|
|
" 'variation_A_value': value_A,\n",
|
|
" 'variation_B_value': value_B,\n",
|
|
" 'absolute_difference': abs_diff,\n",
|
|
" 'relative_difference': rel_diff,\n",
|
|
" 'statistic': z_stat,\n",
|
|
" 'p_value': p_value,\n",
|
|
" 'is_significant_95': is_significant\n",
|
|
" }\n",
|
|
"\n",
|
|
"# Function to run Z-tests for multiple metrics and aggregate results into a DataFrame\n",
|
|
"def run_z_tests(df, z_stat_metric_definition, variations):\n",
|
|
" results = []\n",
|
|
" \n",
|
|
" # Loop over all metrics in z_stat_metric_definition\n",
|
|
" for metric_name, metric_definition in z_stat_metric_definition.items():\n",
|
|
" success_counts = metric_definition['success_counts']\n",
|
|
" total_counts = metric_definition['total_counts']\n",
|
|
" \n",
|
|
" # Run the Z-test for each metric\n",
|
|
" result = calculate_z_test(df, metric_name, variation_a=variations[0], variation_b=variations[1], \n",
|
|
" success_counts=success_counts, total_counts=total_counts)\n",
|
|
" results.append(result)\n",
|
|
" \n",
|
|
" # Create a DataFrame from the results\n",
|
|
" results_df = pd.DataFrame(results)\n",
|
|
" \n",
|
|
" return results_df"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### T-test for non-proportion metrics\n",
|
|
"This section defines the functions used to compute T-tests for metrics outside of the proportion scope, mostly Revenue-related metrics."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 45,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"\n",
|
|
"# Generalized function to calculate T-test for revenue-related metrics\n",
|
|
"def calculate_t_test(df, metric_name, variation_a, variation_b, metric_avg_column, metric_sdv_column, total_counts):\n",
|
|
" # Aggregate the avgs and standard deviations for each variation\n",
|
|
" mean_a = df[df['variation'] == variation_a][metric_avg_column].mean() # Assuming the avg is calculated for each group\n",
|
|
" mean_b = df[df['variation'] == variation_b][metric_avg_column].mean() # Assuming the avg is calculated for each group\n",
|
|
" \n",
|
|
" sdv_a = df[df['variation'] == variation_a][metric_sdv_column].mean() # Assuming the stddev is calculated for each group\n",
|
|
" sdv_b = df[df['variation'] == variation_b][metric_sdv_column].mean() # Assuming the stddev is calculated for each group\n",
|
|
" \n",
|
|
" total_a = df[df['variation'] == variation_a][total_counts].sum()\n",
|
|
" total_b = df[df['variation'] == variation_b][total_counts].sum()\n",
|
|
"\n",
|
|
" # Absolute difference (B - A)\n",
|
|
" abs_diff = mean_b - mean_a\n",
|
|
"\n",
|
|
" # Relative difference (B - A) / A\n",
|
|
" rel_diff = (mean_b - mean_a) / mean_a if mean_a != 0 else 0\n",
|
|
"\n",
|
|
" # Calculate the T-statistic and p-value using the formula for two-sample T-test\n",
|
|
" se_a = sdv_a / (total_a ** 0.5) if total_a != 0 else 0\n",
|
|
" se_b = sdv_b / (total_b ** 0.5) if total_b != 0 else 0\n",
|
|
"\n",
|
|
" # Standard error of the difference between the means\n",
|
|
" se_diff = (se_a ** 2 + se_b ** 2) ** 0.5\n",
|
|
" \n",
|
|
" # T-statistic formula\n",
|
|
" if se_diff != 0:\n",
|
|
" t_stat = (mean_a - mean_b) / se_diff\n",
|
|
" else:\n",
|
|
" t_stat = 0\n",
|
|
" \n",
|
|
" # Degrees of freedom (for independent samples)\n",
|
|
" df_degrees = min(total_a - 1, total_b - 1) # Using the smaller of the two sample sizes minus 1\n",
|
|
" \n",
|
|
" # P-value from the T-distribution\n",
|
|
" p_value = stats.t.sf(abs(t_stat), df_degrees) * 2 # Two-tailed test\n",
|
|
" \n",
|
|
" # Flag for significance at 95% level (p-value < 0.05)\n",
|
|
" is_significant = p_value < 0.05\n",
|
|
"\n",
|
|
" # Return the result as a dictionary\n",
|
|
" return {\n",
|
|
" 'metric': metric_name,\n",
|
|
" 'variation_A_name': variation_a,\n",
|
|
" 'variation_B_name': variation_b,\n",
|
|
" 'variation_A_value': mean_a,\n",
|
|
" 'variation_B_value': mean_b,\n",
|
|
" 'absolute_difference': abs_diff,\n",
|
|
" 'relative_difference': rel_diff,\n",
|
|
" 'statistic': t_stat,\n",
|
|
" 'p_value': p_value,\n",
|
|
" 'is_significant_95': is_significant\n",
|
|
" }\n",
|
|
"\n",
|
|
"# Function to run T-tests for multiple revenue metrics and aggregate results into a DataFrame\n",
|
|
"def run_t_tests(df, t_stat_metric_definition, variations):\n",
|
|
" results = []\n",
|
|
" \n",
|
|
" # Loop over all metrics in t_stat_metric_definition\n",
|
|
" for metric_name, metric_definition in t_stat_metric_definition.items():\n",
|
|
" metric_avg_column = metric_definition['metric_avg_column']\n",
|
|
" metric_sdv_column = metric_definition['metric_sdv_column']\n",
|
|
" total_counts = metric_definition['total_counts']\n",
|
|
" \n",
|
|
" # Run the T-test for each metric\n",
|
|
" result = calculate_t_test(df, metric_name, variation_a=variations[0], variation_b=variations[1], \n",
|
|
" metric_avg_column=metric_avg_column, metric_sdv_column=metric_sdv_column, \n",
|
|
" total_counts=total_counts)\n",
|
|
" results.append(result)\n",
|
|
" \n",
|
|
" # Create a DataFrame from the results\n",
|
|
" results_df = pd.DataFrame(results)\n",
|
|
" \n",
|
|
" return results_df"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Specify the metric definition for Z-stat and T-stat tests"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 46,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Define the Z-test metric definitions (with both success_counts and total_counts)\n",
|
|
"z_stat_metric_definition = {\n",
|
|
" 'conversion_rate': {\n",
|
|
" 'success_counts': 'guest_journey_completed_count',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" },\n",
|
|
" 'payment_rate': {\n",
|
|
" 'success_counts': 'guest_journey_with_payment_count',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" },\n",
|
|
" 'waiver_payment_rate': {\n",
|
|
" 'success_counts': 'waiver_count',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" },\n",
|
|
" 'deposit_payment_rate': {\n",
|
|
" 'success_counts': 'deposit_count',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" },\n",
|
|
" 'CIH_payment_rate': {\n",
|
|
" 'success_counts': 'check_in_cover_count',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"# Define the T-test metric definitions (with both metric_avg_column and metric_sdv_column)\n",
|
|
"t_stat_metric_definition = {\n",
|
|
" 'avg_guest_revenue_per_gj': {\n",
|
|
" 'metric_avg_column': 'guest_revenue_avg_per_guest_journey',\n",
|
|
" 'metric_sdv_column': 'guest_revenue_sdv_per_guest_journey',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" },\n",
|
|
" 'avg_waiver_revenue_per_gj': {\n",
|
|
" 'metric_avg_column': 'waiver_avg_per_guest_journey',\n",
|
|
" 'metric_sdv_column': 'waiver_sdv_per_guest_journey',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" },\n",
|
|
" 'avg_deposit_revenue_per_gj': {\n",
|
|
" 'metric_avg_column': 'deposit_avg_per_guest_journey',\n",
|
|
" 'metric_sdv_column': 'deposit_sdv_per_guest_journey',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" },\n",
|
|
" 'avg_CIH_revenue_per_gj': {\n",
|
|
" 'metric_avg_column': 'check_in_cover_avg_per_guest_journey',\n",
|
|
" 'metric_sdv_column': 'check_in_cover_sdv_per_guest_journey',\n",
|
|
" 'total_counts': 'guest_journeys_count'\n",
|
|
" },\n",
|
|
" 'avg_csat_per_gj_with_response': {\n",
|
|
" 'metric_avg_column': 'csat_avg_per_guest_journey_with_response',\n",
|
|
" 'metric_sdv_column': 'csat_sdv_per_guest_journey_with_response',\n",
|
|
" 'total_counts': 'guest_journey_with_responses_count'\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"# Define the metrics that will be the main ones for this A/B test:\n",
|
|
"main_metrics = ['avg_guest_revenue_per_gj', 'conversion_rate', 'payment_rate']"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Run the computation of the metrics and statistical significance"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 47,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" metric relative_difference p_value\n",
|
|
"0 conversion_rate 0.002890 0.587414\n",
|
|
"1 payment_rate 0.016237 0.167865\n",
|
|
"2 waiver_payment_rate 0.011461 0.398951\n",
|
|
"3 deposit_payment_rate 0.038060 0.303386\n",
|
|
"4 CIH_payment_rate 0.100579 0.173649\n",
|
|
"5 avg_guest_revenue_per_gj 0.020523 0.193648\n",
|
|
"6 avg_waiver_revenue_per_gj 0.017515 0.295568\n",
|
|
"7 avg_deposit_revenue_per_gj 0.056502 0.217435\n",
|
|
"8 avg_CIH_revenue_per_gj 0.103937 0.162819\n",
|
|
"9 avg_csat_per_gj_with_response -0.005045 0.306604\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Call the function to calculate the Z-test for each metric and aggregate the results\n",
|
|
"z_test_results_df = run_z_tests(df, z_stat_metric_definition=z_stat_metric_definition, variations=variations)\n",
|
|
"\n",
|
|
"# Call the function to calculate the T-test for each metric and aggregate the results\n",
|
|
"t_test_results_df = run_t_tests(df, t_stat_metric_definition=t_stat_metric_definition, variations=variations)\n",
|
|
"\n",
|
|
"# Add a new column to identify whether it's from Z-test or T-test\n",
|
|
"z_test_results_df['test_type'] = 'Z-test'\n",
|
|
"t_test_results_df['test_type'] = 'T-test'\n",
|
|
"\n",
|
|
"# Combine the dataframes after adding the 'test_type' column\n",
|
|
"combined_results_df = pd.concat([z_test_results_df, t_test_results_df], ignore_index=True)\n",
|
|
"\n",
|
|
"# Print the main aggregated DataFrame\n",
|
|
"print(combined_results_df[['metric','relative_difference','p_value']])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Results\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 48,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"\n",
|
|
"ShowNewIllustrations results (last updated at 2025-04-16)\n",
|
|
"\n",
|
|
"Total Guest Journeys affected by this A/B test: 41934 - Total Guest Revenue: 444319 GBP.\n",
|
|
" Variation OldIllustrations: Guest Journeys 21018 (50.1%) - Guest Revenue: 220443 GBP (49.6%).\n",
|
|
" Variation NewIllustrations: Guest Journeys 20916 (49.9%) - Guest Revenue: 223876 GBP (50.4%).\n",
|
|
"\n",
|
|
"Main Metrics - Comparing NewIllustrations vs. OldIllustrations.\n",
|
|
"\n",
|
|
"CONVERSION RATE (not significant): 77.2% vs. 77.0% (0.2% ppts.| 0.3%).\n",
|
|
"PAYMENT RATE (not significant): 41.5% vs. 40.8% (0.7% ppts.| 1.6%).\n",
|
|
"AVG GUEST REVENUE PER GJ (not significant): 10.7 vs. 10.49 (0.22 ppts.| 2.1%).\n",
|
|
"\n",
|
|
"Other Metrics\n",
|
|
"\n",
|
|
"WAIVER PAYMENT RATE (not significant): 34.5% vs. 34.1% (0.4% ppts.| 1.1%).\n",
|
|
"DEPOSIT PAYMENT RATE (not significant): 6.9% vs. 6.6% (0.3% ppts.| 3.8%).\n",
|
|
"CIH PAYMENT RATE (not significant): 2.0% vs. 1.8% (0.2% ppts.| 10.1%).\n",
|
|
"AVG WAIVER REVENUE PER GJ (not significant): 10.04 vs. 9.87 (0.17 ppts.| 1.8%).\n",
|
|
"AVG DEPOSIT REVENUE PER GJ (not significant): 0.49 vs. 0.46 (0.03 ppts.| 5.7%).\n",
|
|
"AVG CIH REVENUE PER GJ (not significant): 0.17 vs. 0.16 (0.02 ppts.| 10.4%).\n",
|
|
"AVG CSAT PER GJ WITH RESPONSE (not significant): 3.78 vs. 3.8 (-0.02 ppts.| -0.5%).\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print('\\n{} results (last updated at {})\\n'.format(ab_test_name, last_update))\n",
|
|
"\n",
|
|
"# Get main volume indicators per variation\n",
|
|
"grouped_data = df.groupby('variation')[[\"guest_journeys_count\",\"guest_revenue_sum\"]].sum()\n",
|
|
"\n",
|
|
"# Find the totals over any variation\n",
|
|
"total_count = grouped_data.sum()\n",
|
|
"\n",
|
|
"# Print overall indicators for volumes\n",
|
|
"print('Total Guest Journeys affected by this A/B test: {} - Total Guest Revenue: {} GBP.'.format(int(total_count.loc[\"guest_journeys_count\"]), \n",
|
|
" int(total_count.loc[\"guest_revenue_sum\"])))\n",
|
|
"for var in variations:\n",
|
|
" print(' Variation {}: Guest Journeys {} ({}%) - Guest Revenue: {} GBP ({}%).'.format(\n",
|
|
" var, \n",
|
|
" int(grouped_data.loc[var,'guest_journeys_count']), \n",
|
|
" round(100*(grouped_data.loc[var,'guest_journeys_count']/total_count.loc[\"guest_journeys_count\"]),1),\n",
|
|
" int(grouped_data.loc[var,'guest_revenue_sum']),\n",
|
|
" round(100*(grouped_data.loc[var,'guest_revenue_sum']/total_count.loc[\"guest_revenue_sum\"]),1)\n",
|
|
" ))\n",
|
|
"\n",
|
|
"# Split results whether the metrics are main metrics or not\n",
|
|
"main_metrics_rows = combined_results_df[combined_results_df['metric'].isin(main_metrics)]\n",
|
|
"other_metrics_rows = combined_results_df[~combined_results_df['metric'].isin(main_metrics)]\n",
|
|
"\n",
|
|
"def print_metrics(df, header=None):\n",
|
|
" if header:\n",
|
|
" print(f'\\n{header}\\n')\n",
|
|
"\n",
|
|
" for row in df.iterrows():\n",
|
|
" metric = row[1]['metric'].upper().replace('_', ' ')\n",
|
|
" if row[1]['test_type'] == 'Z-test':\n",
|
|
" value_a = str(round(100 * row[1]['variation_A_value'], 1)) + '%'\n",
|
|
" value_b = str(round(100 * row[1]['variation_B_value'], 1)) + '%'\n",
|
|
" abs_diff = str(round(100 * row[1]['absolute_difference'], 1)) + '%'\n",
|
|
" else:\n",
|
|
" value_a = str(round(row[1]['variation_A_value'], 2))\n",
|
|
" value_b = str(round(row[1]['variation_B_value'], 2))\n",
|
|
" abs_diff = str(round(row[1]['absolute_difference'], 2))\n",
|
|
" rel_diff = str(round(100 * row[1]['relative_difference'], 1)) + '%'\n",
|
|
" stat_sign = row[1]['is_significant_95']\n",
|
|
"\n",
|
|
" if stat_sign:\n",
|
|
" print(f\"{metric} - SIGNIFICANT RESULT: {value_b} vs. {value_a} ({abs_diff} ppts.| {rel_diff}).\")\n",
|
|
" else:\n",
|
|
" print(f\"{metric} (not significant): {value_b} vs. {value_a} ({abs_diff} ppts.| {rel_diff}).\")\n",
|
|
"\n",
|
|
"# Print main metrics\n",
|
|
"print_metrics(main_metrics_rows, header=\"Main Metrics - Comparing {} vs. {}.\".format(var_B, var_A))\n",
|
|
"\n",
|
|
"# Print other metrics\n",
|
|
"print_metrics(other_metrics_rows, header=\"Other Metrics\")\n"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": ".venv",
|
|
"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.12.3"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|