R/2-plot-functions.R

Defines functions plot.ceg barplot.sevt text.sevt edge make_stages_col node plot.sevt

Documented in barplot.sevt edge make_stages_col node plot.ceg plot.sevt text.sevt

#' Plot method for staged event trees
#'
#' Plot method for staged event tree
#' objects. It allows easy plotting of staged event trees with some
#' options (see Examples).
#' @param x an object of class \code{sevt}.
#' @param y alias for \code{limit} for compatibility with \code{plot}.
#' @param limit maximum number of variables plotted.
#' @param xlim the x limits (x1, x2) of the plot.
#' @param ylim the y limits of the plot.
#' @param main an overall title for the plot.
#' @param sub a sub title for the plot.
#' @param asp the y/x aspect ratio.
#' @param cex_label_nodes the magnification to be used for
#'                        the node labels. 
#'                        If set to \code{0} (as default) 
#'                        node labels are not showed.
#' @param cex_label_edges the magnification 
#'                        for the edge labels. 
#'                        If set to \code{0} edge labels are not displayed.
#' @param cex_nodes the magnification  for 
#'                  the nodes of the tree.
#' @param cex_tree_y the magnification for the 
#'                   tree in the vertical direction.
#'                   Default is \code{0.9} to leave some space 
#'                   for the variable names. 
#' @param col 
#'        color mapping for stages, one of the following: 
#'        NULL (color will be assigned based on the current palette);
#'        a named (variables) list of named (stages)
#'        vectors of colors;
#'        the character \code{"stages"}, in which case the stage names 
#'        will be used as colors; 
#'        a function that takes
#'        as input a vector of stages and output the corresponding colors.
#'        Check the provided examples. 
#'        The function \code{make_stages_col} is used internally
#'        and \code{make_stages_col(x, NULL)} or \code{make_stages_col(x, "stages")} 
#'        can be used as a starting point for colors tweaking.
#' @param col_edges color for the edges. 
#' @param var_names logical, if variable names should be added to the plot,
#'                  otherwise variable names can be added manually using 
#'                  \code{\link{text.sevt}}.
#' @param ignore vector of stages which will be ignored and left untouched,
#'               by default the name of the unobserved stages stored in
#'               `x$name_unobserved`.
#' @param pch_nodes either an integer specifying a symbol or a single character 
#'                  to be used as the default in plotting nodes shapes see 
#'                  \code{\link{points}}.
#' @param lwd_nodes the line width for edges, a positive number, defaulting to 1.
#' @param lwd_edges the line width for nodes, a positive number, defaulting to 1.
#' @param ... additional graphical parameters to be passed to
#'         \code{points}, \code{lines}, \code{title},
#'         \code{text} and \code{plot.window}.
#' @export
#' @importFrom graphics lines plot.new plot.window title
#'
#' @examples
#'
#' data("PhDArticles")
#' mod <- stages_bj(full(PhDArticles, join_unobserved = TRUE))
#'
#' ### simple plotting
#' plot(mod)
#'
#' ### labels in nodes 
#' plot(mod, cex_label_nodes = 1, cex_nodes = 0)
#'
#' ### reduce nodes size
#' plot(mod, cex_nodes = 0.5)
#'
#' ### change line width and nodes style
#' plot(mod, lwd_edges = 3, pch_nodes = 5)
#'
#' ### changing palette
#' plot(mod, col = function(s) heat.colors(length(s)))
#' 
#' ### or changing global palette
#' palette(hcl.colors(10, "Harmonic"))
#' plot(mod)
#' palette("default") ##
#' 
#' ### forcing plotting of unobserved stages
#' plot(mod, ignore = NULL)
#' 
#' ### use function to specify colors 
#' plot(mod, col = function(stages){
#'     hcl.colors(n = length(stages))
#' })
#'
#' ### manually give stages colors
#' ### as an example we will assign colors only to the stages of two variables
#' ### Gender (one stage named "1") and Mentor (six stages)
#' col <- list(Gender = c("1" = "blue"), 
#'             Mentor = c("UNOBSERVED" = "grey",
#'                         "2" = "red", 
#'                         "3" = "purple",
#'                         "10" = "pink",
#'                         "18" = "green",
#'                         "22" = "brown"))
#' ### by setting ignore = NULL we will plot also the UNOBSERVED stage for Mentor
#' plot(mod, col = col, ignore = NULL)
plot.sevt <-
  function(x,
           y = 10,
           limit = y,
           xlim = c(0, 1),
           ylim = c(0, 1),
           main = NULL,
           sub = NULL,
           asp = 1,
           cex_label_nodes = 0,
           cex_label_edges = 1,
           cex_nodes = 2,
           cex_tree_y = 0.9,
           col = NULL,
           col_edges = "black",
           var_names = TRUE,
           ignore = x$name_unobserved,
           pch_nodes = 16,
           lwd_nodes = 1,
           lwd_edges = 1,
           ...) {
    check_sevt(x)
    plot.new()
    d <- min(length(x$tree), limit) ## avoid too much plotting
    nms <- names(x$tree) ## name of variable
    if (is.null(x$stages[[nms[1]]])){ ## add stage name also to root
      x$stages[[nms[1]]] <- c("1")
    }
    col <- make_stages_col(x, col, ignore,limit =  d) 
    if (is.null(col_edges)){
      col_edges <- "black"
    }
    M <- prod(sapply(x$tree[1:d], length))
    cex_nodes <- rep(cex_nodes, d)[1:d]
    cex_label_nodes <- rep(cex_label_nodes, d)[1:d]
    plot.window(
      xlim = xlim,
      ylim = ylim,
      asp = asp,
      ...
    )
    title(main = main, sub = sub, ...)
    n <- x$tree
    p <- length(x$tree)
    Ls <- rep(0, d)
    Ls[d] <- cex_tree_y*(ylim[2] - ylim[1])
    ns <- M
    As <- rep(0, d)
    nv <- length(x$tree[[1]])
    if (d >= 2) {
      for (i in d:2) {
        nv <- length(x$tree[[i]])
        ns <- ns / nv
        As[i] <- Ls[i] / (ns + (ns - 1) / (nv - 1))
        Ls[i - 1] <- Ls[i] - As[i]
      }
      nv <- length(x$tree[[i - 1]])
      ns <- ns / nv
      As[i - 1] <- Ls[i - 1] / (ns + (ns - 1) / (nv - 1))
    }
    s1 <- ifelse(is.null(x$stages[[nms[1]]]), "1",
                 x$stages[[nms[1]]][1]
    )
    node(
      c(xlim[1], mean(ylim)),
      label = s1,
      cex_label = cex_label_nodes[1],
      cex_node = cex_nodes[1],
      col = col[[nms[1]]][s1],
      pch = pch_nodes,
      lwd = lwd_nodes,
      ...
    ) # plot first node
    xx <- xlim[1]
    y <- yy <- mean(ylim)
    ns <- 1
    step <- (xlim[2] - xlim[1]) / d
    for (k in 1:d) {
      # plot nodes for every strata
      v <- x$tree[[k]]
      yyy <- yy
      yy <- c()
      lj <- 0
      xx <- step * k # increase x position
      nv <- length(v)
      for (i in 1:ns) {
        # for every old node
        y <-
          yyy[i] + As[k] * seq(
            from = -0.5,
            to = 0.5,
            length.out = nv
          )
        # compute new y positions
        yy <- c(yy, y)
          for (j in 1:nv) {
            # plot nodes
            lj <- lj + 1
            if (k < d) {
              if (!(x$stages[[nms[k + 1]]][lj] %in% ignore)){
                node(
                  c(xx, y[j]),
                  label = x$stages[[nms[k + 1]]][lj],
                  cex_label = cex_label_nodes[k + 1],
                  col = col[[nms[k + 1]]][x$stages[[nms[k + 1]]][lj]],
                  cex_node = cex_nodes[k + 1],
                  pch = pch_nodes,
                  lwd = lwd_nodes,
                  ...
                )
              }
              if (!(x$stages[[nms[k]]][i] %in% ignore)){
                edge(c(
                  xx - step,
                  yyy[i]
                ), c(xx, y[j]),
                v[j],
                col = col_edges,
                cex_label = cex_label_edges, 
                lwd = lwd_edges,
                ...) # plot edge with previous node
              }
            }else{
              if (!(x$stages[[nms[k]]][i] %in% ignore)){
                edge(c(
                  xx - step,
                  yyy[i]
                ), c(xx, y[j]),
                v[j],
                col = col_edges,
                cex_label = cex_label_edges, 
                lwd = lwd_edges,
                ...
                ) # plot edge with previous nodes
              }
            }
          }
      }
      ns <- ns * nv
    }
    if (var_names){
      text.sevt(x, limit = limit, xlim = xlim, ylim = ylim, adj = 0)
    }
  }

#' Plot a node
#'
#' @param x the center
#' @param label the label
#' @param col color
#' @param cex_label cex parameter to be passed to text
#' @param cex_node cex parameter for nodes
#' @param ... additional parameters passed to \code{par()}
#' @importFrom graphics text lines points
#' @keywords internal
node <- function(x,
                 label = "",
                 col = "black",
                 cex_label = 1,
                 cex_node = 1,
                 ...) {
  points(x[1], x[2], col = col, cex = cex_node, ...)
  if (cex_label > 0) {
    text(
      x = x[1],
      y = x[2],
      labels = label,
      col = col,
      cex = cex_label,
      ...
    )
  }
}

#' @rdname plot.sevt
#' @export
make_stages_col <- function(x, col = NULL, 
                            ignore = x$name_unobserved, 
                            limit = NULL){
  d <- min(length(x$tree), limit)
  nms <- names(x$tree)
  if (is.null(col)) {
    col <- lapply(x$stages[nms[1:d]], function(stages) {
      if (is.null(stages)) {
        return(list("1" = "black"))
      }
      stages <- unique(stages)
      stages <- stages[!(stages %in% ignore)]
      vc <- seq_along(stages)
      names(vc) <- stages
      return(vc)
    })
  } else if (is.function(col)) {
    col <- lapply(x$stages[nms[1:d]], function(stages) {
      if (is.null(stages)) {
        return(list("1" = "black"))
      }
      stages <- unique(stages)
      stages <- stages[!(stages %in% ignore)]
      cs <- col(unique(stages))
      if (is.null(names(cs))){
        names(cs) <- unique(stages)[seq_along(cs)]
      }
      return(cs)
    })
  } else if (length(col) == 1 && col == "stages") {
    if (col == "stages") {
      col <- lapply(x$stages[nms[1:d]], function(stages) {
        if (is.null(stages)) {
          return(list("1" = 1))
        }
        stages <- unique(stages)
        stages <- stages[!(stages %in% ignore)]
        names(stages) <- stages
        return(stages)
      })
    }
  }
  return(col)
}

#' Plot an edge
#'
#' @param from From
#' @param to To
#' @param label the label
#' @param col color
#' @param cex_label numerical
#' @param ... additional parameters passed to \code{par()}
#' @importFrom graphics text lines
#' @keywords internal
edge <-
  function(from,
           to,
           label = "",
           col = "black",
           cex_label = 1,
           ...) {
    lines(c(from[1], to[1]), c(from[2], to[2]), col = col, ...)
    a <-
      180 * atan2((to[2] - from[2]), (to[1] - from[1])) / pi ## compute the angle of the line
    if (cex_label > 0) {
      ## put the label rotated of the proper angle
      text(
        x = (from[1] + to[1]) / 2,
        y = (from[2] + to[2]) / 2,
        labels = label,
        srt = a,
        col = col,
        cex = cex_label,
        ...
      )
    }
  }


#' Add text to a staged event tree plot
#'
#' @param x An object of class \code{sevt}.
#' @param y the position of the labels.
#' @param limit maximum number of variables plotted.
#' @param xlim graphical parameter.
#' @param ylim graphical parameter.
#' @param ... additional parameters passed to \code{\link{text}}.
#' @importFrom graphics text
#' @export
text.sevt <-
  function(x,
           y = ylim[1],
           limit = 10,
           xlim = c(0, 1),
           ylim = c(0, 1),
           ...) {
    check_sevt(x)
    d <- min(length(x$tree), limit) ## avoid too much plotting
    step <- (xlim[2] - xlim[1]) / d
    yy <- y
    var <- names(x$tree)
    text(x = seq(from = xlim[1], to = xlim[2], length.out = d + 1)[1:d], y = y, 
         labels = var[1:d], ...)
  }


#' Bar plots of stage probabilities
#' 
#' Create a bar plot visualizing probabilities associated to the 
#' different stages of a variable in a staged event tree. 
#' @param height an object of class \code{sevt}.
#' @param var name of a variable in \code{object}.
#' @param ignore vector of stages which will be ignored and left untouched,
#'               by default the name of the unobserved stages stored in
#'               `object$name_unobserved`.
#' @param beside a logical value. See \code{\link{barplot}}.
#' @param horiz a logical value. See \code{\link{barplot}}.
#' @param legend.text logical.
#' @param col color mapping for the stages, see \code{col}
#'        argument in \code{\link{plot.sevt}}.
#' @param xlab a label for the x axis.
#' @param ylab a label for the y axis.
#' @param ... additional arguments passed to \code{\link{barplot}}.
#' @return As \code{\link{barplot}}: 
#'         A numeric vector (or matrix, when beside = TRUE), 
#'         giving the coordinates of all the bar midpoints drawn, useful 
#'         for adding to the graph.
#' @export
#' @examples 
#' model <- stages_fbhc(full(PhDArticles, lambda = 1))
#' barplot(model, "Kids", beside = TRUE)
#' @importFrom graphics barplot
barplot.sevt <- function(height, var, 
                           ignore = height$name_unobserved,
                           beside = TRUE,
                           horiz = FALSE,
                           legend.text = FALSE, 
                           col = NULL, 
                           xlab = ifelse(horiz, "probability", NA),
                           ylab = ifelse(!horiz, "probability", NA),
                           ...){
  check_sevt_prob(height)
  stg <- stages(height, var)
  stg <- stg[!(stg %in% ignore)]
  ustg <- unique(stg) 
  if (is.null(col)) {
      if (is.null(stg)) {
        col = list("1" = "black")
      }else{
        col <- seq_along(ustg)
        names(col) <- ustg
      }
  } else if (is.function(col)) {
      if (is.null(stg)) {
        col <- list("1" = 1)
      }else{
        col <- col(ustg)
        if (is.null(names(col))){
          names(col) <- ustg
        }
      }
  } else if (length(col) == 1 && col == "stages") {
    if (col == "stages") {
        if (is.null(stg)) {
          col <- list("1" = 1)
        }else{
          col <- ustg
          names(col) <- ustg
        }
    }
  }
  tmp <- summary(height)[["stages.info"]][[var]]
  if (legend.text){
    legend.text = tmp$stage[tmp$stage %in% ustg]
  }
  hei <- as.matrix(tmp[tmp$stage %in% ustg, -(1:3)])
  hei[is.nan(hei)] <- 0
  
  barplot(hei, col = col, 
          legend.text = legend.text, beside = beside,
          xlab = xlab, ylab = ylab,
          horiz = horiz, ...)
}

#' igraph's plotting for CEG 
#' 
#' @param x an object of class \code{\link{ceg}}. 
#' @param col colors specification see \code{\link{plot.sevt}}.
#' @param ignore vector of stages which will be ignored and left untouched,
#'               by default the name of the unobserved stages stored in
#'               `x$name_unobserved`.
#' @param layout an igraph layout.
#' @param ... additional arguments passed to \code{plot.igraph}.
#' @details This function is a simple wrapper around 
#'  \pkg{igraph}'s \code{plot.igraph}.
#'  The ceg object is converted to an igraph object 
#'  by firstly obtaining the adjacency matrix representation
#'  with \code{\link{ceg2adjmat}}. 
#'  If not specified, the default \code{layout} used is 
#'  a rotated \code{layout.reingold.tilford}.
#'  
#'  We use \code{palette()} as palette for
#'  the \pkg{igraph} plotting, while \code{plot.igraph} uses 
#'  as default a different palette. This is to allow matching 
#'  stages colors between \code{plot.ceg} 
#'  and \code{\link{plot.sevt}}.
#' @examples 
#' \dontrun{
#'  model <- stages_bhc(full(Titanic))
#'  model.ceg <- ceg(model)
#'  plot(model.ceg, edge.arrow.size = 0.1, vertex.label.dist = -2)
#'  }
#' @importFrom grDevices palette
#' @export
plot.ceg <- function(x, col = NULL,
                     ignore = x$name_unobserved, 
                     layout = NULL,
                      ...){
  if (!requireNamespace("igraph", quietly = TRUE)) {
    stop("Package \"igraph\" is needed to plot ceg.",
         call. = FALSE
    )
  }
  nms <- names(x$tree)
  if (is.null(x$stages[[nms[1]]])){ ## add stage name also to root
    x$stages[[nms[1]]] <- c("1")
  }
  A <- ceg2adjmat(x)
  ### get colors as in plot.sevt
  col <- make_stages_col(x, col, ignore)
  g <- igraph::graph_from_adjacency_matrix(A)
  col.pos <- lapply(seq_along(x$positions), function(i){
    upos <- unique(x$positions[[nms[i]]])
    ustag <- x$stages[[nms[i]]][sapply(upos, function(pp) which.max(x$positions[[nms[i]]] == pp))]
    cc <- col[[nms[i]]][ustag]
    if (is.null(cc)) cc <- NA
    names(cc) <- paste0(nms[i], ":", upos)
    return(cc)
  })
  igraph::V(g)$color <- c(unlist(col.pos), 1)
  if (is.null(layout)){
    layout = igraph::layout.reingold.tilford(g)
    layout = layout[,2:1]
    layout[,1] <- -layout[,1]
  }
  igraph::plot.igraph(g, layout = layout, 
                      palette = palette(), ...)
}

Try the stagedtrees package in your browser

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

stagedtrees documentation built on April 29, 2022, 1:06 a.m.