Visualization & Plotting#

Source Files
  • twiga/core/plot/theme.py - Twiga brand theme & palette

  • twiga/core/plot/timeseries.py - forecast & time series plots

  • twiga/core/plot/exploration.py - scatter, line, correlation heatmap

  • twiga/core/plot/distribution.py - density, KDE, CDF, distribution

  • twiga/core/plot/stats.py - ACF, metrics bar, PIT histogram, reliability diagram

  • twiga/core/plot/residuals.py - residual diagnostic plots

  • twiga/core/plot/matplot_theme.py - Matplotlib theme & plot_prediction

  • twiga/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

line_plot(y, x=None, ...)

Simple line chart from arrays or lists

scatter_plot(df, x_col, y_col, ...)

Scatter chart with optional colour grouping

scatter_matrix(df, variables, targets, ...)

Pair-wise scatter matrix

correlation_heatmap(df, ...)

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

plot_density(df, x_col, ...)

Density / KDE fill plot

plot_kde(df, x_col, ...)

Kernel density estimate overlay

plot_cdf(df, x_col, ...)

Empirical CDF

plot_distribution(df, x_col, y_col, ...)

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

lets-plot>=4.9.0

Interactive plots, notebook display, PDF/PNG/SVG export

Matplotlib

matplotlib>=3.10.0

Publication figures, custom axes

Great Tables

great-tables>=0.21.0

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().