@@ -175,6 +175,11 @@ exp_chaid <- function(df,
175175 model , train_all , binary_classification_threshold
176176 )
177177 model $ predicted_prob <- chaid_positive_probability(model , train_all )
178+ # Full class-probability matrix for report_metrics (Macro / One-vs-Rest AUCs).
179+ # Column names are the raw class levels so multiclass_auc_by_class() can use them.
180+ model $ predicted_prob_matrix <- chaid_as_probability_matrix(
181+ train_all , model $ class_levels
182+ )
178183
179184 # Metadata expected by the framework.
180185 model $ terms_mapping <- names(group_name_map )
@@ -317,6 +322,37 @@ chaid_positive_probability <- function(model, all_prediction) {
317322 }
318323}
319324
325+ # ' Convert CHAID probability output to a class-named matrix.
326+ # '
327+ # ' `chaid_predict(type = "prob"|"all")` uses `.pred_prob_<class>` columns. The
328+ # ' shared Decision Tree report helpers (`multiclass_auc_by_class`,
329+ # ' `evaluate_by_class_report_metrics`) expect colnames to be the class levels.
330+ # '
331+ # ' @param prediction A data frame / matrix from `chaid_predict`, or NULL.
332+ # ' @param class_levels Optional class order; columns are reordered when present.
333+ # ' @return A numeric matrix, or NULL when conversion is not possible.
334+ chaid_as_probability_matrix <- function (prediction , class_levels = NULL ) {
335+ if (is.null(prediction )) {
336+ return (NULL )
337+ }
338+ if (is.data.frame(prediction ) || is.matrix(prediction )) {
339+ mat <- as.matrix(prediction )
340+ } else {
341+ return (NULL )
342+ }
343+ if (ncol(mat ) == 0 ) {
344+ return (NULL )
345+ }
346+ colnames(mat ) <- sub(" ^\\ .pred_prob_" , " " , colnames(mat ))
347+ if (! is.null(class_levels ) && length(class_levels ) > 0 ) {
348+ if (! all(class_levels %in% colnames(mat ))) {
349+ return (NULL )
350+ }
351+ mat <- mat [, class_levels , drop = FALSE ]
352+ }
353+ mat
354+ }
355+
320356# ' Return an empty CHAID permutation-importance result.
321357# '
322358# ' @return A data frame with the stable importance schema.
@@ -684,14 +720,17 @@ augment.exploratory_chaid <- function(x, data = NULL, newdata = NULL,
684720# '
685721# ' @param x A fitted `exploratory_chaid` model.
686722# ' @param pretty.name Whether to use display-friendly column names.
723+ # ' @param report_metrics Whether to include Decision Tree report extras
724+ # ' (ROC AUC / PR AUC / …).
687725# ' @param ... Unused.
688726# ' @return A one-row model summary data frame.
689727# ' @export
690- glance.exploratory_chaid <- function (x , pretty.name = FALSE , ... ) {
728+ glance.exploratory_chaid <- function (x , pretty.name = FALSE , report_metrics = FALSE , ... ) {
691729 if (" error" %in% class(x )) {
692730 return (data.frame (Note = x $ message ))
693731 }
694- tidy.exploratory_chaid(x , type = " evaluation" , pretty.name = pretty.name , ... )
732+ tidy.exploratory_chaid(x , type = " evaluation" , pretty.name = pretty.name ,
733+ report_metrics = report_metrics , ... )
695734}
696735
697736# ' tidy for a CHAID model (broom S3 method).
@@ -703,6 +742,9 @@ glance.exploratory_chaid <- function(x, pretty.name = FALSE, ...) {
703742# ' `partial_dependence`.
704743# ' @param pretty.name Whether to use display-friendly column names.
705744# ' @param binary_classification_threshold Positive-class threshold (binary).
745+ # ' @param report_metrics Whether to include Decision Tree report extras
746+ # ' (ROC AUC / PR AUC / Balanced Accuracy / Specificity for binary;
747+ # ' Macro AUCs for multiclass; One-vs-Rest AUCs for evaluation_by_class).
706748# ' @param ... Unused.
707749# ' @return A data frame whose shape depends on `type`.
708750# ' @export
@@ -733,12 +775,23 @@ chaid_display_node_ids <- function(df) {
733775}
734776
735777tidy.exploratory_chaid <- function (x , type = " evaluation" , pretty.name = FALSE ,
736- binary_classification_threshold = 0.5 , ... ) {
778+ binary_classification_threshold = 0.5 ,
779+ report_metrics = FALSE , ... ) {
737780 if (" error" %in% class(x ) && type != " evaluation" ) {
738781 return (data.frame ())
739782 }
740783 actual <- x $ y
741- predicted <- x $ predicted_class
784+ # Re-threshold binary labels so Settings → cut point updates F1 / Accuracy etc.
785+ # (ROC / PR AUC use predicted_prob and are threshold-independent.)
786+ predicted <- if (identical(x $ classification_type , " binary" ) &&
787+ ! is.null(x $ predicted_prob )) {
788+ factor (
789+ ifelse(x $ predicted_prob > = binary_classification_threshold , " TRUE" , " FALSE" ),
790+ levels = x $ class_levels
791+ )
792+ } else {
793+ x $ predicted_class
794+ }
742795 chaid_display_node_ids(switch (
743796 type ,
744797 evaluation = {
@@ -747,17 +800,41 @@ tidy.exploratory_chaid <- function(x, type = "evaluation", pretty.name = FALSE,
747800 }
748801 if (identical(x $ classification_type , " binary" )) {
749802 evaluate_binary_classification(actual , predicted , x $ predicted_prob ,
750- pretty.name = pretty.name , is_rpart = FALSE )
803+ pretty.name = pretty.name , is_rpart = FALSE ,
804+ report_metrics = report_metrics )
751805 } else {
752- evaluate_multi_(data.frame (predicted = predicted , actual = actual ),
753- " predicted" , " actual" , pretty.name = pretty.name )
806+ ret <- evaluate_multi_(data.frame (predicted = predicted , actual = actual ),
807+ " predicted" , " actual" , pretty.name = pretty.name )
808+ # Mirror tidy.rpart: Macro ROC/PR AUC for the Decision Tree report (#37156).
809+ if (report_metrics ) {
810+ balanced_accuracy <- multiclass_balanced_accuracy(actual , predicted )
811+ auc_by_class <- multiclass_auc_by_class(actual , x $ predicted_prob_matrix )
812+ macro_roc_auc <- if (nrow(auc_by_class ) > 0 ) mean(auc_by_class $ roc_auc , na.rm = TRUE ) else NA_real_
813+ macro_pr_auc <- if (nrow(auc_by_class ) > 0 ) mean(auc_by_class $ pr_auc , na.rm = TRUE ) else NA_real_
814+ extra <- if (pretty.name ) {
815+ tibble :: tibble(`Balanced Accuracy` = balanced_accuracy ,
816+ `Macro ROC AUC` = macro_roc_auc ,
817+ `Macro PR AUC` = macro_pr_auc )
818+ } else {
819+ tibble :: tibble(balanced_accuracy = balanced_accuracy ,
820+ macro_roc_auc = macro_roc_auc ,
821+ macro_pr_auc = macro_pr_auc )
822+ }
823+ ret <- dplyr :: bind_cols(ret , extra )
824+ }
825+ ret
754826 }
755827 },
756828 evaluation_by_class = {
757829 per_level <- function (level ) {
758830 evaluate_classification(actual , predicted , level , pretty.name = pretty.name )
759831 }
760- dplyr :: bind_rows(lapply(x $ class_levels , per_level ))
832+ ret <- dplyr :: bind_rows(lapply(x $ class_levels , per_level ))
833+ if (report_metrics && nrow(ret ) > 0 ) {
834+ ret <- dplyr :: bind_cols(ret , evaluate_by_class_report_metrics(
835+ actual , predicted , x $ class_levels , x $ predicted_prob_matrix , pretty.name ))
836+ }
837+ ret
761838 },
762839 conf_mat = {
763840 calc_conf_mat(actual , predicted )
0 commit comments