Nothing
#' @export
#' @title Predict Binary Responses from a Logistic Biplot
#' @description
#' Predicts the binary response matrix from a fitted logistic biplot and
#' computes the optimal classification threshold for each variable by minimising
#' the Balanced Error Rate (BER).
#'
#' @details
#' The optimal threshold for variable \eqn{j} is the value \eqn{\alpha_j \in [0,1]}
#' that minimises the Balanced Error Rate:
#' \deqn{BER_j = 1 - \frac{1}{2}
#' \left(\frac{TP_j}{TP_j + FN_j} + \frac{TN_j}{TN_j + FP_j}\right),}
#' where \eqn{TP}, \eqn{TN}, \eqn{FP}, and \eqn{FN} denote true positives,
#' true negatives, false positives, and false negatives, respectively.
#'
#' @author Giovany Babativa <jgbabativam@@unal.edu.co>
#'
#' @param object An object of class \code{BiplotML}, as returned by
#' \code{\link{LogBip}}.
#' @param x The original binary matrix used to fit the model.
#' @param ncuts Number of equally spaced threshold candidates in \eqn{[0, 1]}.
#' Default is \code{100}.
#'
#' @return A named list of class \code{BiplotML} with components:
#' \describe{
#' \item{\code{thresholds}}{Data frame with the optimal threshold and minimum
#' BER for each variable.}
#' \item{\code{predictX}}{Predicted binary matrix.}
#' \item{\code{fitted}}{Confusion matrix (sensitivity, specificity, global
#' accuracy) for each variable.}
#' \item{\code{BER}}{Overall Balanced Error Rate (in percent).}
#' }
#'
#' @examples
#' \donttest{
#' data("Methylation")
#' LB <- LogBip(Methylation, plot = FALSE)
#' out <- pred_LB(LB, Methylation)
#' }
pred_LB <- function(object, x, ncuts = 100) {
for (pkg in c("tidyr", "dplyr")) {
if (!requireNamespace(pkg, quietly = TRUE)) {
stop("Package '", pkg, "' is required for this function. Please install it.",
call. = FALSE)
}
}
P <- fitted_LB(object, type = "response")
if (is.null(colnames(x))) colnames(x) <- paste0("V", seq_len(ncol(x)))
N1 <- ceiling(sum(x, na.rm = TRUE))
N0 <- length(as.matrix(x)) - N1
TE <- lapply(seq(0, 1, length.out = ncuts), function(z) {
Pr <- ifelse(P >= z, 1, 0)
c1 <- 1 - apply((Pr == 1) & (x == 1), 2, sum, na.rm = TRUE) /
apply(x == 1, 2, sum)
c2 <- 1 - apply((Pr == 0) & (x == 0), 2, sum, na.rm = TRUE) /
apply(x == 0, 2, sum)
list(100 / 2 * (c1 + c2))
})
TEp <- data.frame(dplyr::bind_rows(TE),
threshold = seq(0, 1, length.out = ncuts))
thresholds <- TEp |>
tidyr::pivot_longer(-threshold, names_to = "variable",
values_to = "BACC") |>
dplyr::group_by(variable) |>
dplyr::mutate(merror = min(BACC)) |>
dplyr::filter(BACC == merror) |>
dplyr::mutate(row = dplyr::row_number()) |>
dplyr::filter(row == 1) |>
dplyr::ungroup() |>
dplyr::select(variable, threshold, BACC)
thresholds <- thresholds[match(colnames(x), thresholds$variable), ]
Pr <- matrix(NA, nrow(P), ncol(P))
for (p in seq_len(ncol(P))) {
Pr[, p] <- ifelse(P[, p] >= thresholds$threshold[p], 1, 0)
}
c1 <- 1 - sum(apply((Pr == 1) & (x == 1), 2, sum, na.rm = TRUE)) / N1
c2 <- 1 - sum(apply((Pr == 0) & (x == 0), 2, sum, na.rm = TRUE)) / N0
BACC <- round(100 / 2 * (c1 + c2), 2)
PCC <- ifelse((x == 1 & Pr == 1) | (x == 0 & Pr == 0), 1, 0)
PCC[is.na(PCC)] <- 0
ones <- apply(x, 2, sum, na.rm = TRUE)
zeros <- length(as.matrix(x)) - ones
confusion <- data.frame(
Sensitivity = round(100 * apply((Pr == 1) & (x == 1), 2, sum,
na.rm = TRUE) / ones, 1),
Specificity = round(100 * apply((Pr == 0) & (x == 0), 2, sum,
na.rm = TRUE) / zeros, 1),
Global = round(100 * colSums(PCC) / nrow(PCC), 1)
)
out <- list(thresholds = thresholds, predictX = Pr,
fitted = confusion, BER = BACC)
class(out) <- c("BiplotML", "list")
return(out)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.