Using sklearn to cross-validate bayesian network classifier
![]() | ![]() |
Overview
Section titled “Overview”BNClassifier from pyagrum.skbn is a Bayesian Network-based classifier compatible with the scikit-learn API. This means you can use it with all standard sklearn tools such as train_test_split, cross_validate, accuracy_score, confusion_matrix, and more.
This notebook demonstrates four typical use cases on the Iris dataset (3 classes, 4 continuous features):
fit(X, y)— Standard sklearn training from a feature matrix and a label vectorfitFromData(df, targetName)— Training directly from a pandas DataFrame or a CSV filefromTrainedModel(bn, targetAttribute)— Wrapping an existing pyAgrum Bayesian network into a sklearn-compatible classifier- Cross-validation — Using sklearn’s
cross_validateto get a robust performance estimate
from pyagrum.skbn import createBNClassifier
from sklearn import datasetsfrom sklearn.model_selection import train_test_split, cross_validatefrom sklearn.metrics import ( accuracy_score, confusion_matrix, classification_report, ConfusionMatrixDisplay,)
import pandas as pdimport matplotlib.pyplot as plt1. Loading the Iris Dataset
Section titled “1. Loading the Iris Dataset”The Iris dataset is one of the most well-known classification benchmarks. It contains 150 samples described by 4 continuous measurements:
- sepal length and width (cm)
- petal length and width (cm)
The target variable has 3 classes (setosa, versicolor, virginica).
Since the features are continuous, BNClassifier will automatically discretize them into bins before learning the structure of the Bayesian network.
## Load the Iris datasetiris = datasets.load_iris()X = iris.data # shape (150, 4) — continuous featuresy = iris.target # 0: setosa, 1: versicolor, 2: virginica
print(f"Samples : {X.shape[0]}")print(f"Features : {X.shape[1]} → {iris.feature_names}")print(f"Classes : {len(iris.target_names)} → {iris.target_names.tolist()}")Samples : 150Features : 4 → ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']Classes : 3 → ['setosa', 'versicolor', 'virginica']model = createBNClassifier( learningMethod="MIIC", prior="Smoothing", priorWeight=1, discretizationNbBins=3, discretizationStrategy="kmeans", discretizationThreshold=10,)2. Training and Evaluating the Classifier
Section titled “2. Training and Evaluating the Classifier”We define a BNClassifier using the MIIC structure learning algorithm and k-means discretization into 3 bins. A Smoothing prior is added to avoid zero probabilities in the conditional probability tables.
The dataset is split into a training set (80%) and a test set (20%). The classifier is then trained with fit(X_train, y_train) — the standard sklearn interface — and evaluated using accuracy_score, classification_report, and a confusion matrix.
## Split into training (80%) and test (20%) setsX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
## Train the classifiermodel.fit(X_train, y_train)
## Predict on the test sety_pred = model.predict(X_test)
## Overall accuracyprint(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}\n")
## Per-class precision, recall and F1-scoreprint("Classification Report:")print(classification_report(y_test, y_pred, target_names=iris.target_names))Accuracy: 1.0000
Classification Report: precision recall f1-score support
setosa 1.00 1.00 1.00 10 versicolor 1.00 1.00 1.00 9 virginica 1.00 1.00 1.00 11
accuracy 1.00 30 macro avg 1.00 1.00 1.00 30weighted avg 1.00 1.00 1.00 30## Confusion matrixfig, ax = plt.subplots(figsize=(5, 4))ConfusionMatrixDisplay.from_predictions(y_test, y_pred, display_labels=iris.target_names, ax=ax)ax.set_title("Confusion Matrix — BNClassifier on Iris")plt.tight_layout()plt.show()3. Loading Data with fitFromTabular
Section titled “3. Loading Data with fitFromTabular”The fitFromTabular(data, targetName) method offers a more convenient interface when data is already in tabular form. Instead of separating features and labels manually, you pass either:
- a pandas DataFrame with a named column for the target, or
- a CSV file path along with the name of the target column.
This avoids the manual X = df.drop(...) / y = df[...] split required by the standard sklearn fit(X, y).
## Build a DataFrame from the training set only (features + target column)df_train = pd.DataFrame(X_train, columns=iris.feature_names)df_train["species"] = y_train
## Train using fitFromTabular — target column is identified by namemodel2 = createBNClassifier( learningMethod="MIIC", prior="Smoothing", priorWeight=1, discretizationNbBins=3, discretizationStrategy="kmeans", discretizationThreshold=10,)model2.fitFromTabular(df_train, targetName="species")
## Evaluate on the test set (features only, passed as a DataFrame)X_test_df = pd.DataFrame(X_test, columns=iris.feature_names)y_pred2 = model2.predict(X_test_df)print(f"Accuracy with fitFromTabular: {accuracy_score(y_test, y_pred2):.4f}")Accuracy with fitFromTabular: 1.00004. Building a Classifier from a Pre-trained Bayesian Network
Section titled “4. Building a Classifier from a Pre-trained Bayesian Network”If you already have a trained Bayesian network — learned externally or provided by a domain expert — you can wrap it into a BNClassifier using fromTrainedModel.
The resulting object is fully sklearn-compatible: you can call predict, predict_proba, and use it in pipelines or cross-validation.
Here we reuse the Bayesian network learned in section 2 to create a new classifier without re-training.
## Retrieve the Bayesian network learned in section 2bn = model.bn_target = model.target_ # "y" — the name assigned to the target variable during fit
## Wrap the pre-trained network into a new BNClassifier## dtype=int ensures predictions are returned as integers, matching the original target typemodel3 = createBNClassifier()model3.fromTrainedModel(bn, targetAttribute=target, dtype=int)
## Predict on the same test sety_pred3 = model3.predict(X_test)print(f"Accuracy with fromTrainedModel: {accuracy_score(y_test, y_pred3):.4f}")Accuracy with fromTrainedModel: 0.70005. Cross-Validation
Section titled “5. Cross-Validation”One of the main advantages of the sklearn-compatible API is the ability to use cross_validate directly. This performs k-fold cross-validation and returns the score for each fold, giving a more robust performance estimate than a single train/test split.
We run the cross-validation with cv=30 and cv=50 folds on the full Iris dataset.
## 30-fold cross-validationcv_30 = cross_validate(model, X, y, cv=30)print(f"Fold scores (cv=30) : {cv_30['test_score']}")print(f"Mean accuracy (cv=30) : {cv_30['test_score'].mean():.4f}")Fold scores (cv=30) : [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0.8 1. 1. 1. 0.8 1. 1. 0.8 1. 1. 1. 0.8 1. 1. 1. 1. 1. 1. 1. 1. ]Mean accuracy (cv=30) : 0.9733## 50-fold cross-validationcv_50 = cross_validate(model, X, y, cv=50)print(f"Fold scores (cv=50) : {cv_50['test_score']}")print(f"Mean accuracy (cv=50) : {cv_50['test_score'].mean():.4f}")Fold scores (cv=50) : [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0.66666667 1. 1. 1. 1. 1. 1. 0.66666667 1. 1. 1. 1. 1. 0.66666667 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. ]Mean accuracy (cv=50) : 0.9800
