1
1
from preswald import (
2
- Workflow, connect, get_df, text, table, plotly, selectbox,
3
- slider, checkbox, separator
2
+ Workflow,
3
+ connect,
4
+ get_df,
5
+ table,
6
+ text,
7
+ plotly,
8
+ selectbox,
9
+ slider,
10
+ separator,
4
11
)
5
- import pandas as pd
6
12
import plotly.express as px
7
13
8
14
workflow = Workflow()
9
15
16
+ # --- 1. Load data and fix types ---
10
17
@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()
15
-
16
- @workflow.atom(dependencies=["init"])
17
18
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
19
+ connect()
20
+ df = get_df("sample_csv")
28
21
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] )
22
+ # Coerce latency columns to float
23
+ df["P50"] = df["P50"].astype("float64")
24
+ df["P95"] = df["P95 "].astype("float64" )
25
+ df["P99"] = df["P99"].astype("float64" )
33
26
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
27
return df
40
28
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)
29
+ # --- 2. Intro text ---
30
+ @workflow.atom()
31
+ def intro():
32
+ text("# 🚦 API Latency Explorer")
33
+ text("Explore P50, P95, and P99 latency across endpoints. Filter, chart, and inspect details.")
50
34
51
- @workflow.atom(dependencies=["filter_significant"])
52
- def conversion_lift_analysis(filter_significant):
53
- text("## 🚀 Conversion Lift Compared to Control")
35
+ # --- 3. Display table ---
36
+ @workflow.atom(dependencies=["load_data"])
37
+ def show_table(load_data):
38
+ text("## 📋 Full Dataset")
39
+ table(load_data)
54
40
55
- # Compute average CR per variant
56
- summary = filter_significant.groupby("Variant").agg(
57
- AvgCR=("Conversion Rate", "mean")
58
- ).reset_index()
41
+ # --- 4. P99 threshold filter ---
42
+ @workflow.atom(dependencies=["load_data"])
43
+ def p99_filter(load_data):
44
+ text("## 🔍 Filter by P99 Latency")
45
+ threshold = slider("Maximum P99 (ms)", min_val=100, max_val=300, step=10, default=200)
46
+ filtered = load_data[load_data["P99"] <= threshold]
59
47
60
- # Get control rate
61
- control_rate = summary[summary["Variant"] == "Control"]["AvgCR"].values[0]
48
+ if filtered.shape[0] > 0:
49
+ table(filtered, title=f"Endpoints with P99 ≤ {threshold} ms")
50
+ else:
51
+ text("⚠️ No endpoints under selected threshold.")
62
52
63
- # Calculate lift
64
- summary["Lift vs Control (%)"] = summary["AvgCR"] - control_rate
53
+ # --- 5. Plot percentiles ---
54
+ @workflow.atom(dependencies=["load_data"])
55
+ def plot_percentiles(load_data):
56
+ text("## 📊 Latency Breakdown (Grouped Bar Chart)")
57
+
58
+ melted = load_data.melt(
59
+ id_vars="Endpoint",
60
+ value_vars=["P50", "P95", "P99"],
61
+ var_name="Percentile",
62
+ value_name="Latency"
63
+ )
65
64
66
65
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)"
66
+ melted,
67
+ x="Endpoint",
68
+ y="Latency",
69
+ color="Percentile",
70
+ barmode="group",
71
+ title="Latency by Endpoint",
72
+ labels={"Latency": "ms"}
70
73
)
71
- fig.update_traces(texttemplate='%{text:.2f}%', textposition='outside' )
74
+ fig.update_layout(xaxis_tickangle=-45 )
72
75
plotly(fig)
73
76
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()
77
+ # --- 6. Per-endpoint breakdown ---
78
+ @workflow.atom(dependencies=["load_data"])
79
+ def endpoint_detail(load_data):
80
+ text("## 🧪 Inspect Specific Endpoint")
81
+ options = load_data["Endpoint"].tolist()
82
+ selected = selectbox("Choose Endpoint", options)
81
83
84
+ row = load_data[load_data["Endpoint"] == selected].iloc[0]
82
85
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"}
86
+ x=["P50 ", "P95 ", "P99"] ,
87
+ y=[row["P50"], row["P95"], row["P99"]] ,
88
+ labels={"x": "Percentile", "y": "Latency (ms)"} ,
89
+ title=f"Latency Profile: {selected}"
87
90
)
88
91
plotly(fig)
89
92
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
-
93
+ # --- 7. Footer ---
102
94
@workflow.atom()
103
- def wrap_up ():
95
+ def end_note ():
104
96
separator()
105
- text("_This interactive dashboard is built with [Preswald](https://preswald.com), empowering anyone to build production dashboards in Python._ ")
97
+ text("✅ Dashboard complete. Use this to spot latency bottlenecks in your backend! ")
106
98
107
- workflow.execute()
99
+ # --- Run ---
100
+ workflow.execute()
0 commit comments