4.2. Charts
Tables are a powerful way to share results with stakeholders, but often you’ll also want to visualise the same data. The good news is that once you’ve aggregated your data for tables, half the work is already done for charts - both start from the same summarised dataset.
The process is similar:
Aggregate the data to the level you want to analyse (e.g. by Driver Age, Area, or Vehicle Group).
Pass the aggregated results into a plotting library to create visuals.
Whereas great_tables gave us polished static outputs, charting libraries let us go further - adding interactivity, multiple views of the same data, and dashboards.
In Python you’ll commonly see:
-
matplotlib: the foundation library, very flexible but low-level.
-
seaborn: built on matplotlib, great for quick statistical charts.
-
plotly: interactive charts out of the box, ideal for sharing and dashboarding.
Here we’ll focus on Plotly, since it produces interactive visuals that are easy to embed in reports or expand into dashboards with Plotly Dash. Like great_tables, it’s highly customisable and can give professional-looking outputs straight from Python code.
Charting function
The function below creates a dual-axis chart for frequency analysis:
Bars show exposure.
Lines show actual vs predicted frequency.
This mirrors the tables we built earlier, but presents the comparison visually so trends and deviations stand out more clearly.
def plot_aggregated_data(visual_data: pl.DataFrame, feature: str, target: str, prediction: str, exposure: str) -> None:
fig = go.Figure()
# Bar: Exposure
fig.add_trace(go.Bar(
x=visual_data[feature],
y=visual_data[exposure],
name=exposure,
marker_color='lightskyblue',
yaxis='y1'
))
# Line: ClaimCount
fig.add_trace(go.Scatter(
x=visual_data[feature],
y=visual_data[target],
name=target,
mode='lines+markers',
line=dict(color='firebrick'),
yaxis='y2'
))
# Line: gbm_predictions
fig.add_trace(go.Scatter(
x=visual_data[feature],
y=visual_data[prediction],
name="Frequency Predictions",
mode='lines+markers',
line=dict(color='green', dash='dot'),
yaxis='y2'
))
# Layout with dual y-axes
fig.update_layout(
title=f"Frequency - Actuals vs Prediction - {feature}",
xaxis=dict(title=feature),
yaxis=dict(
title=exposure,
side="left",
showgrid=False
),
yaxis2=dict(
title="Frequency",
overlaying="y",
side="right"
),
barmode='group',
legend=dict(x=0.01, y=0.99)
)
return fig
Running it looks like this:
plot_aggregated_data(visual_data = aggregated_data,
feature = feature,
target = 'Frequency',
prediction = 'FrequencyPrediction',
exposure = 'Exposure')
Which generates the below plot.