data-jupyter-notebooks/ab_test_guest_journey_monitoring.ipynb

708 lines
70 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": 47,
"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": 48,
"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": 49,
"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": 50,
"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": [
"## 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": 51,
"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": [
"# A/B test name to measure\n",
"#ab_test_name = \"AAVariantTest\"\n",
"ab_test_name = \"VerificationProductSelectionButtonPosition\"\n",
"\n",
"# 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": 52,
"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": 53,
"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": 54,
"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": 55,
"metadata": {},
"outputs": [],
"source": [
"# Define the variations in which we want to run the tests\n",
"var_A = 'Fixed'\n",
"var_B = 'Relative'\n",
"variations = [var_A, var_B]\n",
"\n",
"# 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": 56,
"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": 57,
"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% | 150.0%).\n",
"PAYMENT RATE (not significant): 20.8% vs. 22.2% (-1.4% | -6.2%).\n",
"AVG GUEST REVENUE PER GJ (not significant): 5.9 vs. 5.8 (0.1 | 1.8%).\n",
"\n",
"Other Metrics\n",
"\n",
"WAIVER PAYMENT RATE (not significant): 16.7% vs. 16.7% (0.0% | 0.0%).\n",
"DEPOSIT PAYMENT RATE (not significant): 4.2% vs. 5.6% (-1.4% | -25.0%).\n",
"CIH PAYMENT RATE (not significant): 0.0% vs. 5.6% (-5.6% | -100.0%).\n",
"AVG WAIVER REVENUE PER GJ (not significant): 5.64 vs. 4.91 (0.73 | 14.9%).\n",
"AVG DEPOSIT REVENUE PER GJ (not significant): 0.26 vs. 0.37 (-0.11 | -28.9%).\n",
"AVG CIH REVENUE PER GJ (not significant): 0.0 vs. 0.52 (-0.52 | -100.0%).\n",
"AVG CSAT PER GJ WITH RESPONSE (not significant): 3.0 vs. 4.0 (-1.0 | -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} | {rel_diff}).\")\n",
" else:\n",
" print(f\"{metric} (not significant): {value_b} vs. {value_a} ({abs_diff} | {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"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"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
}