Predicting student mental stress levels (0–5) using behavioral data
A compact, reproducible scikit-learn pipeline and notebook that cleans and explores a student behavior dataset, engineers domain features, trains a balanced Random Forest, and exports a single pipeline artifact for inference.
This project demonstrates a practical machine-learning pipeline that predicts students' stress levels (ordinal target values 0–5) from a behavioral dataset. The goal was not only to maximize predictive performance but to produce a clean, reproducible artifact (a single scikit-learn Pipeline) that bundles preprocessing and model into one object ready for inference.
You can use this repo as a starting point for behavioral analytics, mental-health risk scoring prototypes, or pedagogy-focused feature engineering examples.
- End-to-end Jupyter Notebook showing the entire workflow.
- Domain-driven feature engineering (e.g., SME and sleep_to_screen_ratio).
ColumnTransformer+Pipelineto ensure identical preprocessing at train and inference time.- Class imbalance handled with
class_weight='balanced'during training andStratifiedKFoldCV. - Hyperparameter search using
RandomizedSearchCVwithf1_weightedscoring. - Model persistence (pickled pipeline) for one-step inference.
student-stress-prediction/
├─ data/
│ └─ student_distress_dataset.csv
├─ notebooks/
│ └─ student_stress_analysis.ipynb
├─ models/
│ └─ mental_stress_pipeline.pkl
├─ requirements.txt
├─ README.md
└─ .gitignore
Adjust names to match your repository if you move files around.
- Python 3.13
- Recommended: create a virtual environment (conda or venv)
Suggested packages (also included in requirements.txt):
pandas
numpy
scikit-learn
matplotlib
seaborn
joblib
imbalanced-learn
xgboost # optional
shap # optional, for explainability
# create and activate virtualenv (example with venv)
python -m venv .venv
source .venv/bin/activate # mac/linux
.\.venv\Scripts\activate # windows
# install deps
pip install -r requirements.txtPlace your dataset CSV in the data/ folder (default path: data/student_distress_dataset.csv). The notebook expects typical behavioral columns such as:
Student ID(dropped to protect privacy)Gender,AgeSocial Media Usage(hours/day)Number of Notifications(per day)Sleep Duration(hours/night)- ...and a target column:
Stress Level(0–5)
If your columns are named differently, update the notebook or the small wrapper scripts accordingly.
- Removed identifier columns to avoid leakage.
- Normalized or imputed
Prefer not to sayaspd.NAthen imputed with mode for categorical fields. - Checked for duplicates and missing values, plotted distributions and correlations.
Two domain features were engineered and used in training:
-
SME (Social Media Engagement)
- Combines time-on-platform and notification load:
SME = social_media_hours + (notifications / max_notifications) - Purpose: capture both duration and engagement pressure.
- Combines time-on-platform and notification load:
-
sleep_to_screen_ratio
sleep_to_screen_ratio = sleep_hours / (SME + 1)- Purpose: measure restorative sleep relative to screen engagement.
Using these condensed features helped reduce collinearity and boosted signal during training.
- Numeric features:
StandardScaler. - Categorical features:
OneHotEncoder(drop='first', handle_unknown='ignore'). - Bundled with
ColumnTransformerso preprocessing is deterministic and reproducible.
-
Base model:
RandomForestClassifier(random_state=42, class_weight='balanced'). -
Hyperparameter tuning:
RandomizedSearchCVwithStratifiedKFold(n_splits=5)andscoring='f1_weighted'. -
Why these choices?
class_weight='balanced'addresses class imbalance without synthetic sampling.StratifiedKFoldpreserves class distributions across folds.f1_weightedbalances precision & recall across all classes.
- Evaluation metrics: per-class precision/recall/f1 (via
classification_report), confusion matrix heatmap. - Persisted the entire pipeline to
models/mental_stress_pipeline.pklusingjoblib/picklefor easy inference.
Open the notebook and run cells in order (recommended):
# start jupyter lab / notebook
jupyter lab # or jupyter notebookOr run a script (if you add one):
python src/train.py --data data/student_distress_dataset.csv --out models/mental_stress_pipeline.pklThe provided notebook contains the exact code used to reproduce preprocessing, CV, and model persistence.
import pickle
import pandas as pd
# load
with open('models/mental_stress_pipeline.pkl', 'rb') as f:
pipe = pickle.load(f)
# example input -- ensure same engineered fields or raw fields depending on how pipeline is saved
sample = pd.DataFrame([
{
'Gender': 'Male',
'Age': 21,
'SME': 2.5,
'sleep_to_screen_ratio': 1.2,
# ...other fields required by pipeline
}
])
pred = pipe.predict(sample)
print('Predicted stress level:', pred[0])
# probability (if available)
print('Probabilities:', pipe.predict_proba(sample))- Use the same random seed used inside the notebook (e.g.,
random_state=42). - Keep the
Pipelineartifact; it contains preprocessing transforms, so you won’t accidentally preprocess differently at inference time. - If you change feature names, update the notebook and the saved pipeline accordingly.
- Add SHAP or permutation importance to explain model outputs.
- Try gradient-boosted models (XGBoost/LightGBM) and calibrated probabilities.
- Add a small REST API (Flask/FastAPI) that loads the pipeline and serves predictions.
- Add monitoring to detect data drift and trigger re-training.
Feel free to open issues or PRs. If you find bugs or want a feature (e.g., Dockerfile, REST API, or a CLI wrapper), open an issue and I’ll prioritize.
This repo is released under the MIT License — see LICENSE.
If you want the full notebook cleaned up into a runnable script, a Dockerfile, or a demo API, ping me here or open an issue.
Happy modeling 🙌 — and remember: data without good features is like coffee without caffeine: wasted potential.