## 3 Materials and Methods
### Methods  
In our implementation, we first transformed the annotated dataset into a machine-readable form by encoding textual and categorical features. The free-text \"Description\" of each use case was vectorised using the TF-IDF approach (scikit-learn's TfidfVectorizer, limited to the top 500 terms). At the same time, the five-category \"Domain\" field was converted into dummy variables via one-hot encoding. Together, these steps produced a feature matrix X suitable for input to standard classifiers. The target vector y, representing the seven research-method classes, was generated with scikit-learn's LabelEncoder, mapping each method to a unique integer label [27].  
To address class imbalance, we activated the class_weight = \"balanced\" parameter in both random forest and support vector machine classifiers, thereby adjusting the loss function to impose a greater penalty on misclassification of underrepresented labels. This weighting scheme promotes equitable performance across all seven methodology classes.  
To assess model generalisability, we used stratified five-fold cross-validation (StratifiedKFold (n_splits = 5, shuffle = True, random_state = 42)) to preserve class proportions in each fold. Within this framework, we evaluated three shallow classifiers--support vector machine (sklearn.svm.SVC), random forest (sklearn.ensemble.RandomForestClassifier), and K-nearest neighbours (sklearn.neighbors.KNeighborsClassifier)--selected for their complementary strengths in handling tabular data. Hyperparameters were optimized via grid search (GridSearchCV with cv = 5) over carefully chosen parameter grids: for the SVM, regularization parameter C \(\in\) {0.1, 1, 10}, kernels \(\in\) {linear, rbf}, and \(\gamma\in\) {scale, auto}; for random forest, number of estimators \(\in\) {100, 200, 500}, maximum tree depth \(\in\) {None, 10, 20}, and minimum samples to split \(\in\) {2, 5}; and for KNN, neighbours \(\in\) {3, 5, 7}, weight schemes \(\in\) {uniform, distance}, and distance metrics \(\in\) {euclidean, manhattan}. Throughout, random seeds were fixed (random_state = 42) to ensure reproducibility [28].  
To further assess model generalisability beyond internal cross-validation, we partitioned 20% of the dataset as an independent test set, withheld during training and hyperparameter tuning. Evaluation on this held-out subset produced performance metrics--accuracy, F1-score\({}_{1}\), and ROC-AUC--that closely matched those obtained via stratified five-fold cross-validation. The concordance between internal and external evaluations substantiates the Random Forest model's robustness and mitigates optimism bias, providing a more realistic performance estimate on unseen project descriptions. Future work will extend validation to novel, user-defined case studies from diverse domains.  
Model performance was quantified using a suite of classification metrics--overall accuracy, macro-averaged precision, recall, F1-score (computed via precision_recall_fscore_support), and one-versus-rest ROC-AUC (using roc_auc_score). Confusion matrices (confusion_matrix) were also examined to identify systematic misclassification patterns across the seven method categories. We deliberately omitted regression-only metrics, such as MSE and MAE, focusing instead on metrics that reflect multiclass discriminative performance. All experiments were conducted in Python 3.10 with scikit-learn 1.3.0, pandas1.5.3, and NumPy 1.24.2. The complete codebase, including data-processing and modelling scripts, will be publicly available upon publication to facilitate full reproducibility.