R/mdaplot.R

Defines functions mdaplotyy mdaplot mdaplot.plotAxes mdaplot.getYTickLabels mdaplot.getXTickLabels mdaplot.getYTicks mdaplot.getXTicks mdaplot.getYAxisLim mdaplot.getXAxisLim mdaplot.getColors mdaplot.showLines mdaplot.showColorbar mdaplot.prepareColors mdaplot.formatValues mdaplot.areColors

Documented in mdaplot mdaplot.areColors mdaplot.formatValues mdaplot.getColors mdaplot.getXAxisLim mdaplot.getXTickLabels mdaplot.getXTicks mdaplot.getYAxisLim mdaplot.getYTickLabels mdaplot.getYTicks mdaplot.plotAxes mdaplot.prepareColors mdaplot.showColorbar mdaplot.showLines mdaplotyy

#' Check color values
#'
#' @description
#' Checks if elements of argument are valid color values
#'
#' @param palette
#' vector with possibly color values (names, RGB, etc.)
#'
mdaplot.areColors <- function(palette) {
   sapply(palette, function(x) tryCatch(is.matrix(col2rgb(x)), error = function(e) FALSE))
}

#' Format vector with numeric values
#'
#' @description
#' Format vector with values, so only significant decimal numbers are left.
#'
#' @param data
#' vector or matrix with values
#' @param round.only
#' logical, do formatting or only round the values
#' @param digits
#' how many significant digits take into account
#'
#' @details
#' Function takes into accound difference between values and the values themselves.
#'
#' @return
#' matrix with formatted values
#'
mdaplot.formatValues <- function(data, round.only = FALSE, digits = 3) {

   # if values are not numeric - return as is
   if (!is.numeric(data[1])) return(data)

   fdata <- if (round.only) round(data, digits) else prettyNum(data, digits = digits)

   if (!is.null(dim(data))) {
      dim(fdata) <- dim(data)
      dimnames(fdata) <- dimnames(data)
   }

   return(fdata)
}


#' Prepare colors based on palette and opacity value
#'
#' @param palette
#' vector with main colors for current pallette
#' @param ncolors
#' number of colors to generate
#' @param opacity
#' opacity for the colors (one value or individual for each color)
#'
#' @return
#' vector with colors
#'
mdaplot.prepareColors <- function(palette, ncolors, opacity) {

   # generate colors based on color ramp and palette
   colors <- colorRampPalette(palette)(ncolors)

   # no opacity - just return colors as is
   if (is.null(opacity) || all(opacity == 1)) {
      return(colors)
   }

   # repeate opacity values for each color
   if (length(opacity) == 1) {
      opacity <- rep(opacity, ncolors)
   }

   if (length(opacity) != ncolors) {
      stop('Wrong number of values for "opacity" parameter!')
   }

   # apply opacity
   for (i in 1:ncolors) {
      colors[i] <- adjustcolor(colors[i], alpha.f = opacity[i])
   }

   return(colors)
}


#' Plot colorbar
#'
#' @description
#' Shows a colorbar if plot has color grouping of elements (points or lines).
#'
#' @param cgroup
#' a vector with values used to make color grouping of the elements
#' @param colmap
#' a colormap to be used for color generation
#' @param lab.col
#' color for legend labels
#' @param lab.cex
#' size for legend labels
#'
mdaplot.showColorbar <- function(cgroup, colmap = "default", lab.col = "darkgray", lab.cex = 0.65) {
   # get number of levels for the cgroup

   # define if colorbar should be discrete (for factors) or not
   shift <- ifelse(is.factor(cgroup), 1, 0)

   if (!is.factor(cgroup) && length(unique(cgroup)) > 12) {
         # get colors for 8 groups based on colormap
         col <- mdaplot.getColors(ngroups = 12, colmap = colmap)
         ncol <- length(unique(col))

         # split values to intervals
         cgroupl <- levels(cut(as.vector(cgroup), ncol))

         # get left and right values for the intervals
         lvals <- as.numeric(sub("\\((.+),.*", "\\1", cgroupl))
         rvals <- as.numeric(sub("[^,]*,([^]]*)\\]", "\\1", cgroupl))

         # correct issue with first element
         if (min(cgroup) != lvals[1]) {
            lvals[1] <- min(cgroup)
         }

         # combine values and define matrix for labels
         vals <- c(lvals, rvals[ncol])
         labels <- matrix(0, ncol = 2, nrow = ncol + 1)
   } else {
      if (!is.factor(cgroup)) {
         cgroup <- factor(cgroup)
      }

      nlevels <- length(attr(cgroup, "levels"))

      # no splitting is needed, just use factors as labels
      col <- mdaplot.getColors(ngroups = nlevels, colmap = colmap)
      ncol <- length(unique(col))
      vals <- levels(cgroup)
      labels <- matrix(0, ncol = 2, nrow = ncol)
   }

   # use formatted values as rownames for labels matrix
   rownames(labels) <- mdaplot.formatValues(vals)

   # get size of the plotting area and calculate size for color bar elements
   lim <- par("usr")

   dx <- lim[2] - lim[1]
   dy <- lim[4] - lim[3]

   w <- (dx * 0.8) / ncol
   h <- dy * 0.015
   shift <- shift * w * 0.02 # 2 percent of segment width

   x <- lim[1] + dx * 0.1
   y <- lim[4] - (h + 0.1 * h)

   # show colorbar and define coordinates for labels
   for (i in seq_len(ncol)) {
      rect(shift + x + w * (i - 1), y, x + w * i, y - h, col = col[i], border = NA)
      labels[i, ] <- c(x + w * (i - 1), y - h)
   }

   # add last value or shift coordinates if labels shall be centered
   if (nrow(labels) > i) {
      labels[i + 1, ] <- c(x + w * i, y - h)
   } else {
      labels[, 1] <- labels[, 1] + w / 2
   }

   # show labels for colorbar regions
   text(labels[, 1], labels[, 2], labels = rownames(labels), pos = 1, col = lab.col, cex = lab.cex)
}

#' Plot lines
#'
#' @description
#' Shows horisontal and vertical lines on a plot.
#'
#' @param point
#' vector with two values: x coordinate for vertical point y for horizontal
#' @param lty
#' line type
#' @param lwd
#' line width
#' @param col
#' color of lines
#'
#' @details
#' If it is needed to show only one line, the other coordinate shall be set to NA.
#'
mdaplot.showLines <- function(point, lty = 2, lwd = 0.75, col = rgb(0.2, 0.2, 0.2)) {

   if (!is.na(point[2])) {
      abline(h = point[2], lty = lty, lwd = lwd, col = col)
   }

   if (!is.na(point[1])) {
      abline(v = point[1], lty = lty, lwd = lwd, col = col)
   }
}


#' Color values for plot elements
#'
#' @description
#' Generate vector with color values for plot objects (lines, points, bars), depending
#' on number of groups for the objects.
#'
#' @param ngroups
#' number of colors to create.
#' @param cgroup
#' vector of values, used for color grouping of plot points or lines.
#' @param colmap
#' which colormap to use ('default', 'gray', 'old', or user defined in form c('col1', 'col2', ...)).
#' @param opacity
#' opacity for colors (between 0 and 1)
#' @param maxsplits
#' if contenuous values are used for color gruping - how many groups to create?
#'
#' @importFrom grDevices col2rgb colorRampPalette rgb adjustcolor
#'
#' @return
#' Returns vector with generated color values
#'
#' @export
mdaplot.getColors <- function(ngroups = NULL, cgroup = NULL, colmap = "default",
   opacity = 1, maxsplits = 64) {

   # if non of the main arguments defined assume only one color is needed
   if (is.null(ngroups) && is.null(cgroup)) {
      ngroups <- 1
   }

   # list with currently supported color maps
   colmaps <- list(
      "gray" = c(
         "#E8E8E8", "#D6D6D6", "#C4C4C4", "#B2B2B2",
         "#9A9A9A", "#808080", "#484848", "#101010"
      ),
      # jet (like old in MATLAB)
      "jet" = c(
         "#00007F", "blue", "#007FFF", "cyan",
         "#7FFF7F", "yellow", "#FF7F00", "red", "#7F0000"
      ),
      # old (used in mdatools in versions < 0.10.0)
      "old" = c(
         "#3288BD", "#66C2A5", "#ABDDA4", "#E6F598",
         "#FEE08B", "#FDAE61", "#F46D43", "#D53E4F"
      ),
      # current default
      "default" = c(
         "#2679B2", "#1C9AA8", "#379531",
         "#EED524", "#FB7F28", "#D22C2F"
      )
   )

   # define palette (if colmap has more than one value - take it as palette)
   palette <- if (length(colmap) > 1) colmap else colmaps[[colmap]]

   if (!all(mdaplot.areColors(palette))) {
      stop("Parameter 'colmap' must contains valid color values or name of palette.")
   }

   # if grayscale palette and only one color is needed reorder pallete so the black is first
   if (all(colmap == "gray") && is.null(cgroup) && ngroups == 1) {
      palette <- rev(palette)
   }

   # if cgroup is not provided just return the colors

   if (is.null(cgroup)) {
      return(mdaplot.prepareColors(palette, ngroups, opacity))
   }

   if (!is.null(dim(cgroup))) {
      stop("Parameter 'cgroup' should be a vector of values or a factor.")
   }

   # if cgroup is factor return vector with corresponding values
   if (is.factor(cgroup)) {
      ngroups <- length(attr(cgroup, "levels"))
      return(mdaplot.prepareColors(palette, ngroups, opacity)[as.numeric(cgroup)])
   }

   # if not split it into groups
   if (is.null(ngroups)) {
      ngroups <- length(unique(cgroup))
      ngroups <- ifelse(ngroups > maxsplits, maxsplits, ngroups)
   }

   # if number of groups is larger make binning of cgroup parameter
   if (ngroups > 1) {
      cgroup <- cut(as.numeric(cgroup), ngroups, include.lowest = TRUE)
   }

   # create colors and return
   out_palette <- mdaplot.prepareColors(palette, ngroups, opacity)
   colors <- out_palette[as.numeric(cgroup)]
   attr(colors, "palette") <- out_palette

   return(colors)
}


#' Calculate limits for x-axis.
#'
#' @description
#' Calculates limits for x-axis depending on data values that have to be plotted,
#' extra plot elements that have to be shown and margins.
#'
#' @param ps
#' `plotseries` object.
#' @param xlim
#' limits provided by user
#' @param show.labels
#' logical, will data labels be shown on the plot
#' @param show.lines
#' logical or numeric with line coordinates to be shown on the plot.
#' @param show.excluded
#' logical, will excluded values be shown on the plot
#' @param bwd
#' if limits are computed for bar plot, this is a bar width (otherwise NULL)
#'
#' @return
#' Returns a vector with two limits.
#'
mdaplot.getXAxisLim <- function(ps, xlim, show.labels = FALSE, show.lines = FALSE,
   show.excluded = FALSE, bwd = 0.8) {

   # if user provided limits for x - use them
   if (!is.null(xlim)) return(xlim)

   # x axis limits in case of bar plot
   if (ps$type == "h") {
      values <- ps$x_values
      bwd <- if (length(values) == 1) 2 * bwd else bwd * min(diff(values))
      return(c(min(values) - bwd / 2, max(values) + bwd / 2))
   }

   # if excluded values must be shown - correct internal limits
   xlim <- ps$xlim

   # correct if limits are equal
   if (diff(xlim) == 0) xlim <- xlim * c(0.95, 1.05)

   if (show.excluded && !is.null(ps$x_values_excluded)) {
      xlim_excluded <- range(ps$x_values_excluded)
      xlim <- c(
         min(xlim[1], xlim_excluded[1], na.rm = TRUE),
         max(xlim[2], xlim_excluded[2], na.rm = TRUE)
      )
   }

   # if labels must be shown increase the upper limit
   if (show.labels) {
      xlim[1] <- xlim[1] - diff(xlim) * 0.05
      xlim[2] <- xlim[2] + diff(xlim) * 0.05
   }

   # find if show.lines is in use
   if (length(show.lines) == 2 && is.numeric(show.lines[1])) {
      xlim <- c(
         min(xlim[1], show.lines[1], na.rm = TRUE),
         max(xlim[2], show.lines[1], na.rm = TRUE)
      )
   }

   # add extra margins (3.5%)
   m <- diff(xlim) * 0.035
   xlim <- xlim + c(-m, m)

   return(xlim)
}

#' Calculate limits for y-axis.
#'
#' @description
#' Calculates limits for y-axis depending on data values that have to be plotted,
#' extra plot elements that have to be shown and margins.
#'
#' @param ps
#' `plotseries` object.
#' @param ylim
#' limits provided by user
#' @param show.lines
#' logical or numeric with line coordinates to be shown on the plot.
#' @param show.excluded
#' logical, will excluded values be shown on the plot
#' @param show.labels
#' logical, will data labels be shown on the plot
#' @param show.colorbar
#' logical, will colorbar be shown on the plot
#'
#' @return
#' Returns a vector with two limits.
#'
mdaplot.getYAxisLim <- function(ps, ylim, show.lines = FALSE, show.excluded = FALSE,
   show.labels = FALSE, show.colorbar = FALSE) {

   # if user provided limits for y - use them
   if (!is.null(ylim)) return(ylim)

   # get computed data limits
   ylim <- ps$ylim

   # correct if limits are equal
   if (diff(ylim) == 0) ylim <- ylim * c(0.95, 1.05)

   # if excluded values must be shown - correct internal limits
   if (show.excluded && !is.null(ps$y_values_excluded)) {
      ylim_excluded <- range(ps$y_values_excluded)
      ylim <- c(
         min(ylim[1], ylim_excluded[1], na.rm = TRUE),
         max(ylim[2], ylim_excluded[2], na.rm = TRUE)
      )
   }

   # if labels must be shown increase the upper limit
   if (show.labels) {
      ylim[2] <- ylim[2] + diff(ylim) * 0.075
   }

   # if it is a bar plot and some bars look "down" correct the bottom limit as well
   if (show.labels && ps$type == "h" && any(ps$y_values < 0)) {
      ylim[1] <- ylim[1] - diff(ylim) * 0.05
   }

   # find if show.lines is in use
   if (length(show.lines) == 2 && is.numeric(show.lines[2])) {
      ylim <- c(
         min(ylim[1], show.lines[2], na.rm = TRUE),
         max(ylim[2], show.lines[2], na.rm = TRUE)
      )
   }

   # add an extra margin to y limit if colorbar must be shown
   if (show.colorbar) {
      ylim[2] <- ylim[2] + diff(ylim) * 0.20
   }

   # add extra margins (3.5%)
   m <- diff(ylim) * 0.035
   ylim <- ylim + c(-m, m)
   return(ylim)
}

#' Prepare xticks for plot
#'
#' @param xticks
#' xticks provided by user (if any)
#' @param xlim
#' limits for x axis
#' @param x_values
#' x values for the plot data object
#' @param type
#' type of the plot
#'
#' @export
mdaplot.getXTicks <- function(xticks, xlim, x_values = NULL, type = NULL) {

   if (!is.null(xticks)) return(xticks)
   if (type != "p" && length(x_values) == 1) return(1)
   return(axisTicks(xlim, log = FALSE))
}

#' Prepare yticks for plot
#'
#' @param yticks
#' yticks provided by user (if any)
#' @param ylim
#' limits for y axis
#' @param y_values
#' y values for the plot data object
#' @param type
#' type of the plot
#'
#' @export
mdaplot.getYTicks <- function(yticks, ylim, y_values = NULL, type = NULL) {

   if (!is.null(yticks)) return(yticks)
   if (type != "p" && length(y_values) == 1) return(1)

   return(axisTicks(ylim, log = FALSE))
}

#' Prepare xticklabels for plot
#'
#' @param xticklabels
#' xticklables provided by user (if any)
#' @param xticks
#' xticks (provided or computed)
#' @param excluded_cols
#' columns excluded from plot data (if any)
#'
#' @export
mdaplot.getXTickLabels <- function(xticklabels, xticks, excluded_cols) {

   if (is.null(xticklabels)) return(TRUE)
   if (is.null(xticks)) stop("You need to specify both 'xticklabels' and 'xticks'")

   # if xticklabels were provided - remove excluded columns if any and check the length
   if (!is.null(excluded_cols)) xticklabels <- xticklabels[-excluded_cols]
   if (length(xticks) != length(xticklabels)) {
      stop('Number of elements in "xticks" and "xticklabels" should be the same')
   }

   return(xticklabels)
}

#' Prepare yticklabels for plot
#'
#' @param yticklabels
#' yticklables provided by user (if any)
#' @param yticks
#' yticks (provided or computed)
#' @param excluded_rows
#' rows excluded from plot data (if any)
#'
#' @export
mdaplot.getYTickLabels <- function(yticklabels, yticks, excluded_rows) {

   if (is.null(yticklabels)) return(TRUE)
   if (is.null(yticks)) stop("You need to specify both 'yticklabels' and 'yticks'")

   # if yticklabels were provided - remove excluded rows if any and check the length
   if (length(yticks) != length(yticklabels)) {
      stop('Number of elements in "yticks" and "yticklabels" should be the same')
   }

   return(yticklabels)
}

#' Create axes plane
#'
#' @description
#' Creates an empty axes plane for given parameters
#'
#' @param xticklabels
#' labels for x ticks
#' @param yticklabels
#' labels for y ticks
#' @param xticks
#' values for x ticks
#' @param yticks
#' values for y ticks
#' @param xlim
#' vector with limits for x axis
#' @param ylim
#' vector with limits for y axis
#' @param main
#' main title for the plot
#' @param xlab
#' label for x axis
#' @param ylab
#' label for y axis
#' @param xlas
#' orientation of xticklabels
#' @param ylas
#' orientation of yticklabels
#' @param show.grid
#' logical, show or not axes grid
#' @param grid.lwd
#' line thinckness (width) for the grid
#' @param grid.col
#' line color for the grid
#'
mdaplot.plotAxes <- function(xticklabels = NULL, yticklabels = NULL,
   xlim = xlim, ylim = ylim, xticks = NULL, yticks = NULL, main = NULL, xlab = NULL, ylab = NULL,
   xlas = 0, ylas = 0, show.grid = TRUE, grid.lwd = 0.5, grid.col = "lightgray") {

   # make plot without ticks
   plot(0, 0, type = "n", main = main, xlab = xlab, ylab = ylab, xlim = xlim, ylim = ylim,
        xaxt = "n", yaxt = "n")

   # generate x and y ticks
   if (is.null(xticks)) xticks <- axisTicks(xlim, log = FALSE)
   if (is.null(yticks)) yticks <- axisTicks(ylim, log = FALSE)

   # show x-axis
   if (is.null(xticklabels)) xticklabels <- TRUE
   axis(1, at = xticks, labels = xticklabels, las = xlas)

   # show y-axis
   if (is.null(yticklabels)) yticklabels <- TRUE
   axis(2, at = yticks, labels = yticklabels, las = ylas)

   # show grid if needed
   if (show.grid) {
      grid(lwd = grid.lwd, col = grid.col)
   }
}

#' Plotting function for a single set of objects
#'
#' @description
#' \code{mdaplot} is used to make different kinds of plot for one set of data objects.
#'
#' @param data
#' a vector, matrix or a data.frame with data values.
#' @param ps
#' `plotseries` object, if NULL will be created based on the provided data values
#' @param type
#' type of the plot ("p", "d", "l", "b", "h", "e").
#' @param cgroup
#' a vector with values to use for make color groups.
#' @param colmap
#' a colormap to use for coloring the plot items.
#' @param pch
#' a character for markers (same as \code{plot} parameter).
#' @param col
#' a color for markers or lines (same as \code{plot} parameter).
#' @param bg
#' background color for scatter plots wich `pch=21:25`.
#' @param bwd
#' a width of a bar as a percent of a maximum space available for each bar.
#' @param border
#' color for border of bars (if barplot is used)
#' @param lty
#' line type
#' @param lwd
#' line width
#' @param cex
#' scale factor for the marker
#' @param xlim
#' limits for the x axis (if NULL, will be calculated automatically).
#' @param ylim
#' limits for the y axis (if NULL, will be calculated automatically).
#' @param xlab
#' a title for the x axis (same as \code{plot} parameter).
#' @param ylab
#' a title for the y axis (same as \code{plot} parameter).
#' @param main
#' an overall title for the plot (same as \code{plot} parameter).
#' @param labels
#' a vector with text labels for data points or one of the following: "names", "indices", "values".
#' @param show.labels
#' logical, show or not labels for the data objects.
#' @param show.colorbar
#' logical, show or not colorbar legend if color grouping is on.
#' @param show.lines
#' vector with two coordinates (x, y) to show horizontal and vertical line cross the point.
#' @param show.grid
#' logical, show or not a grid for the plot.
#' @param grid.lwd
#' line thinckness (width) for the grid.
#' @param grid.col
#' line color for the grid.
#' @param show.axes
#' logical, make a normal plot or show only elements (markers, lines, bars) without axes.
#' @param xticks
#' values for x ticks.
#' @param yticks
#' values for y ticks.
#' @param xticklabels
#' labels for x ticks.
#' @param yticklabels
#' labels for y ticks.
#' @param xlas
#' orientation of xticklabels.
#' @param ylas
#' orientation of yticklabels.
#' @param lab.col
#' color for data point labels.
#' @param lab.cex
#' size for data point labels.
#' @param show.excluded
#' logical, show or hide rows marked as excluded (attribute `exclrows`).
#' @param col.excluded
#' color for the excluded objects (rows).
#' @param nbins
#' if scatter density plot is shown, number of segments to split the plot area into.
#' (see also ?smoothScatter)
#' @param force.x.values
#' vector with corrected x-values for a bar plot (do not specify this manually).
#' @param opacity
#' opacity for plot colors (value between 0 and 1).
#' @param pch.colinv
#' allows to swap values for `col` and `bg` for scatter plots with `pch` valyes from 21 to 25.
#' @param ...
#' other plotting arguments.
#'
#' @details
#' Most of the parameters are similar to what are used with standard \code{plot} function. The
#' differences are described below.
#'
#' The function makes a plot of one set of objects. It can be a set of points (scatter plot),
#' bars, lines, scatter-lines, errorbars og an image. The data is organized as a data frame,
#' matrix or vector. For scatter and only first two columns will be used, for bar plot only
#' values from the first row. It is recommended to use \code{\link{mda.subset}} method if plot
#' should be made only for a subset of the data, especially if you have any excluded rows or
#' columns or other special attributed, described in the Bookdown tutorial.
#'
#' If data is a data frame and contains one or more factors, they will be converted to a dummy
#' variables (using function \code{\link{mda.df2mat}}) and appears at the end (last columns) if
#' line or bar plot is selected.
#'
#' The function allows to colorize lines and points according to values of a parameter
#' \code{cgroup}. The parameter must be a vector with the same elements as number of objects (rows)
#' in the data. The values are divided into up to eight intervals and for each interval a
#' particular color from a selected color scheme is assigned. Parameter \code{show.colorbar}
#' allows to turn off and on a color bar legend for this option.
#'
#' The used color scheme is defined by the \code{colmap} parameter. The default scheme is based
#' on color brewer (colorbrewer2.org) diverging scheme with eight colors. There is also a gray
#' scheme (\code{colmap = "gray"}) and user can define its own just by specifing the needed
#' sequence of colors (e.g. \code{colmap = c("red", "yellow", "green")}, two colors is minimum).
#' The scheme will then be generated automatically as a gradient among the colors.
#'
#' Besides that the function allows to change tick values and corresponding tick labels for x and
#' y axis, see Bookdown tutorial for more details.
#'
#' @author
#' Sergey Kucheryavskiy (svkucheryavski@@gmail.com)
#'
#' @seealso
#' \code{\link{mdaplotg}} - to make plots for several sets of data objects (groups of objects).
#'
#' @examples
#' # See all examples in the tutorial.
#'
#' @importFrom grDevices axisTicks dev.cur
#' @importFrom graphics abline axis grid hist image lines matlines par
#' @importFrom graphics plot plot.new points rasterImage rect segments text mtext
#'
#' @export
mdaplot <- function(data = NULL, ps = NULL, type = "p",
   pch = 16, col = NULL, bg = par("bg"), bwd = 0.8, border = NA, lty = 1, lwd = 1, cex = 1,
   cgroup = NULL, xlim = NULL, ylim = NULL, colmap = "default", labels = NULL,
   main = NULL, xlab = NULL, ylab = NULL, show.labels = FALSE,
   show.colorbar = !is.null(cgroup), show.lines = FALSE, show.grid = TRUE, grid.lwd = 0.5,
   grid.col = "lightgray", show.axes = TRUE, xticks = NULL, yticks = NULL,
   xticklabels = NULL, yticklabels = NULL,
   xlas = 0, ylas = 0, lab.col = "darkgray", lab.cex = 0.65,
   show.excluded = FALSE, col.excluded = "#C0C0C0", nbins = 60,
   force.x.values = NA, opacity = 1,
   pch.colinv = FALSE, ...) {

   if (is.null(ps)) {
      # get the data for plot
      ps <- plotseries(data, type, col = col, cgroup = cgroup, colmap = colmap, labels = labels,
         opacity = opacity)
   }

   # if cgroup is NULL for plot data - color grouping is not allowed
   if (is.null(ps$cgroup)) {
      show.colorbar <- FALSE
   }

   # show axes if needed
   if (show.axes) {
      # get limits for axes
      xlim <- mdaplot.getXAxisLim(ps, xlim = xlim, show.labels = show.labels,
         show.excluded = show.excluded, show.lines = show.lines,
         bwd = (if (type == "h") bwd else NULL))

      ylim <- mdaplot.getYAxisLim(ps, ylim = ylim, show.excluded = show.excluded,
         show.lines = show.lines, show.labels = show.labels, show.colorbar = show.colorbar)

      # check and prepare xticklabels
      xticklabels <- mdaplot.getXTickLabels(xticklabels, xticks, ps$excluded_cols)
      xticks <- mdaplot.getXTicks(xticks, xlim, ps$x_values, type)

      # check and prepare yticklabels
      yticklabels <- mdaplot.getYTickLabels(yticklabels, yticks, ps$excluded_rows)
      yticks <- mdaplot.getYTicks(yticks, ylim, ps$y_values, type)

      # define title and labels
      if (is.null(main)) main <- ps$name
      if (is.null(xlab)) xlab <- attr(ps$x_values, "name")
      if (is.null(ylab)) ylab <- attr(ps$y_values, "name")

      # make an empty plot with proper limits and axis labels
      mdaplot.plotAxes(xticklabels = xticklabels, yticklabels = yticklabels, xticks = xticks,
         yticks = yticks, xlim = xlim, ylim = ylim, main = main, xlab = xlab, ylab = ylab,
         xlas = xlas, ylas = ylas, show.grid = show.grid, grid.lwd = grid.lwd, grid.col = grid.col
      )
   }

   # make plot for the data
   switch(type,
      "p" = plotScatter(ps, pch.colinv = pch.colinv, pch = pch, bg = bg, lwd = lwd, cex = cex,
         col.excluded = col.excluded, show.excluded = show.excluded, ...),
      "d" = plotDensity(ps, nbins = nbins, colmap = colmap),
      "l" = plotLines(ps, pch = pch, lwd = lwd, lty = lty, cex = cex, show.excluded = show.excluded,
         col.excluded = col.excluded, ...),
      "b" = plotLines(ps, pch = pch, lwd = lwd, cex = cex, show.excluded = show.excluded,
         col.excluded = col.excluded, ...),
      "h" = plotBars(ps, bwd = bwd, border = border, force.x.values = force.x.values, ...),
      "e" = plotErrorbars(ps, pch = pch, lwd = lwd, cex = cex, ...),
      stop("Wrong plot type.")
   )

   # show lines if needed
   if (is.numeric(show.lines) && length(show.lines) == 2) {
      mdaplot.showLines(show.lines)
   }

   # show lables
   if (show.labels) {
      showLabels(ps, show.excluded = show.excluded, col = lab.col, cex = lab.cex,
         force.x.values = force.x.values, bwd = bwd)
   }

   # show colorbar if needed
   if (show.colorbar) {
      mdaplot.showColorbar(ps$cgroup, ps$colmap, lab.col = lab.col, lab.cex = lab.cex)
   }

   invisible(ps)
}

#' Create line plot with double y-axis
#'
#' @description
#' \code{mdaplotyy} create line plot for two plot series and uses separate y-axis for each.
#'
#' @param data
#' a matrix or a data.frame with two rows of values.
#' @param type
#' type of the plot ("l" or "b").
#' @param col
#' a color for markers or lines (same as \code{plot} parameter) for each series.
#' @param lty
#' line type for each series (two values)
#' @param lwd
#' line width for each series (two values)
#' @param pch
#' a character for markers (same as \code{plot} parameter) for each series (two values).
#' @param cex
#' scale factor for the markers
#' @param xlim
#' limits for the x axis (if NULL, will be calculated automatically).
#' @param ylim
#' limits for the y axis, either list with two vectors (one for each series) or NULL.
#' @param main
#' an overall title for the plot (same as \code{plot} parameter).
#' @param xlab
#' a title for the x axis (same as \code{plot} parameter).
#' @param ylab
#' a title for each of the two y axis (as a vector of two text values).
#' @param labels
#' a vector with text labels for data points or one of the following: "names", "indices", "values".
#' @param show.labels
#' logical, show or not labels for the data objects.
#' @param lab.cex
#' size for data point labels.
#' @param lab.col
#' color for data point labels.
#' @param show.grid
#' logical, show or not a grid for the plot.
#' @param grid.lwd
#' line thinckness (width) for the grid.
#' @param grid.col
#' line color for the grid.
#' @param xticks
#' values for x ticks.
#' @param xticklabels
#' labels for x ticks.
#' @param xlas
#' orientation of xticklabels.
#' @param ylas
#' orientation of yticklabels (will be applied to both y axes).
#' @param show.legend
#' logical show legend with name of each plot series or not
#' @param legend.position
#' position of legend if it must be shown
#' @param legend
#' values for the legend
#' @param ...
#' other plotting arguments.
#'
#' @details
#' This plot has properties both \code{mdaplot} and \code{mdaplotg}, so when you specify color,
#' line properties etc. you have to do it for both plot series.
#'
#' @author
#' Sergey Kucheryavskiy (svkucheryavski@@gmail.com)
#'
#' @seealso
#' \code{\link{mdaplotg}} - to make plots for several sets of data objects (groups of objects).
#'
#' @examples
#' # See all examples in the tutorial.
#'
#' @export
mdaplotyy <- function(data, type = "l", col = mdaplot.getColors(2), lty = c(1, 1),
   lwd = c(1, 1), pch = (if (type == "b") c(16, 16) else c(NA, NA)), cex = 1,
   xlim = NULL, ylim = NULL, main = attr(data, "name"), xlab = attr(data, "xaxis.name"),
   ylab = rownames(data), labels = "values", show.labels = FALSE, lab.cex = 0.65,
   lab.col = "darkgray", show.grid = TRUE, grid.lwd = 0.5, grid.col = "lightgray",
   xticks = NULL, xticklabels = NULL, xlas = 0, ylas = 0, show.legend = TRUE,
   legend.position = "topright", legend = ylab, ...) {

   if (!(type %in% c("l", "b"))) {
      stop("YY line plot can only be made for type 'l' or 'b'.")
   }

   if (nrow(data) != 2) {
      stop("Matrix with two rows is required to make YY line plot.")
   }

   if (length(ylab) != 2) {
      stop("Y-axis label ('ylab') should be specified for both axes.")
   }

   if (length(col) != 2 || length(lty) != 2 || length(lwd) != 2 || length(pch) != 2) {
      stop("Color and line properties should be specified for both series.")
   }

   # create plot series
   ps1 <- plotseries(mda.subset(data, subset = 1), type = type, labels = labels, col = col[1])
   ps2 <- plotseries(mda.subset(data, subset = 2), type = type, labels = labels, col = col[2])

   # get limits for first series
   xlim <- mdaplot.getXAxisLim(ps1, xlim = xlim, show.labels = show.labels)
   ylim1 <- mdaplot.getYAxisLim(ps1, ylim = ylim[[1]], show.labels = show.labels,
      show.colorbar = TRUE)

   # check and prepare xticklabels
   xticklabels <- mdaplot.getXTickLabels(xticklabels, xticks, NULL)
   xticks <- mdaplot.getXTicks(xticks, xlim, ps1$x_values, type)

   # check and prepare yticklabels
   yticklabels <- mdaplot.getYTickLabels(NULL, NULL, NULL)
   yticks <- mdaplot.getYTicks(NULL, ylim1, ps1$y_values, type)

   # define title and labels
   if (is.null(main)) main <- ps1$name

   par(mar = c(5, 5, 5, 5))

   # make an empty plot with proper limits and axis labels
   mdaplot.plotAxes(xticklabels = xticklabels, yticklabels = yticklabels, xticks = xticks,
      yticks = yticks, xlim = xlim, ylim = ylim1, main = main, xlab = xlab, ylab = ylab[1],
      xlas = xlas, ylas = ylas, show.grid = show.grid, grid.lwd = grid.lwd, grid.col = grid.col
   )

   # show first series
   plotLines(ps1, col = col[1], lty = lty[1], lwd = lwd[1], cex = cex, pch = pch[1], ...)
   if (show.labels) {
      showLabels(ps1, show.excluded = FALSE, col = lab.col, cex = lab.cex)
   }

   # second series
   ylim2 <- mdaplot.getYAxisLim(ps2, ylim = ylim[[2]], show.labels = show.labels)
   par(new = TRUE)
   plot(ps2$x_values, ps2$y_values[1, ], type = type, col = col[2], axes = FALSE, xlab = NA,
      xlim = xlim, ylab = NA, ylim = ylim2, lty = lty[2], lwd = lwd[2], cex = cex, pch = pch[2],
      ...)
   axis(side = 4, las = ylas)
   mtext(side = 4, line = 2, ylab[2], cex = 0.9)

   if (show.labels) {
      showLabels(ps2, show.excluded = FALSE, col = lab.col, cex = lab.cex)
   }

   if (show.legend) {
      mdaplotg.showLegend(legend, col = col, pch = pch, lty = lty, lwd = lwd, cex = 0.9,
         position = legend.position)
   }

   par(mar = c(5.1, 4.1, 4.1, 2.1))
}

Try the mdatools package in your browser

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

mdatools documentation built on Aug. 13, 2023, 1:06 a.m.