data-jupyter-notebooks/ab_test_guest_journey_monitoring.ipynb

721 lines
71 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": 23,
"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": 24,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"/home/uri/.superhog-dwh/credentials.yml\n"
]
}
],
"source": [
"CREDS_FILEPATH = pathlib.Path.home() / \".superhog-dwh\" / \"credentials.yml\"\n",
"print(CREDS_FILEPATH)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"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": 26,
"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": 27,
"metadata": {},
"outputs": [],
"source": [
"# A/B test name to measure\n",
"#ab_test_name = \"AAVariantTest\"\n",
"ab_test_name = \"VerificationProductSelectionButtonPosition\"\n",
"\n",
"# Define the variations in which we want to run the tests\n",
"var_A = 'Fixed' # Ideally, this should be the control group\n",
"var_B = 'Relative' # 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": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" ab_test_name variation last_update \\\n",
"0 VerificationProductSelectionButtonPosition Fixed 2024-12-11 \n",
"1 VerificationProductSelectionButtonPosition Relative 2024-12-11 \n",
"\n",
" guest_journeys_count guest_journey_started_count \\\n",
"0 18 18 \n",
"1 24 24 \n",
"\n",
" guest_journey_completed_count guest_journey_with_responses_count \\\n",
"0 3 2 \n",
"1 10 3 \n",
"\n",
" guest_journey_with_payment_count guest_revenue_count deposit_count ... \\\n",
"0 4 4 1 ... \n",
"1 5 5 1 ... \n",
"\n",
" guest_revenue_avg_per_guest_journey guest_revenue_sdv_per_guest_journey \\\n",
"0 5.800667 13.402593 \n",
"1 5.902642 13.025999 \n",
"\n",
" deposit_avg_per_guest_journey deposit_sdv_per_guest_journey \\\n",
"0 0.367500 1.559170 \n",
"1 0.261454 1.280859 \n",
"\n",
" waiver_avg_per_guest_journey waiver_sdv_per_guest_journey \\\n",
"0 4.910261 11.769242 \n",
"1 5.641188 13.081060 \n",
"\n",
" check_in_cover_avg_per_guest_journey check_in_cover_sdv_per_guest_journey \\\n",
"0 0.522906 2.2185 \n",
"1 0.000000 0.0000 \n",
"\n",
" csat_avg_per_guest_journey_with_response \\\n",
"0 4.0 \n",
"1 3.0 \n",
"\n",
" csat_sdv_per_guest_journey_with_response \n",
"0 1.414214 \n",
"1 1.732051 \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",
"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": 29,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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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": 30,
"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": 31,
"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": 32,
"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": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" metric relative_difference p_value\n",
"0 conversion_rate 1.500000 0.082857\n",
"1 payment_rate -0.062500 0.913554\n",
"2 waiver_payment_rate 0.000000 1.000000\n",
"3 deposit_payment_rate -0.250000 0.834319\n",
"4 CIH_payment_rate -1.000000 0.242526\n",
"5 avg_guest_revenue_per_gj 0.017580 0.980584\n",
"6 avg_waiver_revenue_per_gj 0.148857 0.851687\n",
"7 avg_deposit_revenue_per_gj -0.288560 0.816919\n",
"8 avg_CIH_revenue_per_gj -1.000000 0.331333\n",
"9 avg_csat_per_gj_with_response -0.250000 0.608173\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": 34,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"VerificationProductSelectionButtonPosition results (last updated at 2024-12-11)\n",
"\n",
"Total Guest Journeys affected by this A/B test: 42 - Total Guest Revenue: 246 GBP.\n",
" Variation Fixed: Guest Journeys 18 (42.9%) - Guest Revenue: 104 GBP (42.4%).\n",
" Variation Relative: Guest Journeys 24 (57.1%) - Guest Revenue: 141 GBP (57.6%).\n",
"\n",
"Main Metrics - Comparing Relative vs. Fixed.\n",
"\n",
"CONVERSION RATE (not significant): 41.7% vs. 16.7% (25.0% ppts.| 150.0%).\n",
"PAYMENT RATE (not significant): 20.8% vs. 22.2% (-1.4% ppts.| -6.2%).\n",
"AVG GUEST REVENUE PER GJ (not significant): 5.9 vs. 5.8 (0.1 ppts.| 1.8%).\n",
"\n",
"Other Metrics\n",
"\n",
"WAIVER PAYMENT RATE (not significant): 16.7% vs. 16.7% (0.0% ppts.| 0.0%).\n",
"DEPOSIT PAYMENT RATE (not significant): 4.2% vs. 5.6% (-1.4% ppts.| -25.0%).\n",
"CIH PAYMENT RATE (not significant): 0.0% vs. 5.6% (-5.6% ppts.| -100.0%).\n",
"AVG WAIVER REVENUE PER GJ (not significant): 5.64 vs. 4.91 (0.73 ppts.| 14.9%).\n",
"AVG DEPOSIT REVENUE PER GJ (not significant): 0.26 vs. 0.37 (-0.11 ppts.| -28.9%).\n",
"AVG CIH REVENUE PER GJ (not significant): 0.0 vs. 0.52 (-0.52 ppts.| -100.0%).\n",
"AVG CSAT PER GJ WITH RESPONSE (not significant): 3.0 vs. 4.0 (-1.0 ppts.| -25.0%).\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
}