Designing an Advanced Multi-Page Analytics Dashboard with Panel
These articles are AI-generated summaries. Please check the original sources for full details.
How to Design an Advanced Multi-Page Interactive Analytics Dashboard with Dynamic Filtering, Live KPIs, and Rich Visual Exploration Using Panel
Asif Razzaq demonstrates a working implementation of a multi-page analytics dashboard using Panel, complete with live KPI updates and dynamic filtering. The system processes synthetic time-series data across segments and regions, enabling real-time exploration of traffic, conversions, and revenue.
Why This Matters
Real-world dashboards must balance interactivity with performance, but idealized models often overlook the complexity of dynamic filtering and live updates. This tutorial addresses these challenges by leveraging Panel’s reactive widgets and hvPlot’s visualization capabilities, ensuring updates occur without redundant computations. Failure to manage these aspects can lead to latency or incorrect data aggregation, costing hours in debugging.
Key Insights
- “8-hour App Engine outage, 2012”: While not directly relevant, highlights the cost of poor real-time system design.
- “Sagas over ACID for e-commerce”: Not applicable here, but underscores the need for flexible state management in dashboards.
- “Panel used by [companies]”: Panel is widely adopted in data science for its integration with Python libraries like hvPlot and Bokeh.
Working Example
import sys, subprocess
def install_deps():
pkgs = ["panel", "hvplot", "pandas", "numpy", "bokeh"]
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q"] + pkgs)
try:
import panel as pn
import hvplot.pandas
import pandas as pd
import numpy as np
except ImportError:
install_deps()
import panel as pn
import hvplot.pandas
import pandas as pd
import numpy as np
pn.extension()
rng = np.random.default_rng(42)
dates = pd.date_range("2024-01-01", periods=365, freq="D")
segments = ["A", "B", "C"]
regions = ["North", "South", "East", "West"]
base = pd.DataFrame(
{
"date": np.tile(dates, len(segments) * len(regions)),
"segment": np.repeat(segments, len(dates) * len(regions)),
"region": np.repeat(np.tile(regions, len(segments)), len(dates)),
}
)
base["traffic"] = (
100
+ 40 * np.sin(2 * np.pi * base["date"].dt.dayofyear / 365)
+ rng.normal(0, 15, len(base))
)
trend = {"A": 1.0, "B": 1.5, "C": 2.0}
base["traffic"] *= base["segment"].map(trend)
base["conversions"] = (base["traffic"] * rng.uniform(0.01, 0.05, len(base))).astype(int)
base["revenue"] = base["conversions"] * rng.uniform(20, 60, len(base))
df = base.reset_index(drop=True)
segment_sel = pn.widgets.CheckBoxGroup(name="Segment", value=segments[:2], options=segments, inline=True)
region_sel = pn.widgets.MultiChoice(name="Region", value=["North"], options=regions)
metric_sel = pn.widgets.Select(name="Metric", value="traffic", options=["traffic", "conversions", "revenue"])
date_range = pn.widgets.DateRangeSlider(
name="Date Range",
start=df["date"].min(),
end=df["date"].max(),
value=(df["date"].min(), df["date"].max()),
)
smooth_slider = pn.widgets.IntSlider(name="Rolling Window (days)", start=1, end=30, value=7)
def filtered_df(segment, region, drange):
d1, d2 = drange
mask = (
df["segment"].isin(segment)
& df["region"].isin(region or regions)
& (df["date"] >= d1)
& (df["date"] <= d2)
)
sub = df[mask].copy()
if sub.empty:
return df.iloc[:0]
return sub
@pn.depends(segment_sel, region_sel, metric_sel, smooth_slider, date_range)
def timeseries_plot(segment, region, metric, window, drange):
data = filtered_df(segment, region, drange)
if data.empty:
return pn.pane.Markdown("### No data for current filters")
grouped = data.sort_values("date").groupby("date")[metric].sum()
line = grouped.hvplot.line(title=f"{metric.title()} over time", ylabel=metric.title())
if window > 1:
smooth = grouped.rolling(window).mean().hvplot.line(line_width=3, alpha=0.6)
return (line * smooth).opts(legend_position="top_left")
return line
@pn.depends(segment_sel, region_sel, metric_sel, date_range)
def segment_bar(segment, region, metric, drange):
data = filtered_df(segment, region, drange)
if data.empty:
return pn.pane.Markdown("### No data to aggregate")
agg = data.groupby("segment")[metric].sum().sort_values(ascending=False)
return agg.hvplot.bar(title=f"{metric.title()} by Segment", yaxis=None)
@pn.depends(segment_sel, region_sel, metric_sel, date_range)
def region_heatmap(segment, region, metric, drange):
data = filtered_df(segment, region, drange)
if data.empty:
return pn.pane.Markdown("### No data to aggregate")
pivot = data.pivot_table(index="segment", columns="region", values=metric, aggfunc="sum")
return pivot.hvplot.heatmap(title=f"{metric.title()} Heatmap", clabel=metric.title())
kpi_source = df.copy()
kpi_idx = [0]
def compute_kpi(slice_df):
if slice_df.empty:
return 0, 0, 0
total_rev = slice_df["revenue"].sum()
avg_conv = slice_df["conversions"].mean()
cr = (slice_df["conversions"].sum() / slice_df["traffic"].sum()) * 100
return total_rev, avg_conv, cr
kpi_value = pn.indicators.Number(name="Total Revenue (window)", value=0, format="$0,0")
conv_value = pn.indicators.Number(name="Avg Conversions", value=0, format="0.0")
cr_value = pn.indicators.Number(name="Conversion Rate", value=0, format="0.00%")
def update_kpis():
step = 200
start = kpi_idx[0]
end = start + step
if start >= len(kpi_source):
kpi_idx[0] = 0
start, end = 0, step
window_df = kpi_source.iloc[start:end]
kpi_idx[0] = end
total_rev, avg_conv, cr = compute_kpi(window_df)
kpi_value.value = total_rev
conv_value.value = avg_conv
cr_value.value = cr / 100
pn.state.add_periodic_callback(update_kpis, period=1000, start=True)
controls = pn.WidgetBox(
"### Global Controls",
segment_sel,
region_sel,
metric_sel,
date_range,
smooth_slider,
sizing_mode="stretch_width",
)
page_overview = pn.Column(
pn.pane.Markdown("## Overview: Filtered Time Series"),
controls,
timeseries_plot,
)
page_insights = pn.Column(
pn.pane.Markdown("## Segment & Region Insights"),
pn.Row(segment_bar, region_heatmap),
)
page_live = pn.Column(
pn.pane.Markdown("## Live KPI Window (simulated streaming)"),
pn.Row(kpi_value, conv_value, cr_value),
)
dashboard = pn.Tabs(
("Overview", page_overview),
("Insights", page_insights),
("Live KPIs", page_live),
)
dashboard
Practical Applications
- Use Case: Data analysts using Panel to build real-time dashboards for monitoring e-commerce metrics.
- Pitfall: Overloading widgets with too many dependencies, causing lag in updates.
References:
Continue reading
Next article
Optimizing LLM Training with AdamW and Cosine Decay
Related Content
End-to-End Interactive Analytics Dashboard with PyGWalker
Build a 5,000-transaction e-commerce dashboard with PyGWalker for real-time data exploration.
Building an Advanced Multi-Page Reflex Web Application with Real-Time Features
A step-by-step guide to creating a full-stack Reflex web app in Python with real-time databases, dynamic state management, and reactive UI components.
How to Extract Tables from PDFs Using Python (Without Losing Your Mind)
This article details methods for extracting tables from PDFs using Python, acknowledging the complexities beyond simple text extraction and offering an API solution.