Skip to content

Commit 99b1e28

Browse files
committed
A/B Testing Template
1 parent c14e435 commit 99b1e28

File tree

1 file changed

+102
-8
lines changed

1 file changed

+102
-8
lines changed
Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,107 @@
1-
from preswald import text, plotly, connect, get_df, table
1+
from preswald import (
2+
Workflow, connect, get_df, text, table, plotly, selectbox,
3+
slider, checkbox, separator
4+
)
25
import pandas as pd
36
import plotly.express as px
47

5-
text("# A/B Test Results Dashboard")
6-
text("Visualize and analyze A/B test results with statistical significance indicators.")
8+
workflow = Workflow()
79

8-
# Load the CSV
9-
connect()
10-
df = get_df('sample_csv')
10+
@workflow.atom()
11+
def init():
12+
text("# 🧪 A/B Testing Dashboard")
13+
text("Analyze A/B test results, compare conversion rates, evaluate statistical significance, and discover top-performing variants.")
14+
connect()
1115

12-
# Show the data
13-
table(df)
16+
@workflow.atom(dependencies=["init"])
17+
def load_data():
18+
df = get_df("sample_csv").copy()
19+
df["Conversion Rate"] = df["Conversion Rate"].str.replace('%', '', regex=False).astype(float)
20+
df["Significance"] = df["Significance"].astype(str)
21+
table(df, title="📋 Raw Data", limit=10)
22+
return df
23+
24+
@workflow.atom(dependencies=["load_data"])
25+
def filter_significant(load_data):
26+
show_only = checkbox("Show only significant results?", default=False)
27+
return load_data[load_data["Significance"].str.lower() == "yes"] if show_only else load_data
28+
29+
@workflow.atom(dependencies=["filter_significant"])
30+
def variant_selector(filter_significant):
31+
variants = filter_significant["Variant"].unique().tolist()
32+
return selectbox("🎛️ Choose a Variant", options=variants, default=variants[0])
33+
34+
@workflow.atom(dependencies=["filter_significant", "variant_selector"])
35+
def show_variant_table(filter_significant, variant_selector):
36+
df = filter_significant[filter_significant["Variant"] == variant_selector].reset_index(drop=True).copy()
37+
df["Test Number"] = df.index + 1
38+
table(df, title=f"🔍 Details for {variant_selector}")
39+
return df
40+
41+
@workflow.atom(dependencies=["show_variant_table"])
42+
def plot_variant_trend(show_variant_table):
43+
df = show_variant_table
44+
text(f"## 📈 Conversion Trend: {df['Variant'].iloc[0]}")
45+
fig = px.line(
46+
df, x="Test Number", y="Conversion Rate", markers=True,
47+
title="Conversion Rate Over Time", labels={"Conversion Rate": "Conversion Rate (%)"}
48+
)
49+
plotly(fig)
50+
51+
@workflow.atom(dependencies=["filter_significant"])
52+
def conversion_lift_analysis(filter_significant):
53+
text("## 🚀 Conversion Lift Compared to Control")
54+
55+
# Compute average CR per variant
56+
summary = filter_significant.groupby("Variant").agg(
57+
AvgCR=("Conversion Rate", "mean")
58+
).reset_index()
59+
60+
# Get control rate
61+
control_rate = summary[summary["Variant"] == "Control"]["AvgCR"].values[0]
62+
63+
# Calculate lift
64+
summary["Lift vs Control (%)"] = summary["AvgCR"] - control_rate
65+
66+
fig = px.bar(
67+
summary, x="Variant", y="Lift vs Control (%)",
68+
color="Lift vs Control (%)", color_continuous_scale="Viridis",
69+
text="Lift vs Control (%)", title="Conversion Rate Lift (vs. Control)"
70+
)
71+
fig.update_traces(texttemplate='%{text:.2f}%', textposition='outside')
72+
plotly(fig)
73+
74+
@workflow.atom(dependencies=["filter_significant"])
75+
def funnel_view(filter_significant):
76+
text("## 🔄 Mini Funnel: Visitors → Conversions")
77+
summary = filter_significant.groupby("Variant").agg({
78+
"Visitors": "sum",
79+
"Conversions": "sum"
80+
}).reset_index()
81+
82+
fig = px.bar(
83+
summary.melt(id_vars="Variant", value_vars=["Visitors", "Conversions"]),
84+
x="Variant", y="value", color="variable", barmode="group",
85+
title="Visitor vs Conversion Totals", text="value",
86+
labels={"value": "Count", "variable": "Stage"}
87+
)
88+
plotly(fig)
89+
90+
@workflow.atom(dependencies=["filter_significant"])
91+
def callout_best_variant(filter_significant):
92+
summary = (
93+
filter_significant.groupby("Variant")["Conversion Rate"]
94+
.mean()
95+
.reset_index()
96+
.sort_values(by="Conversion Rate", ascending=False)
97+
)
98+
best = summary.iloc[0]
99+
text(f"## 🏆 Best Variant: **{best['Variant']}**")
100+
text(f"Achieved an average **{best['Conversion Rate']:.2f}%** conversion rate.")
101+
102+
@workflow.atom()
103+
def wrap_up():
104+
separator()
105+
text("_This interactive dashboard is built with [Preswald](https://preswald.com), empowering anyone to build production dashboards in Python._")
106+
107+
workflow.execute()

0 commit comments

Comments
 (0)