R/micmac.R

Defines functions plot_micmac print.micmac_result micmac_analysis

Documented in micmac_analysis plot_micmac print.micmac_result

#' MICMAC Analysis
#'
#' Performs MICMAC (Cross-Impact Matrix Multiplication Applied to Classification)
#' analysis on a reachability matrix to classify elements based on their driving
#' power and dependence power.
#'
#' @param reach_matrix A square reachability matrix (n x n) with 0/1 entries,
#'   typically computed using \code{\link{compute_reachability}}.
#'
#' @return An object of class \code{micmac_result}, which is a data frame with:
#'   \itemize{
#'     \item \code{node}: node index
#'     \item \code{label}: node label (from matrix dimnames or numeric)
#'     \item \code{driving_power}: number of elements this node can reach
#'     \item \code{dependence_power}: number of elements that can reach this node
#'     \item \code{cluster}: classification into one of four clusters
#'   }
#'
#'   The four clusters are:
#'   \describe{
#'     \item{I - Autonomous}{Low driving power, low dependence. Disconnected from system.}
#'     \item{II - Dependent}{Low driving power, high dependence. Outcomes/results.}
#'     \item{III - Linkage}{High driving power, high dependence. Unstable, key connectors.}
#'     \item{IV - Independent}{High driving power, low dependence. Root causes/drivers.}
#'   }
#'
#' @details
#' MICMAC analysis is a complementary technique to ISM that helps identify the
#' key drivers and dependent variables in a system.
#'
#' \strong{Driving Power}: The number of elements that a given element can reach
#' (row sum of reachability matrix).
#'
#' \strong{Dependence Power}: The number of elements that can reach a given element
#' (column sum of reachability matrix).
#'
#' Elements are classified into four clusters based on whether their driving and
#' dependence powers are above or below the median values.
#'
#' @references
#' Duperrin, J. C., & Godet, M. (1973). Methode de hierarchisation des elements
#' d'un systeme. Rapport economique du CEA, R-45-41.
#'
#' Warfield, J. N. (1974). Developing interconnection matrices in structural
#' modeling. \emph{IEEE Transactions on Systems, Man, and Cybernetics},
#' SMC-4(1), 81-87. \doi{10.1109/TSMC.1974.5408524}
#'
#' @seealso
#' \code{\link{plot_micmac}} for visualization,
#' \code{\link{compute_reachability}} for computing reachability matrices,
#' \code{\link{level_partitioning}} for hierarchical decomposition.
#'
#' @export
#' @examples
#' # Create adjacency matrix
#' adj <- matrix(c(0, 1, 0, 0, 0,
#'                 0, 0, 1, 0, 0,
#'                 0, 0, 0, 1, 1,
#'                 0, 0, 0, 0, 0,
#'                 0, 0, 0, 0, 0), nrow = 5, byrow = TRUE)
#' rownames(adj) <- colnames(adj) <- paste0("F", 1:5)
#'
#' # Compute reachability and MICMAC
#' reach <- compute_reachability(adj)
#' micmac <- micmac_analysis(reach)
#' print(micmac)
#'
#' # View cluster distribution
#' table(micmac$cluster)
micmac_analysis <- function(reach_matrix) {
  # Input validation
  if (!is.matrix(reach_matrix)) {
    stop("Input must be a matrix", call. = FALSE)
  }
  if (nrow(reach_matrix) != ncol(reach_matrix)) {
    stop("Matrix must be square", call. = FALSE)
  }

  n <- nrow(reach_matrix)

  # Get labels
  labels <- rownames(reach_matrix)
  if (is.null(labels)) {
    labels <- as.character(seq_len(n))
  }

  # Calculate driving power (row sums) and dependence power (column sums)
  driving_power <- rowSums(reach_matrix)
  dependence_power <- colSums(reach_matrix)

  # Calculate thresholds (median values)
  driving_threshold <- stats::median(driving_power)
  dependence_threshold <- stats::median(dependence_power)

  # Classify into clusters
  cluster <- character(n)
  for (i in seq_len(n)) {
    high_driving <- driving_power[i] >= driving_threshold
    high_dependence <- dependence_power[i] >= dependence_threshold

    if (!high_driving && !high_dependence) {
      cluster[i] <- "I - Autonomous"
    } else if (!high_driving && high_dependence) {
      cluster[i] <- "II - Dependent"
    } else if (high_driving && high_dependence) {
      cluster[i] <- "III - Linkage"
    } else {
      cluster[i] <- "IV - Independent"
    }
  }

  result <- data.frame(
    node = seq_len(n),
    label = labels,
    driving_power = driving_power,
    dependence_power = dependence_power,
    cluster = cluster,
    stringsAsFactors = FALSE
  )

  # Add attributes for plotting
  attr(result, "driving_threshold") <- driving_threshold
  attr(result, "dependence_threshold") <- dependence_threshold

  class(result) <- c("micmac_result", "data.frame")
  return(result)
}

#' Print MICMAC Results
#'
#' @param x An object of class \code{micmac_result}
#' @param ... Additional arguments (ignored)
#'
#' @return Invisibly returns the input object
#'
#' @export
#' @method print micmac_result
print.micmac_result <- function(x, ...) {
  cat("MICMAC Analysis Results\n")
  cat("=======================\n\n")

  # Print cluster summary
  cluster_counts <- table(x$cluster)
  cat("Cluster Distribution:\n")
  for (cl in names(cluster_counts)) {
    cat(sprintf("  %s: %d elements\n", cl, cluster_counts[cl]))
  }

  cat("\nThresholds:\n")
  cat(sprintf("  Driving Power: %.1f\n", attr(x, "driving_threshold")))
  cat(sprintf("  Dependence Power: %.1f\n", attr(x, "dependence_threshold")))

  cat("\nElement Details:\n")
  print.data.frame(x, row.names = FALSE)

  invisible(x)
}

#' Plot MICMAC Diagram
#'
#' Creates a scatter plot showing the MICMAC classification of elements based
#' on their driving power and dependence power.
#'
#' @param micmac_result An object of class \code{micmac_result} from
#'   \code{\link{micmac_analysis}}.
#' @param show_labels Logical. If \code{TRUE} (default), show node labels on the plot.
#' @param main Title for the plot. Default is "MICMAC Analysis".
#' @param ... Additional arguments passed to \code{plot}.
#'
#' @return Invisibly returns the micmac_result object.
#'
#' @details
#' The plot is divided into four quadrants:
#' \describe{
#'   \item{Quadrant I (bottom-left)}{Autonomous variables - weak drivers, weak dependence}
#'   \item{Quadrant II (bottom-right)}{Dependent variables - weak drivers, strong dependence}
#'   \item{Quadrant III (top-right)}{Linkage variables - strong drivers, strong dependence}
#'   \item{Quadrant IV (top-left)}{Independent variables - strong drivers, weak dependence}
#' }
#'
#' @seealso
#' \code{\link{micmac_analysis}} for computing MICMAC results.
#'
#' @export
#' @examples
#' adj <- matrix(c(0, 1, 0, 0, 0,
#'                 0, 0, 1, 0, 0,
#'                 0, 0, 0, 1, 1,
#'                 0, 0, 0, 0, 0,
#'                 0, 0, 0, 0, 0), nrow = 5, byrow = TRUE)
#' rownames(adj) <- colnames(adj) <- paste0("F", 1:5)
#'
#' reach <- compute_reachability(adj)
#' micmac <- micmac_analysis(reach)
#' plot_micmac(micmac)
plot_micmac <- function(micmac_result,
                        show_labels = TRUE,
                        main = "MICMAC Analysis",
                        ...) {
  if (!inherits(micmac_result, "micmac_result")) {
    stop("Input must be a micmac_result object from micmac_analysis()", call. = FALSE)
  }

  # Get thresholds
  driving_threshold <- attr(micmac_result, "driving_threshold")
  dependence_threshold <- attr(micmac_result, "dependence_threshold")

  # Cluster colors
  cluster_colors <- c(
    "I - Autonomous" = "#999999",
    "II - Dependent" = "#E69F00",
    "III - Linkage" = "#56B4E9",
    "IV - Independent" = "#009E73"
  )

  # Set up plot
  old_par <- graphics::par(mar = c(5, 5, 4, 2))
  on.exit(graphics::par(old_par))

  x_range <- range(micmac_result$dependence_power)
  y_range <- range(micmac_result$driving_power)

  # Add some padding
  x_pad <- diff(x_range) * 0.1
  y_pad <- diff(y_range) * 0.1

  graphics::plot(
    micmac_result$dependence_power,
    micmac_result$driving_power,
    xlim = c(x_range[1] - x_pad, x_range[2] + x_pad),
    ylim = c(y_range[1] - y_pad, y_range[2] + y_pad),
    xlab = "Dependence Power",
    ylab = "Driving Power",
    main = main,
    pch = 19,
    cex = 1.5,
    col = cluster_colors[micmac_result$cluster],
    ...
  )

  # Add threshold lines
  graphics::abline(h = driving_threshold, lty = 2, col = "gray50")
  graphics::abline(v = dependence_threshold, lty = 2, col = "gray50")

  # Add quadrant labels
  x_mid <- mean(x_range)
  y_mid <- mean(y_range)

  graphics::text(x_range[1], y_range[2], "IV\nIndependent",
                 adj = c(0, 1), col = "gray40", cex = 0.8)
  graphics::text(x_range[2], y_range[2], "III\nLinkage",
                 adj = c(1, 1), col = "gray40", cex = 0.8)
  graphics::text(x_range[1], y_range[1], "I\nAutonomous",
                 adj = c(0, 0), col = "gray40", cex = 0.8)
  graphics::text(x_range[2], y_range[1], "II\nDependent",
                 adj = c(1, 0), col = "gray40", cex = 0.8)

  # Add labels if requested
  if (show_labels) {
    graphics::text(
      micmac_result$dependence_power,
      micmac_result$driving_power,
      labels = micmac_result$label,
      pos = 3,
      cex = 0.8
    )
  }

  # Add legend
  graphics::legend(
    "topright",
    legend = names(cluster_colors),
    col = cluster_colors,
    pch = 19,
    cex = 0.8,
    bg = "white"
  )

  invisible(micmac_result)
}

Try the ISMtools package in your browser

Any scripts or data that you put into this service are public.

ISMtools documentation built on March 13, 2026, 1:06 a.m.