Visualizing Forecasts with K-Diagram

fusionlab-learn focuses on generating robust time series forecasts. To gain deeper insights into these forecasts, especially regarding uncertainty and model behavior over time or across different segments, the k-diagram library offers a suite of specialized polar visualizations.

This guide demonstrates how to use k-diagram (accessed via fusionlab.kdiagram if installed as an extra) with the outputs generated by fusionlab-learn models. Predictions are typically formatted into a pandas DataFrame using utilities like format_predictions_to_dataframe().

Prerequisites:

Ensure fusionlab-learn is installed with the kdiagram extra, which will also install the k-diagram package and matplotlib:

pip install fusionlab-learn[k-diagram]

If you have fusionlab-learn already and only need k-diagram:

pip install k-diagram matplotlib

Preparing Forecast Data for K-Diagram

Most k-diagram plotting functions expect a pandas DataFrame. After training a fusionlab-learn model (e.g., XTFT or TemporalFusionTransformer) and generating predictions, you would typically use format_predictions_to_dataframe() to structure your forecasts.

Let’s assume you have a forecast_df from this utility. For point forecasts, it might have columns like sample_idx, forecast_step, my_target_actual, my_target_pred. For quantile forecasts, it might have my_target_q10, my_target_q50, my_target_q90, in addition to actuals.

The following examples will simulate such DataFrames and then use fusionlab.kdiagram for visualization.

Example 1: Actual vs. Predicted Plot

The plot_actual_vs_predicted() function provides a polar scatter plot comparing actual values against model predictions. This can help identify systematic biases or how prediction errors are distributed.

Scenario: Imagine you have point forecasts from a fusionlab-learn model.

 1import pandas as pd
 2import numpy as np
 3import matplotlib.pyplot as plt
 4import os
 5
 6# Import kdiagram via fusionlab's proxy
 7import fusionlab.kdiagram as fkd
 8# If kdiagram was installed separately, you could also do:
 9# import kdiagram as kd
10
11# Ensure output directory for images exists relative to where script is run
12# For Sphinx, paths in .. figure:: are relative to the .rst file
13output_image_dir = "docs/source/images" # Adjust if script runs from project root
14os.makedirs(output_image_dir, exist_ok=True)
15
16# --- Simulate a forecast_df from fusionlab ---
17np.random.seed(42)
18num_forecast_points = 150
19actual_values = 50 + 10 * np.sin(
20    np.linspace(0, 5 * np.pi, num_forecast_points)
21    ) + np.random.normal(0, 5, num_forecast_points)
22predicted_values = actual_values * 0.9 + \
23                   np.random.normal(0, 4, num_forecast_points) + 5
24
25forecast_eval_df = pd.DataFrame({
26    'sales_actual': actual_values,
27    'sales_pred': predicted_values,
28    'time_index': np.arange(num_forecast_points)
29})
30print("Sample of DataFrame for Actual vs. Predicted plot:")
31print(forecast_eval_df.head())
32
33# --- Plotting with fusionlab.kdiagram ---
34# Note: kdiagram functions are accessed via fusionlab.kdiagram
35# if the proxy is set up correctly and k-diagram is installed.
36try:
37    kd.plot.plot_actual_vs_predicted(
38        df=forecast_eval_df,
39        actual_col='sales_actual',
40        pred_col='sales_pred',
41        title='FusionLab & K-Diagram: Actual vs. Predicted Sales',
42        line=False,  # Use dots for this example
43        r_label="Sales Value",
44        actual_props={
45            'marker': '.', 's': 50, 'alpha': 0.7,
46            'color': '#007acc'     # Blue
47        },
48        pred_props={
49            'marker': '+', 's': 60, 'alpha': 0.8,
50            'color': '#e85e00'     # Valid orange hex
51        },
52        savefig=os.path.join(
53            output_image_dir,
54            "fusionlab_kdiag_actual_vs_pred.png"
55        )
56    )
57    #plt.close()
58    print(
59        f"Plot saved to "
60        f"{output_image_dir}/fusionlab_kdiag_actual_vs_pred.png"
61    )
62except ImportError as e:
63    print(f"K-Diagram not available through fusionlab.kdiagram: {e}")
64except Exception as e:
65    print(f"An error occurred during plotting: {e}")

Expected Output & Plot 1:

Sample of DataFrame for Actual vs. Predicted plot:
   sales_actual  sales_pred  time_index
0     52.483571   53.237185           0
1     50.360953   51.710650           1
2     55.331307   52.078077           2
3     60.725365   60.581843           3
4     52.922266   53.802329           4
...
Plot saved to docs/source/images/fusionlab_kdiag_actual_vs_pred.png
Actual vs. Predicted Plot using K-Diagram with FusionLab Output

Polar plot comparing actual sales to FusionLab model predictions, visualized using k-diagram via fusionlab.kdiagram.

Example 2: Coverage Diagnostic Plot

For quantile forecasts, plot_coverage_diagnostic() visualizes whether the actual values fall within the predicted uncertainty intervals.

Scenario: You have quantile forecasts (e.g., q10, q50, q90) from a fusionlab-learn model.

 1import pandas as pd
 2import numpy as np
 3import matplotlib.pyplot as plt
 4import os
 5import fusionlab.kdiagram as fkd
 6
 7# output_image_dir defined in previous cell
 8
 9# --- Simulate a forecast_df with quantiles ---
10np.random.seed(88)
11num_forecast_points = 200
12actual_values_q = np.random.normal(loc=30, scale=5, size=num_forecast_points)
13q10_values = actual_values_q - np.random.uniform(2, 6, num_forecast_points)
14q50_values = actual_values_q + np.random.normal(0, 1, num_forecast_points)
15q90_values = actual_values_q + np.random.uniform(2, 6, num_forecast_points)
16actual_values_q[::20] = q90_values[::20] + 2
17actual_values_q[::25] = q10_values[::25] - 2
18
19forecast_quant_df = pd.DataFrame({
20    'demand_actual': actual_values_q,
21    'demand_q10': q10_values,
22    'demand_q50': q50_values,
23    'demand_q90': q90_values
24})
25print("\nSample of DataFrame for Coverage Diagnostic plot:")
26print(forecast_quant_df.head())
27
28# --- Plotting with fusionlab.kdiagram ---
29try:
30    fkd.plot.plot_coverage_diagnostic(
31        df=forecast_quant_df,
32        actual_col='demand_actual',
33        q_cols=['demand_q10', 'demand_q90'],
34        title='FusionLab & K-Diagram: Interval Coverage (Q10-Q90)',
35        as_bars=True, # Use bars for this example
36        fill_gradient=True,
37        coverage_line_color='purple', # Changed color
38        bar_props={'alpha': 0.7},
39        verbose=0,
40        savefig=os.path.join(output_image_dir, "fusionlab_kdiag_coverage.png")
41    )
42    plt.close()
43    print(f"Plot saved to {output_image_dir}/fusionlab_kdiag_coverage.png")
44except ImportError as e:
45    print(f"K-Diagram not available through fusionlab.kdiagram: {e}")
46except Exception as e:
47    print(f"An error occurred during plotting: {e}")

Expected Output & Plot 2:

Sample of DataFrame for Coverage Diagnostic plot:
   demand_actual  demand_q10  demand_q50  demand_q90
0      33.003098   29.332539   32.480096   35.169899
1      26.928811   23.406621   27.742191   28.759837
...
Plot saved to docs/source/images/fusionlab_kdiag_coverage.png
Coverage Diagnostic Plot using K-Diagram

Point-wise coverage diagnostic for FusionLab quantile forecasts, visualized using k-diagram via fusionlab.kdiagram.

Example 3: Model Drift Plot

plot_model_drift() helps visualize if prediction interval characteristics change across different forecast horizons or segments.

Scenario: Data is in a wide format where each horizon’s quantiles are columns.

 1import pandas as pd
 2import numpy as np
 3import matplotlib.pyplot as plt
 4import os
 5import fusionlab.kdiagram as fkd
 6
 7# output_image_dir defined previously
 8
 9np.random.seed(0)
10horizons_labels = ['H_Step1', 'H_Step2', 'H_Step3', 'H_Step4']
11num_samples_drift = 50
12df_drift = pd.DataFrame()
13q10_cols_drift, q90_cols_drift = [], []
14
15for i, hor_label in enumerate(horizons_labels):
16    q10_colname = f'pred_{hor_label}_q10'
17    q90_colname = f'pred_{hor_label}_q90'
18    q10_cols_drift.append(q10_colname)
19    q90_cols_drift.append(q90_colname)
20    q10_vals = np.random.rand(num_samples_drift) * 5 + i * 0.7
21    q90_vals = q10_vals + np.random.rand(num_samples_drift) * 2.5 + 1.5 + i * 1.0
22    df_drift[q10_colname] = q10_vals
23    df_drift[q90_colname] = q90_vals
24
25print("\nSample of DataFrame for Model Drift plot:")
26print(df_drift.head(2))
27
28try:
29    fkd.plot.plot_model_drift(
30        df=df_drift,
31        q10_cols=q10_cols_drift,
32        q90_cols=q90_cols_drift,
33        horizons=horizons_labels,
34        acov='semi_circle', # Changed angular coverage
35        title='FusionLab & K-Diagram: Interval Drift',
36        cmap='plasma', # Changed colormap
37        savefig=os.path.join(output_image_dir, "fusionlab_kdiag_model_drift.png")
38    )
39    plt.close()
40    print(f"Plot saved to {output_image_dir}/fusionlab_kdiag_model_drift.png")
41except ImportError as e:
42    print(f"K-Diagram not available through fusionlab.kdiagram: {e}")
43except Exception as e:
44    print(f"An error occurred during plotting: {e}")

Expected Output & Plot 3:

Sample of DataFrame for Model Drift plot:
   pred_H_Step1_q10  pred_H_Step1_q90  pred_H_Step2_q10  pred_H_Step2_q90
0          2.744068          6.988482          3.907212          9.010952
1          3.575947          6.010909          4.975008          8.061101
Plot saved to docs/source/images/fusionlab_kdiag_model_drift.png
Model Drift Plot using K-Diagram

Visualization of prediction interval drift across forecast horizons, using k-diagram via fusionlab.kdiagram.

Example 4: Prediction Velocity Plot

plot_velocity() visualizes the rate and direction of change in predictions across consecutive horizons.

Scenario: Data is wide, with median forecasts for different horizons as columns.

 1import pandas as pd
 2import numpy as np
 3import matplotlib.pyplot as plt
 4import os
 5import fusionlab.kdiagram as fkd
 6
 7# output_image_dir defined previously
 8
 9np.random.seed(123)
10num_points_vel = 100
11horizon_labels_vel = ['Forecast_H1', 'Forecast_H2', 'Forecast_H3', 'Forecast_H4']
12df_velocity = pd.DataFrame({'sample_id': range(num_points_vel)})
13base_value_vel = np.random.rand(num_points_vel) * 20
14trend_vel = np.linspace(0, 3, num_points_vel)
15
16for i, hor_label in enumerate(horizon_labels_vel):
17    noise_vel = np.random.randn(num_points_vel) * 0.8
18    df_velocity[hor_label] = base_value_vel + trend_vel * (i + 1) + noise_vel
19print("\nSample of DataFrame for Velocity plot:")
20print(df_velocity.head(2))
21
22try:
23    fkd.plot.plot_velocity(
24        df=df_velocity,
25        q50_cols=horizon_labels_vel, # List of median forecast columns
26        title='FusionLab & K-Diagram: Prediction Velocity',
27        use_abs_color=True, # Color by magnitude
28        normalize=True,
29        cmap='magma', # Changed colormap
30        cbar=True,
31        s=35, # Marker size
32        savefig=os.path.join(output_image_dir, "fusionlab_kdiag_velocity.png")
33    )
34    plt.close()
35    print(f"Plot saved to {output_image_dir}/fusionlab_kdiag_velocity.png")
36except ImportError as e:
37    print(f"K-Diagram not available through fusionlab.kdiagram: {e}")
38except Exception as e:
39    print(f"An error occurred during plotting: {e}")

Expected Output & Plot 4:

Sample of DataFrame for Velocity plot:
   sample_id  Forecast_H1  Forecast_H2  Forecast_H3  Forecast_H4
0          0     0.592269     1.467803     2.614264     2.931038
1          1     8.900599     9.887309     9.900808     9.969413
Plot saved to docs/source/images/fusionlab_kdiag_velocity.png
Prediction Velocity Plot using K-Diagram

Visualization of prediction velocity using k-diagram via fusionlab.kdiagram.

Example 5: Taylor Diagram for Model Comparison

plot_taylor_diagram_in() summarizes model performance against reference values.

Scenario: Comparing point predictions from two fusionlab-learn models.

 1import numpy as np
 2import matplotlib.pyplot as plt
 3import os
 4# Import directly from fusionlab.kdiagram.plot.evaluation
 5from fusionlab.kdiagram.plot import evaluation as fl_kde
 6
 7# output_image_dir defined previously
 8
 9np.random.seed(42)
10reference_actuals = np.random.normal(10, 2, 100)
11preds_model_A = reference_actuals * 0.85 + np.random.normal(0, 0.8, 100)
12preds_model_B = reference_actuals * 0.6 + np.random.normal(0, 1.5, 100) + 1
13model_names = ['FusionLab_Model_A', 'FusionLab_Model_B']
14
15try:
16    fl_kde.plot_taylor_diagram_in(
17        preds_model_A, preds_model_B,
18        reference=reference_actuals,
19        names=model_names,
20        acov='half_circle', # Changed angular coverage
21        zero_location='N',
22        direction=1,
23        fig_size=(7.5, 7.5), # Slightly different size
24        cmap='RdYlBu', # Different colormap
25        radial_strategy='rmse', # Color by RMSE
26        title='FusionLab & K-Diagram: Taylor Diagram Comparison',
27        savefig=os.path.join(output_image_dir, "fusionlab_kdiag_taylor.png")
28    )
29    plt.close()
30    print(f"\nPlot saved to {output_image_dir}/fusionlab_kdiag_taylor.png")
31except ImportError as e:
32    print(f"K-Diagram (or its submodule) not available through fusionlab.kdiagram: {e}")
33except Exception as e:
34    print(f"An error occurred during plotting: {e}")

Expected Output & Plot 5:

Plot saved to docs/source/images/fusionlab_kdiag_taylor.png
Taylor Diagram using K-Diagram for FusionLab Models

Taylor diagram comparing FusionLab models, visualized using k-diagram via fusionlab.kdiagram.

This guide provides a starting point for using k-diagram with fusionlab-learn. Explore the k-diagram documentation for more customization options and advanced features to further enhance your forecast evaluation and communication.