kdiagram.plot.comparison.plot_horizon_metrics

kdiagram.plot.comparison.plot_horizon_metrics(df, qlow_cols, qup_cols, *, q50_cols=None, xtick_labels=None, normalize_radius=False, show_value_labels=True, cbar_label=None, r_label=None, cmap='coolwarm', acov='default', title=None, figsize=(8.0, 8.0), alpha=0.85, show_grid=True, grid_props=None, mask_angle=False, savefig=None, dpi=300, cbar=True, ax=None)[source]

Plot a polar bar chart comparing metrics across different horizons.

This function visualizes a primary metric (typically mean interval width) as the height of bars arranged in a circle. Each bar represents a distinct category or forecast horizon. A secondary metric (typically the mean Q50 value) can be encoded as the color of the bars, providing a multi-faceted comparison.

Parameters:
dfpd.DataFrame

Input DataFrame where each row represents a distinct horizon or category to be compared.

qlow_colslist of str

List of column names containing lower quantile samples (e.g., Q10) for each horizon.

qup_colslist of str

List of column names containing upper quantile samples (e.g., Q90). Must have the same length as qlow_cols.

q50_colslist of str, optional

List of column names for the median quantile (Q50). If provided, the mean of these values determines the bar color. If None, bar color is determined by the bar height (the mean interval width).

xtick_labelslist of str, optional

Custom labels for each bar on the angular axis. The length must match the number of rows in df. If None, no angular labels are shown.

normalize_radiusbool, default=False

If True, the radial values (bar heights) are min-max scaled to the range [0, 1].

show_value_labelsbool, default=True

If True, display the numeric value of the radial metric on top of each bar.

cbar_labelstr, optional

Custom label for the color bar. If None, a default label is generated.

r_labelstr, optional

Custom label for the radial axis.

cmapstr, default=’coolwarm’

Matplotlib colormap name for coloring the bars.

acov{‘default’, ‘half_circle’, ‘quarter_circle’,
‘eighth_circle’}, default=’default’

Specifies the angular coverage of the plot: 'default' (360°), 'half_circle' (180°), etc.

titlestr, optional

Title for the plot. If None, a default title is used.

figsizetuple of (float, float), default=(8, 8)

Figure size in inches.

alphafloat, default=0.85

Transparency level for the bars.

show_gridbool, default=True

Toggle gridlines via the package helper set_axis_grid.

grid_propsdict, optional

Keyword arguments passed to set_axis_grid for grid customization.

mask_anglebool, default=False

If True and xtick_labels is not provided, this will hide any default angular tick labels.

savefigstr, optional

If provided, save the figure to this path; otherwise the plot is shown interactively.

dpiint, default=300

Resolution for the saved figure.

cbarbool, default=True

If True, display a color bar.

Returns:
axmatplotlib.axes.Axes

The Matplotlib Axes object containing the polar bar plot.

Parameters:

Notes

The plot summarizes metrics for \(N\) horizons (rows) using data from \(M\) samples (columns). Let \(\mathbf{L}\), \(\mathbf{U}\), and \(\mathbf{Q50}\) be data matrices of shape \((N, M)\) extracted from the corresponding columns.

  1. Interval Width Calculation: For each horizon \(j\) and sample \(i\), the interval width is:

    (1)\[W_{j,i} = U_{j,i} - L_{j,i}\]
  2. Radial Value (Bar Height): The radial value \(r_j\) for horizon \(j\) is the mean interval width across all \(M\) samples.

    (2)\[r_j = \frac{1}{M} \sum_{i=0}^{M-1} W_{j,i}\]
  3. Color Value: The color value \(c_j\) for horizon \(j\) is determined by the mean of the q50_cols values.

    (3)\[c_j = \frac{1}{M} \sum_{i=0}^{M-1} Q50_{j,i}\]

    If q50_cols is not provided, the color defaults to the radial value, \(c_j = r_j\).

  4. Angular Position: Horizons are spaced evenly around the circle. For horizon \(j\), the angle is:

    (4)\[\theta_j = \frac{j}{N} \times S\]

    where \(S\) is the angular span from acov. The plot starts at the top (12 o’clock) and proceeds clockwise.

Examples

>>> import numpy as np
>>> import pandas as pd
>>> from kdiagram.plot import plot_horizon_metrics
>>>
>>> # Create synthetic data for 6 horizons with 2 samples each
>>> horizons = ["H+1", "H+2", "H+3", "H+4", "H+5", "H+6"]
>>> df = pd.DataFrame({
...     'q10_s1': [1, 2, 3, 4, 5, 6],
...     'q10_s2': [1.2, 2.3, 3.4, 4.5, 5.6, 6.7],
...     'q90_s1': [3, 4, 5.5, 7, 8, 9.5],
...     'q90_s2': [3.1, 4.2, 5.7, 7.3, 8.4, 9.9],
...     'q50_s1': [2, 3, 4.2, 5.7, 6.5, 8.2],
...     'q50_s2': [2.1, 3.2, 4.4, 5.9, 6.9, 8.8],
... })
>>>
>>> q10_cols = ['q10_s1', 'q10_s2']
>>> q90_cols = ['q90_s1', 'q90_s2']
>>> q50_cols = ['q50_s1', 'q50_s2']
>>>
>>> ax = plot_horizon_metrics(
...     df=df,
...     qlow_cols=q10_cols,
...     qup_cols=q90_cols,
...     q50_cols=q50_cols,
...     title="Mean Interval Width Across Horizons",
...     xtick_labels=horizons,
...     show_value_labels=True,
...     r_label="Mean Interval Width (Q90-Q10)",
...     cbar_label="Mean Q50 Value",
...     acov="default"
... )