From cf69c55ed26f5261332a26ed38a3b934701c6564 Mon Sep 17 00:00:00 2001 From: Joaquin Torres Bravo Date: Thu, 23 May 2024 11:52:48 +0200 Subject: [PATCH] Generated first ROC curve to see behavior --- model_selection/cv_metric_gen.py | 52 +- .../output_cv_metrics/curves/pre_ORIG.svg | 6739 +++++++++++++++++ .../output_cv_metrics/metrics.xlsx | Bin 0 -> 8030 bytes 3 files changed, 6764 insertions(+), 27 deletions(-) create mode 100644 model_selection/output_cv_metrics/curves/pre_ORIG.svg create mode 100644 model_selection/output_cv_metrics/metrics.xlsx diff --git a/model_selection/cv_metric_gen.py b/model_selection/cv_metric_gen.py index 833ff4b..ec6c6ba 100644 --- a/model_selection/cv_metric_gen.py +++ b/model_selection/cv_metric_gen.py @@ -175,8 +175,8 @@ if __name__ == "__main__": # Metric generation through cv for tuned models3 # -------------------------------------------------------------------------------------------------------- scores_sheets = {} # To store score dfs as sheets in the same excel file - for i, group in enumerate(['pre', 'post']): - for j, method in enumerate(['', '', 'over_', 'under_']): + for i, group in enumerate(['pre']): # 'post' + for j, method in enumerate(['']): # '', 'over_', 'under_' # print(f"{group}-{method_names[j]}") # Get train dataset based on group and method X_train = data_dic['X_train_' + method + group] @@ -191,28 +191,29 @@ if __name__ == "__main__": axes = [axes] # Metric generation for each model for model_idx, (model_name, model) in enumerate(models.items()): - print(f"{group}-{method_names[j]}-{model_name}") - # Retrieve cv scores for our metrics of interest - scores = cross_validate(model, X_train, y_train, scoring=scorings, cv=cv, return_train_score=True, n_jobs=10) - # Save results of each fold - for metric_name in scorings.keys(): - scores_df.loc[model_name + f'_{metric_name}']=list(np.around(np.array(scores[f"test_{metric_name}"]),4)) - # Generate ROC curves - mean_fpr = np.linspace(0, 1, 100) - tprs, aucs = [], [] - # Loop through each fold in the cross-validation - for fold_idx, (train, test) in enumerate(cv.split(X_train, y_train)): - # Fit the model on the training data - model.fit(X_train[train], y_train[train]) - # Use RocCurveDisplay to generate the ROC curve - roc_display = RocCurveDisplay.from_estimator(model, X_train[test], y_train[test], - name=f"ROC fold {fold_idx}", alpha=0.3, lw=1, ax=axes[model_idx]) - # Interpolate the true positive rates to get a smooth curve - interp_tpr = np.interp(mean_fpr, roc_display.fpr, roc_display.tpr) - interp_tpr[0] = 0.0 - # Append the interpolated TPR and AUC for this fold - tprs.append(interp_tpr) - aucs.append(roc_display.roc_auc) + if model_name == 'DT': + print(f"{group}-{method_names[j]}-{model_name}") + # Retrieve cv scores for our metrics of interest + scores = cross_validate(model, X_train, y_train, scoring=scorings, cv=cv, return_train_score=True, n_jobs=10) + # Save results of each fold + for metric_name in scorings.keys(): + scores_df.loc[model_name + f'_{metric_name}']=list(np.around(np.array(scores[f"test_{metric_name}"]),4)) + # Generate ROC curves + mean_fpr = np.linspace(0, 1, 100) + tprs, aucs = [], [] + # Loop through each fold in the cross-validation + for fold_idx, (train, test) in enumerate(cv.split(X_train, y_train)): + # Fit the model on the training data + model.fit(X_train[train], y_train[train]) + # Use RocCurveDisplay to generate the ROC curve + roc_display = RocCurveDisplay.from_estimator(model, X_train[test], y_train[test], + name=f"ROC fold {fold_idx}", alpha=0.3, lw=1, ax=axes[model_idx]) + # Interpolate the true positive rates to get a smooth curve + interp_tpr = np.interp(mean_fpr, roc_display.fpr, roc_display.tpr) + interp_tpr[0] = 0.0 + # Append the interpolated TPR and AUC for this fold + tprs.append(interp_tpr) + aucs.append(roc_display.roc_auc) # Plot the diagonal line representing random guessing axes[model_idx].plot([0, 1], [0, 1], linestyle='--', lw=2, color='r', alpha=.8) # Compute the mean and standard deviation of the TPRs @@ -220,19 +221,16 @@ if __name__ == "__main__": mean_tpr[-1] = 1.0 mean_auc = auc(mean_fpr, mean_tpr) # Calculate the mean AUC std_auc = np.std(aucs) - # Plot the mean ROC curve axes[model_idx].plot(mean_fpr, mean_tpr, color='b', label=r'Mean ROC (AUC = %0.2f $\pm$ %0.2f)' % (mean_auc, std_auc), lw=2, alpha=.8) - # Plot the standard deviation of the TPRs std_tpr = np.std(tprs, axis=0) tprs_upper = np.minimum(mean_tpr + std_tpr, 1) tprs_lower = np.maximum(mean_tpr - std_tpr, 0) axes[model_idx].fill_between(mean_fpr, tprs_lower, tprs_upper, color='grey', alpha=.2, label=r'$\pm$ 1 std. dev.') - # Set plot limits and title axes[model_idx].set(xlim=[-0.05, 1.05], ylim=[-0.05, 1.05], title=f"ROC Curve - {model_name} ({group}-{method_names[j]})") diff --git a/model_selection/output_cv_metrics/curves/pre_ORIG.svg b/model_selection/output_cv_metrics/curves/pre_ORIG.svg new file mode 100644 index 0000000..76afdc1 --- /dev/null +++ b/model_selection/output_cv_metrics/curves/pre_ORIG.svg @@ -0,0 +1,6739 @@ + + + + + + + + 2024-05-23T11:50:24.041575 + image/svg+xml + + + Matplotlib v3.8.4, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model_selection/output_cv_metrics/metrics.xlsx b/model_selection/output_cv_metrics/metrics.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..485d2da679ff13ff637a21c45a5c948e010161dd GIT binary patch literal 8030 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