Visualization & Plotting#
Source Files
twiga/core/plot/theme.py- Twiga brand theme & palettetwiga/core/plot/timeseries.py- forecast & time series plotstwiga/core/plot/exploration.py- scatter, line, correlation heatmaptwiga/core/plot/distribution.py- density, KDE, CDF, distributiontwiga/core/plot/stats.py- ACF, metrics bar, PIT histogram, reliability diagramtwiga/core/plot/residuals.py- residual diagnostic plotstwiga/core/plot/matplot_theme.py- Matplotlib theme &plot_predictiontwiga/core/plot/gt.py- Great Tables result formatting
Twiga provides a professional visualisation suite built on lets-plot (primary) and Matplotlib (optional). All plotting functions follow the Twiga brand theme - teal palette, clean grids, Inter font stack.
All public functions are available through a single flat import:
from twiga.core.plot import (
plot_forecast, plot_timeseries, plot_quantile_fan,
twiga_theme, TWIGA_PALETTE,
plot_acf, plot_reliability_diagram,
scatter_plot, correlation_heatmap,
)
Installation
lets-plot and Great Tables are included in the base install (pip install twiga).
Matplotlib, Plotly, and SciencePlots are optional — install with:
pip install "twiga[plots]"
Twiga Theme & Palette#
All lets-plot figures use the Twiga brand theme automatically. You can also apply it manually or access the palette directly.
from lets_plot import LetsPlot
from twiga.core.plot import TWIGA_PALETTE, twiga_theme
LetsPlot.setup_html() # required once per notebook session
# 10-colour colorblind-friendly palette
print(TWIGA_PALETTE)
# ['#107591', '#E69F00', '#D55E00', '#069fac', '#CC79A7',
# '#009E73', '#56595e', '#8aabb4', '#a19368', '#7035b7']
# Apply to any ggplot figure
from lets_plot import ggplot, aes, geom_line
p = ggplot(df, aes("x", "y")) + geom_line() + twiga_theme()
twiga_theme() signature:
twiga_theme(
show_x_axis=True,
font_size=11,
line_width=0.8,
x_axis_angle=0,
legend_pos="top",
grid=True,
)
Result Formatting#
The gt.py module renders model evaluation results as publication-quality tables using Great Tables.
twiga_report()#
from twiga.core.plot.gt import twiga_report
res = (
metrics_df
.groupby("Model")[["mae", "corr", "rmse", "smape"]]
.mean()
.round(2)
.reset_index()
.rename(columns={"mae": "MAE", "corr": "Corr", "rmse": "RMSE", "smape": "SMAPE"})
)
twiga_report(
res,
metrics=["MAE", "Corr", "SMAPE", "RMSE"],
minimize_cols=["MAE", "SMAPE", "RMSE"],
maximize_cols=["Corr"],
)
Best values are highlighted with a teal fill; alternating row stripes aid readability. The table renders inline in Jupyter and can be exported to HTML/LaTeX.
create_backtest_summary_table()#
For fold-level backtesting results:
from twiga.core.plot.gt import create_backtest_summary_table
create_backtest_summary_table(
backtest_df,
metrics=["MAE", "RMSE"],
fold_col="fold",
model_col="Model",
minimize_cols=["MAE", "RMSE"],
)
Lets-Plot Functions#
Setup#
from lets_plot import LetsPlot
LetsPlot.setup_html() # call once at the top of each notebook
To export figures to PDF / PNG / SVG:
from lets_plot import ggsave
ggsave(p, "forecast.pdf")
Forecast Plots#
plot_forecast()#
Actual vs. predicted with optional confidence interval ribbon:
from twiga.core.plot import plot_forecast
p = plot_forecast(
predictions_df,
date_col="timestamp",
actual_col="Actual",
forecast_col="forecast",
lower_col="lower", # optional
upper_col="upper", # optional
n_samples=7 * 48,
title="Net Load Forecast",
y_label="Net Load (kW)",
)
p
plot_timeseries()#
One or more time series overlaid on the same axes:
from twiga.core.plot import plot_timeseries
p = plot_timeseries(
df,
y_cols=["load_mw", "temperature"],
date_col="timestamp",
title="Load & Temperature",
)
p
plot_forecast_grid()#
Multi-model comparison - one panel per model using facet_wrap. The input DataFrame must contain a column identifying the model (default: "Model"):
from twiga.core.plot import plot_forecast_grid
p = plot_forecast_grid(
combined_df, # long-format DataFrame with a "Model" column
actual_col="Actual",
forecast_col="forecast",
model_col="Model",
date_col="timestamp",
n_samples=7 * 48,
)
p
General-Purpose Exploration Plots#
Function |
Description |
|---|---|
|
Simple line chart from arrays or lists |
|
Scatter chart with optional colour grouping |
|
Pair-wise scatter matrix |
|
Correlation matrix heatmap |
Example - line plot:
from twiga.core.plot import line_plot
p = line_plot(y=load_series, x=None, title="Load Time Series", y_label="kW")
p
Example - scatter matrix:
from twiga.core.plot import scatter_matrix
p = scatter_matrix(
df,
variables=["temperature", "ghi", "wind_speed"],
targets=["load_mw"],
n_sample=500, # random subsample for performance; use None to disable
)
p
Example - correlation heatmap:
from twiga.core.plot import correlation_heatmap
from twiga.core.stats import rank_correlation
corr_df = rank_correlation(df, target="load_mw", features=feature_cols, method="spearman")
p = correlation_heatmap(corr_df)
p
Distribution Plots#
Function |
Description |
|---|---|
|
Density / KDE fill plot |
|
Kernel density estimate overlay |
|
Empirical CDF |
|
Combined PDF + group comparison |
Example - density by split:
from twiga.core.plot import plot_density
p = plot_density(df, x_col="load_mw", color_col="split", title="Load Distribution")
p
Probabilistic Forecast Plots#
These functions implement best-practice probabilistic forecast visualisation.
plot_quantile_fan() - Fan Chart#
Graduated ribbon bands widest-to-narrowest for quantile forecasts:
from twiga.core.plot import plot_quantile_fan
p = plot_quantile_fan(
quantile_df,
date_col="timestamp",
actual_col="Actual",
median_col="q50",
quantile_pairs=[("q10", "q90"), ("q25", "q75"), ("q40", "q60")],
title="Probabilistic Forecast - Fan Chart",
y_label="Net Load (kW)",
)
p
plot_forecast_intervals() - Interval Comparison#
Overlay prediction intervals from multiple sources (e.g. different conformal methods):
from twiga.core.plot import plot_forecast_intervals
interval_specs = [
{"lower_col": "lower_split", "upper_col": "upper_split", "label": "Split CP"},
{"lower_col": "lower_aci", "upper_col": "upper_aci", "label": "ACI"},
]
p = plot_forecast_intervals(
combined_df,
interval_specs=interval_specs,
date_col="timestamp",
actual_col="Actual",
title="Conformal Interval Comparison",
)
p
Statistical & Evaluation Plots#
plot_acf() - ACF / PACF#
from twiga.core.plot import plot_acf
import pandas as pd
p = plot_acf(pd.Series(residuals), max_lag=48, partial=False, title="Residual ACF")
p
Set partial=True for the Partial Autocorrelation Function.
plot_pit_histogram() - Calibration Diagnostic#
Probability Integral Transform histogram - a uniform distribution indicates a well-calibrated forecast:
from twiga.core.plot import plot_pit_histogram
import numpy as np
from scipy.stats import norm
pit_values = norm.cdf(y_true, loc=mu_pred, scale=sigma_pred)
p = plot_pit_histogram(pit_values, n_bins=20, title="PIT Histogram")
p
plot_reliability_diagram() - Coverage Calibration#
Plots empirical vs. nominal coverage levels to diagnose interval calibration:
from twiga.core.plot import plot_reliability_diagram
nominal = [0.1, 0.25, 0.5, 0.75, 0.9]
empirical = [0.11, 0.26, 0.49, 0.76, 0.88]
p = plot_reliability_diagram(nominal, empirical, title="Reliability Diagram")
p
plot_metrics_bar() - Model Comparison Bar Chart#
from twiga.core.plot import plot_metrics_bar
p = plot_metrics_bar(
summary_df,
metric_col="MAE",
model_col="Model", # default column name is "Model" (capital M)
title="MAE by Model",
)
p
Residual Diagnostic Plots#
Four standard regression diagnostic plots built on lets-plot:
from twiga.core.plot import (
residual_plot,
normal_q_qplot,
squared_residual_plot,
residual_leverage_plot,
)
# diagnostics_df must contain columns: fitted, residuals, leverage, cooks_d
residual_plot(diagnostics_df) # Tukey–Anscombe: residuals vs fitted
normal_q_qplot(diagnostics_df) # Normal Q-Q
squared_residual_plot(diagnostics_df) # Scale-Location (homoscedasticity)
residual_leverage_plot(diagnostics_df) # Residuals vs Leverage (influential points)
These functions pair naturally with the residual diagnostic statistics in twiga.core.stats:
from twiga.core.stats import cooks_distance, leverage_statistic, get_standardized_residual
import pandas as pd
diagnostics_df = pd.DataFrame({
"fitted": fitted_values,
"residuals": residuals,
"leverage": leverage_statistic(X),
"cooks_d": cooks_distance(fitted_values, residuals, n_params=X.shape[1]),
})
residual_plot(diagnostics_df)
residual_leverage_plot(diagnostics_df)
Matplotlib Utilities#
For users who prefer Matplotlib, matplot_theme.py provides a matching theme and helper:
from twiga.core.plot.matplot_theme import configure_matlib_style, plot_prediction
import matplotlib.pyplot as plt
configure_matlib_style() # apply Twiga brand style
fig, ax = plt.subplots()
plot_prediction(ax, true=y_true, mu=y_pred) # actual (dots) + forecast (line)
plt.tight_layout()
plt.show()
Use plotting_context() as a context manager to temporarily apply the style:
from twiga.core.plot.matplot_theme import plotting_context
with plotting_context():
fig, ax = plt.subplots()
ax.plot(dates, values)
Supported Backends#
Backend |
Package |
Use Case |
|---|---|---|
lets-plot |
|
Interactive plots, notebook display, PDF/PNG/SVG export |
Matplotlib |
|
Publication figures, custom axes |
Great Tables |
|
Formatted metric comparison tables |
Optional: SciencePlots
scienceplots>=2.2.1 can be used for academic publication styling via the style parameter in configure_matlib_style().