kdiagram.plot.relationship.plot_relationship

kdiagram.plot.relationship.plot_relationship(y_true, *y_preds, names=None, title=None, theta_offset=0.0, theta_scale='proportional', acov='default', figsize=None, cmap='tab10', s=50, alpha=0.7, legend=True, show_grid=True, grid_props=None, color_palette=None, xlabel=None, ylabel=None, z_values=None, z_label=None, savefig=None, ax=None)[source]

Visualize the relationship between true values and one or more prediction series on a polar (circular) scatter plot.

Each point uses an angular position derived from y_true and a radial position derived from the corresponding prediction. This compact view lets you compare multiple prediction series against the same truth—useful for spotting systematic deviations and patterns over a cyclic or ordered domain (e.g., phase, time-of-year).

Parameters:
y_truearray_like of shape (n_samples,)

Ground-truth (observed) values. Must be numeric, 1D, and the same length as every array in y_preds.

*y_predsarray_like(s)

One or more prediction arrays, each with shape (n_samples,) and aligned to y_true.

nameslist of str, optional

Labels for each prediction series. If fewer names than series are provided, placeholders like 'Model_3' are appended.

titlestr, optional

Figure title. If None, uses 'Relationship Visualization'.

theta_offsetfloat, default=0

Constant angular shift (radians) applied after the angle mapping.

theta_scale{‘proportional’, ‘uniform’}, default=’proportional’

Strategy for mapping y_true to angles:

  • 'proportional': angle proportional to the scaled value of y_true within its range over the selected angular span.

  • 'uniform': angles evenly spaced over the selected span, ignoring the numerical spacing in y_true.

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

default=’default’ Angular coverage (span) of the plot:

  • 'default': \(2\pi\) (full circle)

  • 'half_circle': \(\pi\)

  • 'quarter_circle': \(\tfrac{\pi}{2}\)

  • 'eighth_circle': \(\tfrac{\pi}{4}\)

figsizetuple of (float, float), optional

Figure size in inches. If None, a sensible default is used.

cmapstr, default=’tab10’

Matplotlib colormap name used to generate distinct series colors.

sfloat, default=50

Marker size for scatter points.

alphafloat, default=0.7

Alpha (transparency) for scatter points in [0, 1].

legendbool, default=True

If True, show a legend for the prediction series.

show_gridbool, default=True

Toggle polar grid lines (delegated to set_axis_grid).

grid_propsdict, optional

Keyword arguments forwarded to the grid helper (e.g., linestyle, alpha).

color_palettelist of color-like, optional

Explicit list of colors. If omitted, colors are derived from cmap. If provided with fewer colors than series, they repeat.

xlabelstr, optional

Label for the radial axis. Defaults to 'Normalized Predictions (r)'.

ylabelstr, optional

Label for the angular axis. Defaults to 'Angular Mapping (θ)' when z_values is not used.

z_valuesarray_like of shape (n_samples,), optional

Optional values used to label angular ticks (e.g., time, phase). If provided, a subset of positions is selected and tick labels are replaced by formatted entries from z_values.

z_labelstr, optional

Axis/legend label describing z_values (shown as text next to the angular tick labels region).

savefigstr, optional

Path to save the figure (with extension). If None, the figure is shown instead.

Returns:
axmatplotlib.axes.Axes

The polar axes containing the visualization.

Parameters:
  • acov (Literal['default', 'half_circle', 'quarter_circle', 'eighth_circle'])

  • ax (Axes | None)

See also

kdiagram.plot.uncertainty.plot_temporal_uncertainty

General polar series visualization (e.g., quantiles).

kdiagram.plot.uncertainty.plot_actual_vs_predicted

Side-by-side truth vs. point prediction comparison.

Notes

Angular span. Let \(\Delta\theta\) be the selected span: \(2\pi\) (full), \(\pi\), \(\pi/2\), or \(\pi/4\) depending on acov. Angles are then limited to \([0,\,\Delta\theta]\) and shifted by theta_offset.

Angle mapping. For \(N=\text{len}(y_{\text{true}})\) and \(i=0,\dots,N-1\):

  • Proportional mapping (range-aware):

    (1)\[\begin{split}\theta_i \;=\; \begin{cases} \dfrac{y_i - y_{\min}}{y_{\max}-y_{\min}}\,\Delta\theta, & \text{if } y_{\max}>y_{\min},\\[6pt] 0, & \text{otherwise,} \end{cases}\end{split}\]

    where \(y_{\min}=\min_i y_i\) and \(y_{\max}=\max_i y_i\).

  • Uniform mapping (index-based):

    (2)\[\theta_i \;=\; \frac{i}{N}\,\Delta\theta.\]

Radial normalization. Each prediction series \(p\) is scaled to \([0,1]\) by

(3)\[\begin{split}r_i \;=\; \begin{cases} \dfrac{p_i - p_{\min}}{p_{\max}-p_{\min}}, & p_{\max}>p_{\min},\\[6pt] 0.5, & \text{otherwise,} \end{cases}\end{split}\]

to give comparable radii across heterogeneous series [1].

Data preparation. The function first removes joint NaNs via drop_nan_in and validates each pair (y_true, y_pred) through validate_yy (continuous expectations, 1D arrays). Colors are drawn from cmap unless color_palette is supplied. Grid appearance is managed by set_axis_grid.

Interpretation. When theta_scale='proportional', nearby angles reflect similar truth values; with 'uniform', angles reflect order only. Clustering by color (series) indicates systematic agreement or disagreement versus truth across the domain [2].

References

Examples

Basic comparison over a full circle:

>>> import numpy as np
>>> from kdiagram.plot.relationship import plot_relationship
>>> rng = np.random.default_rng(0)
>>> y = rng.random(200)
>>> p1 = y + rng.normal(0, 0.10, size=len(y))
>>> p2 = y + rng.normal(0, 0.20, size=len(y))
>>> ax = plot_relationship(
...     y, p1, p2,
...     names=["Model A", "Model B"],
...     acov="default",
...     title="Truth–Prediction (Full Circle)"
... )

Half-circle with custom angular tick labels (e.g., months):

>>> months = np.linspace(1, 12, len(y))
>>> ax = plot_relationship(
...     y, p1,
...     names=["Model A"],
...     theta_scale="uniform",
...     acov="half_circle",
...     z_values=months,
...     z_label="Month",
...     xlabel="Normalized Predictions (r)"
... )