Predict Student Dropout with XGBoost

Build an explainable AI early warning system that predicts student dropout risk.

Introduction

30 Second Summary

Every year, students quietly disappear from university classrooms. By the time administrators notice, the window for intervention has already closed.

In this project, you will build an early warning system that predicts which students are at risk of dropping out, trained on 4,424 real student records. You will train a machine learning model, explain every prediction it makes, and translate the findings into policy recommendations for education governance.

What You'll Build

Picture running a single Python script and getting back a dropout risk score for every student, alongside visual explanations of exactly which factors drove each prediction.

By the end of this project, you'll have:

  • A working XGBoost model that takes any student's record and predicts whether they are likely to drop out, stay enrolled, or graduate.
  • SHAP plots that visually explain every prediction, letting you point to any flagged student and say exactly which factors drove the risk score.
  • A policy report file mapping the model's top risk factors to concrete governance recommendations, ready to share with education administrators.
  • Secret Mission: Tackle class imbalance with SMOTE to boost the model's ability to catch actual dropout cases.

Are there any prerequisites?

This project assumes basic familiarity with Python and pandas. You should have VS Code installed on your machine.

Before We Start

You are about to build a predictive model that identifies students at risk of dropping out, using the same techniques that Nigerian researchers are actively deploying in schools right now.

Set Up Your Environment and Download the Dataset

You have committed to building a dropout early warning system. Before you can explore the data or train any models, you need the right libraries and the actual student records on your machine.

In this step, you will create a Python environment, install the machine learning libraries, download a real student dataset from the UCI Machine Learning Repository, and write the first lines of code to load it.

In this step, get ready to:
  • Set up a virtual environment with all required ML libraries.
  • Download the UCI student dropout dataset.
  • Load the dataset and confirm it has 4,424 rows and 37 columns.
Create your project folder and virtual environment

A virtual environment keeps your project's libraries isolated from the rest of your system. This prevents version conflicts between projects.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • Open the built-in terminal by pressing Ctrl+` (backtick key, usually above Tab).
  • Move to your Desktop and create the project folder by running these commands:
cd ~/Desktop
mkdir dropout-predictor
cd dropout-predictor
  • Confirm you are inside the new folder by running:
pwd

You should see a path ending in dropout-predictor.

  • Check your Python version by running:
python3 --version

✔️ I see version 3.12 or higher

You are good to go. Python 3.12+ is required because the ML libraries in this project depend on it.

ⓧ I see an older version

The key libraries in this project (XGBoost and SHAP) require Python 3.12 or higher. You need to install a newer version before continuing.

  • Download and install Python 3.12+ from python.org/downloads.
  • On macOS, the system Python may still point to the old version after install. Run python3.12 --version to confirm the new version is available. Use python3.12 in place of python3 for the remaining commands in this project.
  • On Windows, the installer updates your python command automatically. Re-run python --version to confirm.

ⓧ Command not found

Python is not installed or not on your system PATH.

  • Download and install Python 3.12+ from python.org/downloads.
  • On Windows, check Add Python to PATH during installation.
  • After installing, close and reopen your terminal, then run python3 --version again.
  • Create a virtual environment by running:
python3 -m venv venv
  • Activate the virtual environment by running:

macOS

source venv/bin/activate

Windows

venv\Scripts\activate

Your terminal prompt now shows (venv) at the beginning. This confirms the virtual environment is active.

Not seeing (venv) in your prompt?

On Windows, you may need to allow script execution first. Run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned in PowerShell, then try the activate command again.

On macOS, make sure you typed source before the path. Without it, the command will not activate the environment.

Help me activate my virtual environment.

  • Install all the required packages by running:
pip install pandas scikit-learn xgboost shap matplotlib seaborn

What are all these libraries?

  • pandas loads and manipulates tabular data.
  • scikit-learn provides tools for splitting data and evaluating model performance.
  • xgboost is the gradient boosting library you will use to train the dropout prediction model.
  • shap generates explanations that show which features drive each prediction.
  • matplotlib and seaborn create the charts and plots you will use to visualise results.

The install may take a minute or two. You will see progress bars as each package downloads.

  • Verify all libraries loaded successfully by running:
python -c "import xgboost; import sklearn; import shap; print('All libraries loaded')"

You should see All libraries loaded printed with no errors.

Seeing an import error?

Make sure your virtual environment is active. You should see (venv) in your terminal prompt. If not, re-run the activate command above and try the install again.

If a specific package failed to install, check that your Python version is 3.12 or higher. Older Python versions are not compatible with the latest versions of xgboost and shap.

Help me fix my library installation errors.

Download the UCI dataset

The dataset you will use is called "Predict Students' Dropout and Academic Success" from the UCI Machine Learning Repository. It contains 4,424 real student records with 36 features covering demographics, academic performance, and socioeconomic background.

  • Open this link in your browser: UCI Dataset - Predict Students' Dropout and Academic Success.
  • Scroll down to the Dataset Files section. You will see data.csv listed at 520.7 KB.
  • Click Download to save the file to your computer.
  • Move the downloaded data.csv file into your dropout-predictor folder on your Desktop.
  • Confirm the file is in the right place by running:
ls

You should see data.csv and venv listed in the output.

Why this dataset?

This dataset from a Portuguese higher education institution tracks students from enrollment through to their outcome: dropout, still enrolled, or graduated. The 36 features include everything from parental education levels to first-semester grades.

It is one of the only publicly available, research-grade student outcome datasets. Nigerian institutional datasets are not open access, so this dataset serves as a strong proxy for building techniques you can later apply to local data.

Don't see data.csv?

Check your browser's Downloads folder. The file might still be there instead of in dropout-predictor. Drag or copy it into the dropout-predictor folder on your Desktop.

Make sure you are inside the dropout-predictor folder when you run ls. Run pwd to check your current location.

Help me find my downloaded dataset.

Load the dataset and verify it

Now that the data file is in place, you will write a Python script that loads it and confirms the expected dimensions.

  • Create a new file called dropout_model.py in VS Code by pressing Cmd+N (macOS) or Ctrl+N (Windows).
  • Save it immediately with Cmd+S (macOS) or Ctrl+S (Windows). Name it dropout_model.py and save it inside your dropout-predictor folder.

The script needs all the libraries you will use throughout the project. You will import everything now so the file is ready for later steps.

  • Add the following imports to dropout_model.py:
import pandas as pd
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from xgboost import XGBClassifier
import shap

What does this code do?

  • The first four lines set up matplotlib with the Agg backend. This saves plots as image files instead of trying to open interactive windows, which avoids errors when running from the terminal.
  • seaborn builds on matplotlib to create better-looking statistical charts with less code.
  • train_test_split and classification_report from scikit-learn handle data splitting and model evaluation. You will use them in Step 3.
  • XGBClassifier is the gradient boosting model you will train. shap will explain its predictions. Both come into play in Steps 3 and 4.
  • Below the imports, add the code that loads the dataset and prints its shape:
# --- Step 1: Load the dataset ---
df = pd.read_csv("data.csv", delimiter=";")
print(f"Dataset shape: {df.shape}")
print(df.head())

What does this code do?

pd.read_csv loads the CSV file into a pandas DataFrame. The delimiter=";" parameter is critical because this dataset uses semicolons to separate values, not commas.

df.shape prints the number of rows and columns. df.head() shows the first five rows so you can visually inspect the data.

  • Save dropout_model.py.

Before you run the script, what do you expect to see for the dataset shape?

  • Run the script by executing:
python dropout_model.py

You should see output showing the dataset shape as (4424, 37) followed by the first five rows of student data. That is 4,424 students and 37 columns (36 features plus 1 target column).

Seeing a FileNotFoundError?

This means Python cannot find data.csv in the same folder as your script. Make sure both dropout_model.py and data.csv are directly inside the dropout-predictor folder, not in a subfolder.

Run ls to check. You should see both files listed side by side.

Help me fix my FileNotFoundError.

✔️ Awesome, I've got everything!

Great. Double check you have saved dropout_model.py.

ⓧ I'd like to double check the full code

Here is the complete dropout_model.py at the end of this step. Compare it with yours:

import pandas as pd
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from xgboost import XGBClassifier
import shap


# --- Step 1: Load the dataset ---
df = pd.read_csv("data.csv", delimiter=";")
print(f"Dataset shape: {df.shape}")
print(df.head())

Your environment is ready and the data is loaded. Next up, you will explore this dataset to understand how many students actually dropped out and which features might explain why.

Explore the Data and Understand the Dropout Story

In Step 1, you installed your ML libraries, downloaded the UCI dataset, and confirmed that your script loads all 4,424 student records. The data is loaded and ready.

Before training any model, you need to understand the shape of the problem. How many students actually dropped out versus graduated? Is the data clean or full of gaps? This step is exploratory data analysis (EDA), and it prevents you from training a model blindly on data you don't understand.

In this step, get ready to:
  • Examine the class distribution and confirm the data has no missing values.
  • Visualize dropout patterns with a bar chart and a correlation heatmap.
Examine class distribution and data quality

The most important question right now is how many students fall into each outcome category. If one category dominates, your model might learn to predict only that category and ignore the rest.

  • In dropout_model.py, find the line that says print(df.head()) and add the following code below it:
# --- Step 2: Explore the data ---
# Count students in each outcome category
print("\nTarget distribution:")
print(df["Target"].value_counts())

# Check for gaps in the data
print(f"\nMissing values: {df.isnull().sum().sum()}")

What does this code do?

  • df["Target"].value_counts() counts how many students are labeled Dropout, Enrolled, or Graduate. This is the class distribution.
  • df.isnull().sum().sum() adds up every missing value across all 37 columns. A zero here means the data is complete with no gaps to fill.
  • Save dropout_model.py and run your script:
python dropout_model.py

You should see the dataset shape and first rows from Step 1, followed by a Target distribution table showing counts for Graduate, Dropout, and Enrolled. The missing values line should read Missing values: 0.

Seeing a KeyError or unexpected output?

  • If you see KeyError: 'Target', make sure the column name is capitalized exactly as Target with a capital T.
  • If the shape shows fewer than 37 columns, check that your pd.read_csv call uses delimiter=";". This dataset uses semicolons, not commas.

Still stuck? Help me debug my target distribution code.

Visualize the key patterns

Numbers in a table are useful, but a chart makes the class imbalance impossible to miss. You will create two visualizations: a bar chart of student outcomes and a correlation heatmap of the features.

  • Add the bar chart code at the bottom of dropout_model.py, below the missing values line:
# Create a bar chart of student outcomes
fig, ax = plt.subplots(figsize=(8, 5))
df["Target"].value_counts().plot(kind="bar", color=["#e74c3c", "#f39c12", "#2ecc71"], ax=ax)
ax.set_title("Student Outcome Distribution")
ax.set_xlabel("Outcome")
ax.set_ylabel("Count")
plt.tight_layout()
# Save the chart as a PNG file
plt.savefig("class_distribution.png")
plt.close()
print("Saved class_distribution.png")

What does this code do?

  • plt.subplots() creates a matplotlib figure and axis. The bar chart plots one bar per outcome category (Dropout, Enrolled, Graduate) with distinct colors.
  • plt.savefig("class_distribution.png") writes the chart to a PNG file instead of displaying it on screen. This works in any environment, including terminals without a graphical display.
  • plt.close() frees the figure from memory so it does not interfere with the next plot.
  • Save dropout_model.py and run your script:
python dropout_model.py
  • You should see Saved class_distribution.png at the end of the terminal output.
  • Open class_distribution.png from the dropout-predictor folder. You should see three colored bars for Graduate, Dropout, and Enrolled, with Graduate being the tallest bar.

Don't see the PNG file?

  • Make sure you saved dropout_model.py before running the script.
  • Check that your terminal is inside the dropout-predictor folder. The file saves to whichever directory the script runs from.
  • Confirm the matplotlib.use("Agg") line is near the top of your file, before any plotting code. This sets the non-interactive backend needed to save files.

Still stuck? Help me fix my matplotlib bar chart.

  • Add the correlation heatmap code at the bottom of dropout_model.py, below the bar chart code:
# Select the first 10 numeric columns for a readable heatmap
numeric_cols = df.select_dtypes(include=[np.number]).columns[:10]
fig, ax = plt.subplots(figsize=(10, 8))
# Draw a color-coded correlation grid with values annotated
sns.heatmap(df[numeric_cols].corr(), annot=True, fmt=".2f", cmap="coolwarm", ax=ax)
ax.set_title("Feature Correlation Heatmap (First 10 Features)")
plt.tight_layout()
plt.savefig("correlation_heatmap.png")
plt.close()
print("Saved correlation_heatmap.png")

What does this code do?

  • select_dtypes(include=[np.number]).columns[:10] picks the first 10 numeric columns. Plotting all 36 features at once would produce an unreadable chart.
  • sns.heatmap() from seaborn draws a color-coded grid of correlation values. Values close to 1 or -1 mean two features move together (or inversely). Values near 0 mean little relationship.
  • annot=True prints the actual correlation number inside each cell so you can read exact values.

Before you run your script, think about what you expect to see in the terminal. You have two new visualizations on top of the data quality checks from earlier. How many new lines of output should appear?

  • Save dropout_model.py and run your script:
python dropout_model.py

You should see all the previous output plus two new confirmation lines: Saved class_distribution.png and Saved correlation_heatmap.png.

  • Open correlation_heatmap.png from the dropout-predictor folder. You should see a grid of colored squares with numbers showing how strongly the first 10 features relate to each other.

Heatmap looks wrong or will not save?

  • If you see an error about np not being defined, check that import numpy as np is near the top of your file.
  • If the heatmap is blank or has no annotations, make sure annot=True and fmt=".2f" are both inside the sns.heatmap() call.

Still stuck? Help me debug my seaborn heatmap.

✔️ Awesome, I've got everything!

Great. Double check you have saved dropout_model.py and that both PNG files appear in your dropout-predictor folder.

ⓧ I'd like to double check the full code

Scroll up and confirm your dropout_model.py contains these sections in order:

  • The import block and Step 1 data-loading code from the previous step.
  • The # --- Step 2: Explore the data --- comment, followed by the target distribution and missing values prints.
  • The bar chart code that saves class_distribution.png.
  • The correlation heatmap code that saves correlation_heatmap.png.

You now have a clear picture of the data. Next up, you will encode the target, split the data, and train a baseline model to see how well it predicts student outcomes.

Prepare the Data and Train a Baseline Model

You've explored the dataset and seen how students are distributed across Dropout, Enrolled, and Graduate categories.

Now you need to turn that raw data into something a model can learn from. String labels like "Dropout" need to become numbers. The data needs to be split into a training set and a test set. And you need a baseline model so you can measure whether your predictions are actually good.

In this step, get ready to:
  • Encode the target column and split the data into training and test sets.
  • Train a baseline XGBoost classifier and evaluate its predictions with a classification report.
Encode the target and split the data

The Target column contains strings like "Dropout", "Enrolled", and "Graduate". Machine learning models work with numbers, so you need to convert those strings into integers first.

  • In dropout_model.py, scroll to the bottom of the file, below the correlation heatmap code.
  • Add the following code to encode the target column and split the data into training and test sets:
# --- Step 3: Prepare data and train baseline model ---
target_map = {"Dropout": 0, "Enrolled": 1, "Graduate": 2}
df["Target_encoded"] = df["Target"].map(target_map)

X = df.drop(columns=["Target", "Target_encoded"])
y = df["Target_encoded"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

What does this code do?

  • target_map converts the three string labels to integers: Dropout becomes 0, Enrolled becomes 1, Graduate becomes 2.
  • X holds all 36 feature columns (everything except the target). y holds the encoded target labels.
  • train_test_split reserves 80% of the data for training and 20% for testing. The stratify=y parameter keeps the same ratio of Dropout, Enrolled, and Graduate students in both sets.
  • Save dropout_model.py.
  • Run your script to confirm the encoding and split work without errors:
python dropout_model.py

You should see the same dataset shape and EDA output from the earlier steps, followed by a clean exit with no error messages.

Seeing a KeyError or mapping error?

  • Make sure the dictionary keys in target_map match the exact values in the Target column: "Dropout", "Enrolled", and "Graduate" (capital first letter).
  • Check that the new code is placed after the correlation heatmap section, not inside it.
  • Still stuck? Help me debug a KeyError in my target encoding code.
Train a baseline XGBoost classifier

XGBoost is a gradient boosting algorithm that builds many small decision trees, each one learning from the mistakes of the previous one. It consistently ranks as one of the top-performing algorithms for tabular data like your student records.

  • Add the following code below the train_test_split block to create, train, and evaluate the model:
model = XGBClassifier(
    objective="multi:softprob",
    num_class=3,
    eval_metric="mlogloss",
    random_state=42,
    n_estimators=100,
    max_depth=6,
    learning_rate=0.1,
    use_label_encoder=False,
)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)
target_names = ["Dropout", "Enrolled", "Graduate"]
report = classification_report(y_test, y_pred, target_names=target_names)
print("\n--- Baseline Classification Report ---")
print(report)

What does this code do?

  • XGBClassifier creates a gradient boosting model configured for 3-class classification. objective="multi:softprob" tells XGBoost to output a probability for each class.
  • model.fit(X_train, y_train) trains the model on the 80% training split.
  • classification_report prints precision, recall, and F1-score for each class. Precision measures how many of the model's "Dropout" predictions were correct. Recall measures how many actual dropout students the model caught.
  • Save dropout_model.py.

Before you run this, think about it: do you expect the model to perform equally well on Dropout, Enrolled, and Graduate students? Or will one class be harder to predict than the others?

  • Run your script to see the classification report:
python dropout_model.py

Scroll to the bottom of the output. You should see a classification report showing precision, recall, and F1-score for each class.

Now look at the recall column. The Dropout recall is noticeably lower than the Graduate recall. The model is missing actual dropout students. This is the class imbalance problem in action.

Why is Dropout recall lower?

Remember the bar chart from Step 2? There are fewer Dropout students in the dataset compared to Graduates. The model has fewer dropout examples to learn from, so it gets better at predicting the majority class (Graduate) and worse at catching the minority class (Dropout).

For an early warning system, this is exactly the wrong outcome. Missing a dropout student is far worse than a false alarm. You will tackle this problem head-on in the Secret Mission using a technique called SMOTE.

Not seeing the classification report?

  • Make sure you saved dropout_model.py before running it.
  • If you see an indentation error, check that the model = XGBClassifier(...) block is at the top level of the file, not indented inside another block.
  • If training seems to hang, give it a moment. XGBoost trains 100 decision trees, which can take 10-20 seconds depending on your machine.
  • Still stuck? Help me debug my XGBoost model training code.

✔️ Awesome, I've got everything!

Great. Double check you've saved dropout_model.py and that your classification report printed to the terminal.

ⓧ I'd like to double check the full code

Your dropout_model.py file is now longer than 30 lines, so scroll through and confirm each section is present, in order:

  • Imports at the top: pandas, numpy, matplotlib, seaborn, train_test_split, classification_report, XGBClassifier, shap.
  • Step 1 section: pd.read_csv("data.csv", delimiter=";") loads the dataset and prints its shape.
  • Step 2 section: target distribution, missing values check, bar chart saved to class_distribution.png, and heatmap saved to correlation_heatmap.png.
  • Step 3 section: target_map encoding, X and y defined, train_test_split with 80/20 stratified split, XGBClassifier trained and classification report printed.

Your baseline model is trained and you have a concrete measurement of how it performs. The class imbalance gap in that report is exactly the kind of insight that separates a good data scientist from one who just runs code. Next up, you will use SHAP to crack open this model and explain exactly why each prediction was made.

Explain Every Prediction with SHAP

Your XGBoost model can now predict whether a student will drop out, stay enrolled, or graduate. But if you tell a school administrator "this student will drop out" and cannot explain why, they cannot act on it.

In this step, you will use SHAP (SHapley Additive exPlanations) to decompose every prediction into the contribution of each feature. This turns your model from a black box into a transparent decision-support tool that policymakers can actually trust.

In this step, get ready to:
  • Create a SHAP explainer and generate a summary plot of the top dropout risk factors.
  • Drill into an individual high-risk student and visualize exactly which features drove their dropout prediction.
Generate SHAP values for the test set

SHAP works by calculating how much each feature contributed to a specific prediction. For tree-based models like XGBoost, SHAP's TreeExplainer computes exact Shapley values efficiently by leveraging the tree structure directly.

  • In dropout_model.py, scroll to the bottom of the file, below the line that prints the classification report.
  • Add the following code to create the SHAP explainer, compute SHAP values, and generate a summary plot:
# --- Step 4: Explain predictions with SHAP ---
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)

shap.summary_plot(shap_values[0], X_test, show=False)
plt.title("SHAP Summary - Dropout Risk Factors")
plt.tight_layout()
plt.savefig("shap_summary.png", dpi=150, bbox_inches="tight")
plt.close()
print("Saved shap_summary.png")

What does this code do?

shap.TreeExplainer(model) creates an explainer that knows how to trace predictions back through the decision trees inside your XGBoost model.

explainer.shap_values(X_test) computes SHAP values for every student in the test set. Because this is a 3-class problem, it returns a list of 3 arrays. Index 0 corresponds to the Dropout class, which is the one you care about most.

The summary plot (called a beeswarm) shows every feature ranked by importance. Each dot is one student. The color shows whether that student's feature value was high (red) or low (blue), and the horizontal position shows whether it pushed the prediction toward or away from dropout.

  • Save dropout_model.py.
  • Run your script by running this command in the terminal:
python dropout_model.py

You should see Saved shap_summary.png printed in the terminal after the classification report. Open shap_summary.png from the file explorer in VS Code to see the beeswarm plot.

Not seeing shap_summary.png?

  • Make sure you saved dropout_model.py before running the script.
  • Check that you pasted the new code below the existing print(report) line, not inside any existing code block.
  • If you see an import error for shap, confirm your virtual environment is activated and run pip install shap again.

Still stuck? Help me debug why my SHAP summary plot is not being generated.

Why SHAP over built-in feature importance?

XGBoost has its own feature_importances_ attribute, but it only tells you which features matter globally. SHAP tells you which features matter AND in which direction for every individual student. That per-student granularity is what makes SHAP useful for policy. A global score says "grades matter." SHAP says "this student's low second-semester grades pushed them 40% closer to dropout."

Identify the highest-risk student

The summary plot shows patterns across all students. Now you will zoom into one individual. By finding the student the model considers most likely to drop out, you can inspect exactly which features pushed that specific prediction.

  • In dropout_model.py, add the following code below the print("Saved shap_summary.png") line:
dropout_probs = model.predict_proba(X_test)[:, 0]
high_risk_idx = np.argmax(dropout_probs)
print(f"\nHighest dropout risk student (test index {high_risk_idx}):")
print(f"  Predicted probabilities: Dropout={dropout_probs[high_risk_idx]:.3f}")
print(f"  Actual outcome: {target_names[y_test.iloc[high_risk_idx]]}")

What does this code do?

model.predict_proba(X_test)[:, 0] gets the predicted dropout probability for every student in the test set. Column 0 is the Dropout class because you mapped Dropout to 0 in Step 3.

np.argmax(dropout_probs) finds the index of the student with the highest dropout probability. The print statements then reveal that student's predicted probability and their actual outcome, so you can see whether the model got it right.

  • Save dropout_model.py and run the script again:
python dropout_model.py

Near the bottom of the output, you should see the highest-risk student's test index, their predicted dropout probability, and their actual outcome.

Not seeing the student info?

  • Make sure you pasted the new code below the print("Saved shap_summary.png") line, not inside the summary plot block.
  • Check that you saved the file before re-running the script.

Need help? Help me debug why the highest-risk student info is not printing.

Generate a waterfall plot for the highest-risk student

A SHAP waterfall plot shows every feature's contribution to a single prediction, stacked from the base value (average model output) to the final prediction. This is the plot you would hand to a school administrator to explain why the model flagged a specific student.

  • Add this code below the student info print statements you just added:
shap.waterfall_plot(
    shap.Explanation(
        values=shap_values[0][high_risk_idx],
        base_values=explainer.expected_value[0],
        data=X_test.iloc[high_risk_idx],
        feature_names=X_test.columns.tolist(),
    ),
    show=False,
)
plt.title("SHAP Waterfall - Highest Risk Student")
plt.tight_layout()
plt.savefig("shap_waterfall.png", dpi=150, bbox_inches="tight")
plt.close()
print("Saved shap_waterfall.png")

What does this code do?

shap.Explanation bundles the SHAP values, base value, raw feature data, and feature names for a single student into one object that the waterfall plot can render.

shap_values[0][high_risk_idx] grabs the Dropout-class SHAP values (index 0) for the specific student you identified. explainer.expected_value[0] is the model's baseline prediction for the Dropout class before any features are considered.

The resulting waterfall chart reads from bottom to top. Each bar shows one feature pushing the prediction higher (toward dropout) or lower (away from dropout), building up to the final prediction value.

Before you run the script, think about this: based on the SHAP summary plot you already saw, which features do you expect to appear in this individual student's waterfall?

  • Save dropout_model.py and run the script one more time:
python dropout_model.py

You should see Saved shap_waterfall.png at the end of the output. Open shap_waterfall.png in VS Code to see which features drove the highest-risk student's prediction.

Seeing an error about Explanation or waterfall_plot?

  • Make sure the shap.Explanation() call includes all four keyword arguments: values, base_values, data, and feature_names.
  • Check that show=False is included in the waterfall_plot() call. Without it, the plot tries to display interactively, which fails with the Agg backend.

Still stuck? Help me debug my SHAP waterfall plot generation.

✔️ Awesome, I've got everything!

Great. Double check you have saved dropout_model.py and that both shap_summary.png and shap_waterfall.png appear in your dropout-predictor folder.

ⓧ I'd like to double check the full code

The full file is longer than 30 lines, so scroll through your dropout_model.py and confirm each section is present, in this order:

  • Imports at the top (pandas, numpy, matplotlib, seaborn, sklearn, xgboost, shap).
  • Step 1: Load the dataset (pd.read_csv with delimiter=";").
  • Step 2: Explore the data (target distribution, missing values, bar chart, heatmap).
  • Step 3: Prepare data and train baseline model (target encoding, train/test split, XGBClassifier, classification report).
  • Step 4: Explain predictions with SHAP (TreeExplainer, summary plot, highest-risk student lookup, waterfall plot).

Your model now explains itself. You can point at any student and say exactly which factors put them at risk. In the next step, you will extract these risk factors at scale and translate them into concrete policy recommendations that a Nigerian education policymaker could act on.

Translate Findings into Policy Recommendations

Your model can now predict dropout risk, and SHAP has revealed exactly which features drive those predictions. The hard part is done. But a prediction without action is just a number.

In this step, you will extract the top risk factors from your SHAP analysis, connect them to real education challenges in Nigeria, and write concrete policy recommendations that a decision-maker could act on. You will also save everything to a report file that is ready to share.

In this step, get ready to:
  • Rank the top 10 dropout risk factors using mean SHAP values.
  • Write policy recommendations that connect each risk factor to a Nigerian education governance action.
  • Save a complete policy report combining your model results, risk factors, and recommendations.
Extract and rank the top risk factors

The SHAP values you computed in the previous step contain per-feature importance for every prediction. To turn that into a ranked list of risk factors, you need to average the absolute SHAP values across all test students for the Dropout class.

  • In dropout_model.py, scroll to the bottom of the file and find this line:
print("Saved shap_waterfall.png")
  • Add the following code below that line to compute and print the top 10 dropout risk factors:


# --- Step 5: Extract top risk factors and write policy recommendations ---
mean_shap = np.abs(shap_values[0]).mean(axis=0)
feature_importance = pd.DataFrame({
    "Feature": X_test.columns,
    "Mean_SHAP": mean_shap,
}).sort_values("Mean_SHAP", ascending=False)

print("\n--- Top 10 Dropout Risk Factors ---")
for i, row in feature_importance.head(10).iterrows():
    print(f"  {row['Feature']}: {row['Mean_SHAP']:.4f}")

What does this code do?

  • np.abs(shap_values[0]).mean(axis=0) takes the absolute SHAP values for the Dropout class (index 0) and averages them across all test students. This gives you one importance score per feature.
  • feature_importance is a DataFrame with two columns: the feature name and its mean SHAP value, sorted so the most influential feature is first.
  • The loop prints the top 10 features by importance. These are the factors that push students most strongly toward the dropout prediction.
  • Save dropout_model.py.
  • Run the script in your terminal by running:
python dropout_model.py

After the classification report and SHAP outputs you have seen before, you should now see a new section titled --- Top 10 Dropout Risk Factors --- listing features like Curricular units 2nd sem (approved) with their SHAP scores.

Not seeing the top 10 list?

  • Make sure you added the new code after the last print statement from Step 4, not inside it.
  • Check that the variable names shap_values and X_test match exactly what you used in Step 4.
  • Why aren't my top 10 dropout risk factors printing?
Write policy recommendations for each top risk factor

A ranked feature list is useful for data scientists, but a policymaker needs specific actions. Each top risk factor maps to a concrete intervention that Nigerian education governance could implement.

  • Add the following code below the feature importance loop to define and print policy recommendations:
recommendations = {
    "Curricular units 2nd sem (approved)": "Target students failing multiple second-semester courses with tutoring and academic mentorship programs.",
    "Curricular units 2nd sem (grade)": "Implement mid-semester grade monitoring to flag students whose grades drop below thresholds for early intervention.",
    "Curricular units 1st sem (approved)": "Design first-year bridge programs for students who struggle in their first semester, before the gap widens.",
    "Tuition fees up to date": "Expand scholarship and conditional cash transfer programs targeting students with outstanding fees.",
    "Age at enrollment": "Create peer mentorship and orientation programs specifically for mature-age and non-traditional students.",
}

print("\n--- Policy Recommendations for Nigerian Education Governance ---")
for factor, recommendation in recommendations.items():
    print(f"\n  Risk Factor: {factor}")
    print(f"  Recommendation: {recommendation}")

What does this code do?

  • recommendations is a dictionary mapping each top risk factor to a one-sentence policy action. These factors consistently appear at the top of SHAP rankings for this dataset.
  • Each recommendation connects a model finding to a specific governance action. For example, if Tuition fees up to date is a top driver of dropout, the recommendation targets scholarship and financial aid programs.
  • Save dropout_model.py and run the script again:
python dropout_model.py

You should now see a section titled --- Policy Recommendations for Nigerian Education Governance --- with five risk factors, each followed by a concrete recommendation.

Recommendations not printing?

  • Check that the recommendations dictionary is outside the previous for loop, not indented inside it.
  • Make sure there is no syntax error in the dictionary. Each key-value pair ends with a comma.
  • Help me debug why my policy recommendations are not printing.
Save everything to a policy report file

Terminal output disappears when you close the window. A proper deliverable needs to live in a file that you can share, attach to a presentation, or hand to a stakeholder.

  • Add the following code below the recommendations print loop to write the full report to a text file:
with open("policy_report.txt", "w") as f:
    f.write("STUDENT DROPOUT EARLY WARNING SYSTEM - POLICY REPORT\n")
    f.write("=" * 55 + "\n\n")
    f.write("CLASSIFICATION REPORT (Baseline)\n")
    f.write(report + "\n")
    f.write("TOP 10 DROPOUT RISK FACTORS (by mean SHAP value)\n")
    for i, row in feature_importance.head(10).iterrows():
        f.write(f"  {row['Feature']}: {row['Mean_SHAP']:.4f}\n")
    f.write("\nPOLICY RECOMMENDATIONS\n")
    for factor, rec in recommendations.items():
        f.write(f"\n  Risk Factor: {factor}\n")
        f.write(f"  Recommendation: {rec}\n")

print("\nSaved policy_report.txt")
print("\nDone! Your dropout early warning system is complete.")

What does this code do?

  • The with open("policy_report.txt", "w") block creates a new text file and writes three sections into it: the baseline classification report, the top 10 risk factors with their SHAP scores, and the five policy recommendations.
  • The report variable was created back in Step 3 when you ran classification_report(). It is a formatted string that writes directly into the file.
  • The final two print statements confirm the file was saved and signal that the entire pipeline is complete.
  • Save dropout_model.py.

Before you run the script one final time, what do you expect to see as the very last line of output?

  • Run the script to generate your complete policy report:
python dropout_model.py

At the very end of the output, you should see Saved policy_report.txt followed by Done! Your dropout early warning system is complete.

  • Confirm the report file was created by running:
ls

You should see policy_report.txt listed alongside dropout_model.py, data.csv, and your saved PNG files.

What is inside policy_report.txt?

Open policy_report.txt in VS Code to see the finished report. It contains three sections: the baseline classification report with precision, recall, and F1-score for each class. The top 10 dropout risk factors ranked by mean SHAP value. And five policy recommendations, each tying a model finding to a concrete governance action.

Don't see policy_report.txt?

  • Make sure the with open block is not indented inside the previous for loop. It should be at the top level of the script.
  • Check that you are running ls from inside the dropout-predictor folder. The file is saved in the same directory where your script runs.
  • Help me figure out why policy_report.txt was not created.

✔️ Awesome, I've got everything!

Double check you have saved dropout_model.py and that your terminal shows Done! Your dropout early warning system is complete.

ⓧ I'd like to double check the full code

Your dropout_model.py is too long to show in one block. Scroll through the file and confirm it has these sections in order:

  • Step 1: Load the dataset block that reads data.csv and prints the shape.
  • Step 2: Explore the data block that prints target distribution, missing values, and saves class_distribution.png and correlation_heatmap.png.
  • Step 3: Prepare data and train baseline model block that encodes the target, splits the data, trains the XGBClassifier, and prints the classification report.
  • Step 4: Explain predictions with SHAP block that creates the TreeExplainer, saves shap_summary.png and shap_waterfall.png.
  • Step 5: Extract top risk factors and write policy recommendations block that computes mean_shap, builds feature_importance, prints the top 10 features, defines recommendations, prints them, and writes policy_report.txt.

The very last line of the file should be print("\nDone! Your dropout early warning system is complete.").

Secret mission

Tackle Class Imbalance with SMOTE

Your baseline model has a blind spot. It predicts graduates well but misses too many dropout students. In a real early warning system, missing a dropout case is far worse than a false alarm. Can you fix the class imbalance problem with SMOTE to build a fairer, more reliable model?

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs or cloud resources to worry about. Decide whether to keep your project files on your machine, pause your environment to free up terminal resources, or delete everything entirely.

Resources you used:

  • The dropout-predictor project folder on your Desktop, containing your Python scripts (dropout_model.py, dropout_model_smote.py), the UCI dataset (data.csv), generated plots, and policy_report.txt.
  • The venv virtual environment inside the project folder, with all installed packages (pandas, scikit-learn, XGBoost, SHAP, matplotlib, seaborn, imbalanced-learn).

Keep everything running

No action needed. Choose this if you want to keep experimenting with the model, try different hyperparameters, or extend the project further.

  • Your project folder, virtual environment, and all generated files stay on your Desktop exactly as they are.
  • To resume work later, open the dropout-predictor folder in VS Code, activate the virtual environment, and pick up where you left off.

Pause - I'll come back to this later

Deactivate the virtual environment to free up your terminal session. Your files stay on disk, ready to resume whenever you come back.

  • Deactivate the virtual environment by running this command in your terminal:
deactivate
  • Close VS Code when you are done.
  • To resume later, open the dropout-predictor folder in VS Code, open a terminal, and reactivate the virtual environment.

Delete - I don't want to use this again

Remove all project files, the virtual environment, and every generated output. This frees up disk space and leaves no trace of the project.

  • Deactivate the virtual environment first by running:
deactivate
  • Delete the entire project folder. On macOS or Linux, run:
cd ~/Desktop
rm -rf dropout-predictor
  • On Windows, open Command Prompt and run:
cd ~\Desktop
rmdir /s /q dropout-predictor
  • Confirm the folder is gone by running ls (macOS/Linux) or dir (Windows) on your Desktop. You should no longer see dropout-predictor listed.

Nice Work!

Nice Work!

Nice work! You just built a complete early warning system that predicts student dropout risk, explains every prediction it makes, and translates the findings into policy recommendations for education governance.

Here's what you learned:

  • Trained an XGBoost gradient boosting classifier on 4,424 real student records from the UCI Machine Learning Repository to predict dropout, enrollment, or graduation.
  • Used SHAP to explain exactly which features drive dropout risk, turning a black-box model into a transparent decision-support tool with visual summary and waterfall plots.
  • Translated model findings into actionable policy recommendations framed for Nigerian education governance, saved as a reproducible policy_report.txt ready for a report or presentation.
  • Secret Mission: Applied SMOTE to tackle class imbalance, improving the model's ability to catch actual dropout cases.

Ready to quiz yourself?