Skip to content

API Reference

This page is auto-generated from Python docstrings.

ml_vizkit

ML VizKit public API.

Reusable visualizations for inspecting, comparing, and explaining trained machine-learning models.

compare_models

compare_models(
    scores: Mapping[str, float],
    *,
    ax: Axes | None = None,
    metric_name: str = 'Score',
    title: str = 'Model Comparison',
) -> Axes

Compare evaluation scores from already-completed model experiments.

WHY: A consistent visual basis helps analysts compare models evaluated with the same metric and experimental conditions.

NOTE: This function assumes the caller has already established that the supplied scores are meaningfully comparable.

Source code in src/ml_vizkit/comparison.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def compare_models(
    scores: Mapping[str, float],
    *,
    ax: Axes | None = None,
    metric_name: str = "Score",
    title: str = "Model Comparison",
) -> Axes:
    """Compare evaluation scores from already-completed model experiments.

    WHY: A consistent visual basis helps analysts compare models evaluated with
    the same metric and experimental conditions.

    NOTE: This function assumes the caller has already established that the
    supplied scores are meaningfully comparable.
    """
    if not scores:
        msg = "scores must contain at least one model result."
        raise ValueError(msg)

    if ax is None:
        _, ax = plt.subplots()

    names = list(scores)
    values = [scores[name] for name in names]

    ax.bar(names, values)
    ax.set_ylabel(metric_name)
    ax.set_title(title)
    ax.tick_params(axis="x", rotation=30)
    return ax

compare_splits

compare_splits(
    splits: Sequence[SplitView],
) -> tuple[Axes, ...]

Create one train/test visualization per completed split.

WHY: Comparing several partitions makes sampling variability visible.

This function does not create splits or train models. Each visualization is created in its own figure, and all Axes are returned to the caller.

Source code in src/ml_vizkit/comparison.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def compare_splits(
    splits: Sequence[SplitView],
) -> tuple[Axes, ...]:
    """Create one train/test visualization per completed split.

    WHY: Comparing several partitions makes sampling variability visible.

    This function does not create splits or train models. Each visualization is
    created in its own figure, and all Axes are returned to the caller.
    """
    axes: list[Axes] = []

    for split in splits:
        _, ax = plt.subplots()

        title = split.label
        if split.score is not None:
            title = f"{title} | score={split.score:.3f}"

        show_train_test_split(
            split.X_train,
            split.X_test,
            ax=ax,
            title=title,
        )
        axes.append(ax)

    return tuple(axes)

show_actual_vs_predicted

show_actual_vs_predicted(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = 'Actual vs. Predicted',
) -> Axes

Show actual versus predicted regression values.

WHY: Good predictions should generally fall near the identity line.

The implementation delegates to scikit-learn's PredictionErrorDisplay.

Source code in src/ml_vizkit/regression.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def show_actual_vs_predicted(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = "Actual vs. Predicted",
) -> Axes:
    """Show actual versus predicted regression values.

    WHY: Good predictions should generally fall near the identity line.

    The implementation delegates to scikit-learn's PredictionErrorDisplay.
    """
    plot_ax = _new_axes(ax)

    PredictionErrorDisplay.from_predictions(
        y_true,
        y_pred,
        kind="actual_vs_predicted",
        ax=plot_ax,
    )
    plot_ax.set_title(title)
    return plot_ax

show_class_distribution

show_class_distribution(
    y: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = 'Class Distribution',
    x_label: str = 'Class',
) -> Axes

Show the number of observations in each target class.

WHY: Class imbalance can affect training, evaluation, and interpretation.

Source code in src/ml_vizkit/classification.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
def show_class_distribution(
    y: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = "Class Distribution",
    x_label: str = "Class",
) -> Axes:
    """Show the number of observations in each target class.

    WHY: Class imbalance can affect training, evaluation, and interpretation.
    """
    counts = pd.Series(y).value_counts(dropna=False).sort_index()
    plot_ax = _new_axes(ax)

    plot_ax.bar(counts.index.astype(str), counts.to_numpy())
    plot_ax.set_xlabel(x_label)
    plot_ax.set_ylabel("Count")
    plot_ax.set_title(title)
    return plot_ax

show_confusion_matrix

show_confusion_matrix(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    labels: Sequence[Any] | None = None,
    normalize: str | None = None,
    ax: Axes | None = None,
    title: str = 'Confusion Matrix',
) -> Axes

Show a confusion matrix from completed classification predictions.

WHY: Overall accuracy can hide which classes are being confused.

This function delegates the matrix visualization to scikit-learn and returns the Matplotlib Axes.

Source code in src/ml_vizkit/classification.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def show_confusion_matrix(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    labels: Sequence[Any] | None = None,
    normalize: str | None = None,
    ax: Axes | None = None,
    title: str = "Confusion Matrix",
) -> Axes:
    """Show a confusion matrix from completed classification predictions.

    WHY: Overall accuracy can hide which classes are being confused.

    This function delegates the matrix visualization to scikit-learn and
    returns the Matplotlib Axes.
    """
    plot_ax = _new_axes(ax)

    ConfusionMatrixDisplay.from_predictions(
        y_true,
        y_pred,
        labels=labels,
        normalize=normalize,
        ax=plot_ax,
    )
    plot_ax.set_title(title)
    return plot_ax

show_decision_boundary

show_decision_boundary(
    model: Any,
    X: DataFrame,
    y: Sequence[Any] | None = None,
    *,
    ax: Axes | None = None,
    response_method: str = 'auto',
    plot_method: str = 'contourf',
    title: str = 'Decision Boundary',
    alpha: float = 0.25,
) -> Axes

Show the decision boundary for an already-trained classifier.

WHY: A decision boundary makes the classifier's learned separation of feature space visible.

REQ: X must contain exactly two numeric features. REQ: model must already be fitted.

The implementation delegates boundary construction to scikit-learn's DecisionBoundaryDisplay and returns the Matplotlib Axes to the caller.

Source code in src/ml_vizkit/classification.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def show_decision_boundary(
    model: Any,
    X: pd.DataFrame,
    y: Sequence[Any] | None = None,
    *,
    ax: Axes | None = None,
    response_method: str = "auto",
    plot_method: str = "contourf",
    title: str = "Decision Boundary",
    alpha: float = 0.25,
) -> Axes:
    """Show the decision boundary for an already-trained classifier.

    WHY: A decision boundary makes the classifier's learned separation of
    feature space visible.

    REQ: X must contain exactly two numeric features.
    REQ: model must already be fitted.

    The implementation delegates boundary construction to scikit-learn's
    DecisionBoundaryDisplay and returns the Matplotlib Axes to the caller.
    """
    if X.shape[1] != 2:
        msg = "Decision-boundary plots require exactly two features."
        raise ValueError(msg)

    plot_ax = _new_axes(ax)

    DecisionBoundaryDisplay.from_estimator(
        model,
        X,
        response_method=response_method,
        plot_method=plot_method,
        alpha=alpha,
        ax=plot_ax,
    )

    # Overlay observed samples when labels are supplied. The boundary itself is
    # produced by scikit-learn; this layer makes the result easier to inspect.
    if y is not None:
        values = np.asarray(y)
        classes, codes = np.unique(values, return_inverse=True)
        plot_ax.scatter(
            X.iloc[:, 0],
            X.iloc[:, 1],
            c=codes,
            edgecolors="black",
            alpha=0.85,
        )

        # A compact legend makes class identity available without dictating
        # styling beyond the default Matplotlib color cycle.
        handles = []
        for code, label in enumerate(classes):
            handles.append(
                plt.Line2D(
                    [],
                    [],
                    marker="o",
                    linestyle="",
                    label=str(label),
                    markerfacecolor=f"C{code % 10}",
                    markeredgecolor="black",
                )
            )
        plot_ax.legend(handles=handles, title="Class")

    plot_ax.set_title(title)
    return plot_ax

show_feature_importance

show_feature_importance(
    model: Any,
    feature_names: Sequence[str],
    *,
    ax: Axes | None = None,
    title: str = 'Feature Importance',
) -> Axes

Show model-provided feature importance values.

WHY: Importance values can help analysts investigate which features most influenced a fitted model.

REQ: The model must already expose feature_importances_.

Source code in src/ml_vizkit/inspection.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def show_feature_importance(
    model: Any,
    feature_names: Sequence[str],
    *,
    ax: Axes | None = None,
    title: str = "Feature Importance",
) -> Axes:
    """Show model-provided feature importance values.

    WHY: Importance values can help analysts investigate which features most
    influenced a fitted model.

    REQ: The model must already expose ``feature_importances_``.
    """
    if not hasattr(model, "feature_importances_"):
        msg = f"{type(model).__name__} does not expose feature_importances_."
        raise ValueError(msg)

    importance = np.asarray(model.feature_importances_, dtype=float)

    if len(importance) != len(feature_names):
        msg = "feature_names must match the model's feature count."
        raise ValueError(msg)

    order = np.argsort(importance)
    ordered_names = np.asarray(feature_names, dtype=str)[order]
    ordered_values = importance[order]

    if ax is None:
        _, ax = plt.subplots()

    ax.barh(ordered_names, ordered_values)
    ax.set_xlabel("Importance")
    ax.set_title(title)
    return ax

show_prediction_errors

show_prediction_errors(
    X: DataFrame,
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = 'Classification Prediction Errors',
) -> Axes

Show correct and incorrect classifications in two-feature space.

WHY: Looking directly at mistakes can reveal overlap, unusual observations, and regions where a classifier struggles.

REQ: X must contain exactly two numeric features.

Source code in src/ml_vizkit/classification.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def show_prediction_errors(
    X: pd.DataFrame,
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = "Classification Prediction Errors",
) -> Axes:
    """Show correct and incorrect classifications in two-feature space.

    WHY: Looking directly at mistakes can reveal overlap, unusual observations,
    and regions where a classifier struggles.

    REQ: X must contain exactly two numeric features.
    """
    if X.shape[1] != 2:
        msg = "Prediction-error plots require exactly two features."
        raise ValueError(msg)

    actual = np.asarray(y_true)
    predicted = np.asarray(y_pred)

    if len(X) != len(actual) or len(actual) != len(predicted):
        msg = "X, y_true, and y_pred must contain the same number of observations."
        raise ValueError(msg)

    correct = actual == predicted
    plot_ax = _new_axes(ax)

    plot_ax.scatter(
        X.iloc[correct, 0],
        X.iloc[correct, 1],
        marker="o",
        label="Correct",
        alpha=0.65,
    )
    plot_ax.scatter(
        X.iloc[~correct, 0],
        X.iloc[~correct, 1],
        marker="x",
        label="Incorrect",
        alpha=0.95,
    )

    plot_ax.set_xlabel(str(X.columns[0]))
    plot_ax.set_ylabel(str(X.columns[1]))
    plot_ax.set_title(title)
    plot_ax.legend()
    return plot_ax

show_residuals

show_residuals(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = 'Residuals vs. Predicted',
) -> Axes

Show regression residuals against predicted values.

WHY: Residual plots can reveal systematic error patterns, changing variance, and unusual observations.

The implementation delegates to scikit-learn's PredictionErrorDisplay.

Source code in src/ml_vizkit/regression.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def show_residuals(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = "Residuals vs. Predicted",
) -> Axes:
    """Show regression residuals against predicted values.

    WHY: Residual plots can reveal systematic error patterns, changing
    variance, and unusual observations.

    The implementation delegates to scikit-learn's PredictionErrorDisplay.
    """
    plot_ax = _new_axes(ax)

    PredictionErrorDisplay.from_predictions(
        y_true,
        y_pred,
        kind="residual_vs_predicted",
        ax=plot_ax,
    )
    plot_ax.set_title(title)
    return plot_ax

show_train_test_split

show_train_test_split(
    X_train: DataFrame,
    X_test: DataFrame,
    *,
    ax: Axes | None = None,
    title: str = 'Train/Test Split',
) -> Axes

Show which observations were assigned to training and testing.

WHY: Seeing the actual partition makes random splitting concrete instead of treating the random seed as unexplained boilerplate.

REQ: Both frames must contain the same two numeric features.

Source code in src/ml_vizkit/splits.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def show_train_test_split(
    X_train: pd.DataFrame,
    X_test: pd.DataFrame,
    *,
    ax: Axes | None = None,
    title: str = "Train/Test Split",
) -> Axes:
    """Show which observations were assigned to training and testing.

    WHY: Seeing the actual partition makes random splitting concrete instead
    of treating the random seed as unexplained boilerplate.

    REQ: Both frames must contain the same two numeric features.
    """
    if X_train.shape[1] != 2 or X_test.shape[1] != 2:
        msg = "Train/test split plots require exactly two features."
        raise ValueError(msg)

    if list(X_train.columns) != list(X_test.columns):
        msg = "Training and test data must contain the same features in the same order."
        raise ValueError(msg)

    if ax is None:
        _, ax = plt.subplots()

    feature_x, feature_y = X_train.columns

    ax.scatter(
        X_train[feature_x],
        X_train[feature_y],
        marker="o",
        label="Train",
        alpha=0.65,
    )
    ax.scatter(
        X_test[feature_x],
        X_test[feature_y],
        marker="x",
        label="Test",
        alpha=0.95,
    )

    ax.set_xlabel(str(feature_x))
    ax.set_ylabel(str(feature_y))
    ax.set_title(title)
    ax.legend()
    return ax

classification

Classification visualizations.

These functions visualize trained classifiers and completed predictions. They do not fit models or choose analytical settings.

show_class_distribution

show_class_distribution(
    y: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = 'Class Distribution',
    x_label: str = 'Class',
) -> Axes

Show the number of observations in each target class.

WHY: Class imbalance can affect training, evaluation, and interpretation.

Source code in src/ml_vizkit/classification.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
def show_class_distribution(
    y: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = "Class Distribution",
    x_label: str = "Class",
) -> Axes:
    """Show the number of observations in each target class.

    WHY: Class imbalance can affect training, evaluation, and interpretation.
    """
    counts = pd.Series(y).value_counts(dropna=False).sort_index()
    plot_ax = _new_axes(ax)

    plot_ax.bar(counts.index.astype(str), counts.to_numpy())
    plot_ax.set_xlabel(x_label)
    plot_ax.set_ylabel("Count")
    plot_ax.set_title(title)
    return plot_ax

show_confusion_matrix

show_confusion_matrix(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    labels: Sequence[Any] | None = None,
    normalize: str | None = None,
    ax: Axes | None = None,
    title: str = 'Confusion Matrix',
) -> Axes

Show a confusion matrix from completed classification predictions.

WHY: Overall accuracy can hide which classes are being confused.

This function delegates the matrix visualization to scikit-learn and returns the Matplotlib Axes.

Source code in src/ml_vizkit/classification.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def show_confusion_matrix(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    labels: Sequence[Any] | None = None,
    normalize: str | None = None,
    ax: Axes | None = None,
    title: str = "Confusion Matrix",
) -> Axes:
    """Show a confusion matrix from completed classification predictions.

    WHY: Overall accuracy can hide which classes are being confused.

    This function delegates the matrix visualization to scikit-learn and
    returns the Matplotlib Axes.
    """
    plot_ax = _new_axes(ax)

    ConfusionMatrixDisplay.from_predictions(
        y_true,
        y_pred,
        labels=labels,
        normalize=normalize,
        ax=plot_ax,
    )
    plot_ax.set_title(title)
    return plot_ax

show_decision_boundary

show_decision_boundary(
    model: Any,
    X: DataFrame,
    y: Sequence[Any] | None = None,
    *,
    ax: Axes | None = None,
    response_method: str = 'auto',
    plot_method: str = 'contourf',
    title: str = 'Decision Boundary',
    alpha: float = 0.25,
) -> Axes

Show the decision boundary for an already-trained classifier.

WHY: A decision boundary makes the classifier's learned separation of feature space visible.

REQ: X must contain exactly two numeric features. REQ: model must already be fitted.

The implementation delegates boundary construction to scikit-learn's DecisionBoundaryDisplay and returns the Matplotlib Axes to the caller.

Source code in src/ml_vizkit/classification.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def show_decision_boundary(
    model: Any,
    X: pd.DataFrame,
    y: Sequence[Any] | None = None,
    *,
    ax: Axes | None = None,
    response_method: str = "auto",
    plot_method: str = "contourf",
    title: str = "Decision Boundary",
    alpha: float = 0.25,
) -> Axes:
    """Show the decision boundary for an already-trained classifier.

    WHY: A decision boundary makes the classifier's learned separation of
    feature space visible.

    REQ: X must contain exactly two numeric features.
    REQ: model must already be fitted.

    The implementation delegates boundary construction to scikit-learn's
    DecisionBoundaryDisplay and returns the Matplotlib Axes to the caller.
    """
    if X.shape[1] != 2:
        msg = "Decision-boundary plots require exactly two features."
        raise ValueError(msg)

    plot_ax = _new_axes(ax)

    DecisionBoundaryDisplay.from_estimator(
        model,
        X,
        response_method=response_method,
        plot_method=plot_method,
        alpha=alpha,
        ax=plot_ax,
    )

    # Overlay observed samples when labels are supplied. The boundary itself is
    # produced by scikit-learn; this layer makes the result easier to inspect.
    if y is not None:
        values = np.asarray(y)
        classes, codes = np.unique(values, return_inverse=True)
        plot_ax.scatter(
            X.iloc[:, 0],
            X.iloc[:, 1],
            c=codes,
            edgecolors="black",
            alpha=0.85,
        )

        # A compact legend makes class identity available without dictating
        # styling beyond the default Matplotlib color cycle.
        handles = []
        for code, label in enumerate(classes):
            handles.append(
                plt.Line2D(
                    [],
                    [],
                    marker="o",
                    linestyle="",
                    label=str(label),
                    markerfacecolor=f"C{code % 10}",
                    markeredgecolor="black",
                )
            )
        plot_ax.legend(handles=handles, title="Class")

    plot_ax.set_title(title)
    return plot_ax

show_prediction_errors

show_prediction_errors(
    X: DataFrame,
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = 'Classification Prediction Errors',
) -> Axes

Show correct and incorrect classifications in two-feature space.

WHY: Looking directly at mistakes can reveal overlap, unusual observations, and regions where a classifier struggles.

REQ: X must contain exactly two numeric features.

Source code in src/ml_vizkit/classification.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def show_prediction_errors(
    X: pd.DataFrame,
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = "Classification Prediction Errors",
) -> Axes:
    """Show correct and incorrect classifications in two-feature space.

    WHY: Looking directly at mistakes can reveal overlap, unusual observations,
    and regions where a classifier struggles.

    REQ: X must contain exactly two numeric features.
    """
    if X.shape[1] != 2:
        msg = "Prediction-error plots require exactly two features."
        raise ValueError(msg)

    actual = np.asarray(y_true)
    predicted = np.asarray(y_pred)

    if len(X) != len(actual) or len(actual) != len(predicted):
        msg = "X, y_true, and y_pred must contain the same number of observations."
        raise ValueError(msg)

    correct = actual == predicted
    plot_ax = _new_axes(ax)

    plot_ax.scatter(
        X.iloc[correct, 0],
        X.iloc[correct, 1],
        marker="o",
        label="Correct",
        alpha=0.65,
    )
    plot_ax.scatter(
        X.iloc[~correct, 0],
        X.iloc[~correct, 1],
        marker="x",
        label="Incorrect",
        alpha=0.95,
    )

    plot_ax.set_xlabel(str(X.columns[0]))
    plot_ax.set_ylabel(str(X.columns[1]))
    plot_ax.set_title(title)
    plot_ax.legend()
    return plot_ax

comparison

Higher-level visual comparisons of completed ML experiments.

SplitView dataclass

Data needed to visualize one already-created train/test split.

Source code in src/ml_vizkit/comparison.py
13
14
15
16
17
18
19
20
@dataclass(frozen=True, slots=True)
class SplitView:
    """Data needed to visualize one already-created train/test split."""

    label: str
    X_train: pd.DataFrame
    X_test: pd.DataFrame
    score: float | None = None

compare_models

compare_models(
    scores: Mapping[str, float],
    *,
    ax: Axes | None = None,
    metric_name: str = 'Score',
    title: str = 'Model Comparison',
) -> Axes

Compare evaluation scores from already-completed model experiments.

WHY: A consistent visual basis helps analysts compare models evaluated with the same metric and experimental conditions.

NOTE: This function assumes the caller has already established that the supplied scores are meaningfully comparable.

Source code in src/ml_vizkit/comparison.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def compare_models(
    scores: Mapping[str, float],
    *,
    ax: Axes | None = None,
    metric_name: str = "Score",
    title: str = "Model Comparison",
) -> Axes:
    """Compare evaluation scores from already-completed model experiments.

    WHY: A consistent visual basis helps analysts compare models evaluated with
    the same metric and experimental conditions.

    NOTE: This function assumes the caller has already established that the
    supplied scores are meaningfully comparable.
    """
    if not scores:
        msg = "scores must contain at least one model result."
        raise ValueError(msg)

    if ax is None:
        _, ax = plt.subplots()

    names = list(scores)
    values = [scores[name] for name in names]

    ax.bar(names, values)
    ax.set_ylabel(metric_name)
    ax.set_title(title)
    ax.tick_params(axis="x", rotation=30)
    return ax

compare_splits

compare_splits(
    splits: Sequence[SplitView],
) -> tuple[Axes, ...]

Create one train/test visualization per completed split.

WHY: Comparing several partitions makes sampling variability visible.

This function does not create splits or train models. Each visualization is created in its own figure, and all Axes are returned to the caller.

Source code in src/ml_vizkit/comparison.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def compare_splits(
    splits: Sequence[SplitView],
) -> tuple[Axes, ...]:
    """Create one train/test visualization per completed split.

    WHY: Comparing several partitions makes sampling variability visible.

    This function does not create splits or train models. Each visualization is
    created in its own figure, and all Axes are returned to the caller.
    """
    axes: list[Axes] = []

    for split in splits:
        _, ax = plt.subplots()

        title = split.label
        if split.score is not None:
            title = f"{title} | score={split.score:.3f}"

        show_train_test_split(
            split.X_train,
            split.X_test,
            ax=ax,
            title=title,
        )
        axes.append(ax)

    return tuple(axes)

inspection

Visual inspection of already-trained model characteristics.

show_feature_importance

show_feature_importance(
    model: Any,
    feature_names: Sequence[str],
    *,
    ax: Axes | None = None,
    title: str = 'Feature Importance',
) -> Axes

Show model-provided feature importance values.

WHY: Importance values can help analysts investigate which features most influenced a fitted model.

REQ: The model must already expose feature_importances_.

Source code in src/ml_vizkit/inspection.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def show_feature_importance(
    model: Any,
    feature_names: Sequence[str],
    *,
    ax: Axes | None = None,
    title: str = "Feature Importance",
) -> Axes:
    """Show model-provided feature importance values.

    WHY: Importance values can help analysts investigate which features most
    influenced a fitted model.

    REQ: The model must already expose ``feature_importances_``.
    """
    if not hasattr(model, "feature_importances_"):
        msg = f"{type(model).__name__} does not expose feature_importances_."
        raise ValueError(msg)

    importance = np.asarray(model.feature_importances_, dtype=float)

    if len(importance) != len(feature_names):
        msg = "feature_names must match the model's feature count."
        raise ValueError(msg)

    order = np.argsort(importance)
    ordered_names = np.asarray(feature_names, dtype=str)[order]
    ordered_values = importance[order]

    if ax is None:
        _, ax = plt.subplots()

    ax.barh(ordered_names, ordered_values)
    ax.set_xlabel("Importance")
    ax.set_title(title)
    return ax

regression

Regression visualizations for completed predictions.

show_actual_vs_predicted

show_actual_vs_predicted(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = 'Actual vs. Predicted',
) -> Axes

Show actual versus predicted regression values.

WHY: Good predictions should generally fall near the identity line.

The implementation delegates to scikit-learn's PredictionErrorDisplay.

Source code in src/ml_vizkit/regression.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def show_actual_vs_predicted(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = "Actual vs. Predicted",
) -> Axes:
    """Show actual versus predicted regression values.

    WHY: Good predictions should generally fall near the identity line.

    The implementation delegates to scikit-learn's PredictionErrorDisplay.
    """
    plot_ax = _new_axes(ax)

    PredictionErrorDisplay.from_predictions(
        y_true,
        y_pred,
        kind="actual_vs_predicted",
        ax=plot_ax,
    )
    plot_ax.set_title(title)
    return plot_ax

show_residuals

show_residuals(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = 'Residuals vs. Predicted',
) -> Axes

Show regression residuals against predicted values.

WHY: Residual plots can reveal systematic error patterns, changing variance, and unusual observations.

The implementation delegates to scikit-learn's PredictionErrorDisplay.

Source code in src/ml_vizkit/regression.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def show_residuals(
    y_true: Numeric1D,
    y_pred: Numeric1D,
    *,
    ax: Axes | None = None,
    title: str = "Residuals vs. Predicted",
) -> Axes:
    """Show regression residuals against predicted values.

    WHY: Residual plots can reveal systematic error patterns, changing
    variance, and unusual observations.

    The implementation delegates to scikit-learn's PredictionErrorDisplay.
    """
    plot_ax = _new_axes(ax)

    PredictionErrorDisplay.from_predictions(
        y_true,
        y_pred,
        kind="residual_vs_predicted",
        ax=plot_ax,
    )
    plot_ax.set_title(title)
    return plot_ax

splits

Visualizations for already-created train/test partitions.

show_train_test_split

show_train_test_split(
    X_train: DataFrame,
    X_test: DataFrame,
    *,
    ax: Axes | None = None,
    title: str = 'Train/Test Split',
) -> Axes

Show which observations were assigned to training and testing.

WHY: Seeing the actual partition makes random splitting concrete instead of treating the random seed as unexplained boilerplate.

REQ: Both frames must contain the same two numeric features.

Source code in src/ml_vizkit/splits.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def show_train_test_split(
    X_train: pd.DataFrame,
    X_test: pd.DataFrame,
    *,
    ax: Axes | None = None,
    title: str = "Train/Test Split",
) -> Axes:
    """Show which observations were assigned to training and testing.

    WHY: Seeing the actual partition makes random splitting concrete instead
    of treating the random seed as unexplained boilerplate.

    REQ: Both frames must contain the same two numeric features.
    """
    if X_train.shape[1] != 2 or X_test.shape[1] != 2:
        msg = "Train/test split plots require exactly two features."
        raise ValueError(msg)

    if list(X_train.columns) != list(X_test.columns):
        msg = "Training and test data must contain the same features in the same order."
        raise ValueError(msg)

    if ax is None:
        _, ax = plt.subplots()

    feature_x, feature_y = X_train.columns

    ax.scatter(
        X_train[feature_x],
        X_train[feature_y],
        marker="o",
        label="Train",
        alpha=0.65,
    )
    ax.scatter(
        X_test[feature_x],
        X_test[feature_y],
        marker="x",
        label="Test",
        alpha=0.95,
    )

    ax.set_xlabel(str(feature_x))
    ax.set_ylabel(str(feature_y))
    ax.set_title(title)
    ax.legend()
    return ax