Usage¶
This page describes how to use CONFETTI to generate counterfactual explanations for multivariate time series.
Overview¶
CONFETTI generates counterfactual explanations for multivariate time series using three main stages:
Nearest Unlike Neighbour (NUN) search The explainer identifies, within the reference dataset, the instance with a different predicted class label and a sufficiently high predicted probability (controlled by
theta). This neighbour guides the perturbation.Naive subsequence construction (optional) If feature-importance weights (e.g. CAM) are provided, CONFETTI extracts the most influential temporal region of the NUN and uses it to create a first counterfactual candidate via subsequence replacement.
Multi-objective optimization Using NSGA-III, CONFETTI optimizes the perturbation to balance: - increasing confidence in the target class - minimizing the number of perturbed time steps - minimizing proximity to the original instance
The result is a set of counterfactual candidates per input instance,
along with the best-scoring one according to the chosen objective
weight alpha.
Basic Workflow¶
To generate counterfactual explanations, you need:
a path to a trained model (Keras
.kerasor joblib.joblib)one or more instances to explain shaped
(n_instances, time_steps, n_features)a reference dataset in the same format (mandatory for NUN search)
optional feature weights (e.g. CAM values)
from confetti import CONFETTI
from confetti.utils import load_multivariate_ts_from_csv
from confetti.attribution import cam # optional
import keras
# Load model
model_path = "path/to/model.keras"
model = keras.models.load_model(model_path)
# Load datasets
X_train, y_train = load_multivariate_ts_from_csv("train.csv")
X_test, y_test = load_multivariate_ts_from_csv("test.csv")
# Prepare instance to explain
instance = X_test[0:1]
# Optional: feature-importance weights for the reference data
reference_weights = cam(model, X_train)
# Initialize explainer
explainer = CONFETTI(model_path=model_path)
# Generate counterfactuals
results = explainer.generate_counterfactuals(
instances_to_explain=instance,
reference_data=X_train,
reference_weights=reference_weights, # or None
alpha=0.5,
theta=0.51,
)
Interpreting the Output¶
generate_counterfactuals returns a CounterfactualResults object.
This behaves like a list of CounterfactualSet objects, one for each
instance you provided.
A CounterfactualSet contains:
original_instanceoriginal_labelnearest_unlike_neighbourbest→ aCounterfactualobjectall_counterfactuals→ list of all candidates generatedfeature_importance→ 1D array of the NUN CAM weights (if provided)
Example:
cf_set = results[0]
original = cf_set.original_instance
nun = cf_set.nearest_unlike_neighbour
best_cf = cf_set.best.counterfactual
best_label = cf_set.best.label
all_cfs = cf_set.all_counterfactuals
weights = cf_set.feature_importance
Exporting Counterfactuals¶
You can export results to a pandas DataFrame or save them as CSV files.
df = results.to_dataframe()
results.to_csv("all_counterfactuals.csv")
Visualizing Explanations¶
CONFETTI includes simple visualization utilities for inspecting counterfactual explanations.
from confetti.visualizations import plot_counterfactual
plot_counterfactual(
original=cf_set.original_instance,
counterfactual=cf_set.best.counterfactual,
cam_weights=cf_set.feature_importance,
cam_mode="heatmap",
)
The resulting plot highlights:
the original time series
the modified subsequence used for the counterfactual
(optionally) the CAM heatmap used to guide perturbation
This helps interpret both what changed and why the model’s decision was flipped.
Further Reading¶
For a runnable end-to-end example, see the Example page.