R/plot-methods.R

Defines functions .fill_spectra .plot.Spectra plot.Spectra

Documented in plot.Spectra

# Default colour scheme for plot
#
# It's from colorbrewer. I use this fixed thing so I
# do not need to add a dep on RColorBrewer, but users are
# encouraged to make use of it in the doc.
#
# Currently the default scheme is defined by:
# library(RcolorBrewer)
# brewer.pal(n=5, name ="Set1")
#
.defaultSpectraColours <- c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00")

#' @title Plots an object inheriting from the Spectra class
#' @name plot-Spectra
#' @rdname plot-Spectra
#' @description The philosophy of this plotting routine is to provide a "quick'n'dirty" way
#' to plot your spectra collection. For advanced visualisations, the use of
#' \code{\link{melt_spectra}} alongside with ggplot2 or lattice is encouraged.
#' 
#' @aliases plot plot.Spectra plot,Spectra,ANY-method 
#' 
#' @usage \method{plot}{Spectra}(x,gg,gaps,attr,\dots)
#' 
#' @param x an object of class \code{"Spectra"} or inheriting from this class
#' @param gg if TRUE, uses the \code{ggplot2} package to plot the data, if
#' FALSE uses \code{matplot} from base graphics (much faster)
#' @param gaps if TRUE, gaps in the spectra are not plotted
#' @param attr attribute against which lines are coloured (only for \code{gg =
#' TRUE}
#' @param ... additional parameters passed to \code{matplot}
#' @author Pierre Roudier \email{pierre.roudier@@gmail.com}
#' @examples
#' 
#' # Loading example data
#' data(australia)
#' spectra(australia) <- sr_no ~ ... ~ 350:2500
#' 
#' # Default plotting method
#' plot(australia[1:5,])
#' 
#' # Default plot using ggplot2
#' plot(australia[1:5,], gg = TRUE)
#' 
#' \dontrun{
#' 
#' # Managing gaps in the spectra
#' s <- cut(australia, wl =c(-1*450:500, -1*1800:2050))
#' plot(s, gaps = TRUE)
#' plot(s, gaps = FALSE)
#' 
#' # passing various options to matplot
#' plot(
#'   australia, 
#'   lty = 1:5, 
#'   col = 'blue', 
#'   xlab = 'foo', ylab = 'bar', 
#'   ylim = c(0.4,0.6), 
#'   main = 'my plot'
#' )
#' 
#' # Using colour ramps
#' plot(
#'   australia, 
#'   lty = 1, 
#'   col = rainbow(10), 
#'   main = "It is possible to create really ugly visualisations"
#' )
#' 
#' # Example using colours given by ColorBrewer (http://colorbrewer2.org/)
#' library(RColorBrewer)
#' plot(australia, lty = 1, col = brewer.pal(n = 8, name = "Set2"))
#' 
#' # Using an attribute to group spectra
#' 
#' # Generate some kind of factor
#' australia$fact <- sample(
#'   LETTERS[1:3], 
#'   size = nrow(australia), 
#'   replace = TRUE
#' ) 
#' 
#' s <- aggregate_spectra(australia, fun = mean, id = 'fact')
#' plot(s, gg = TRUE, attr = 'fact')
#' }
NULL

#' @rdname plot-Spectra
#' @export plot
plot.Spectra <- function(x, gg = FALSE, gaps = TRUE, attr = NULL, ...) .plot.Spectra(x, gg, gaps, attr, ...)

.plot.Spectra <- function(x, gg = FALSE, gaps = TRUE, attr = NULL, ...){

  # Show gaps in the data?
  if (gaps) {
    x <- fill_spectra(x, fill = NA, ...)
  }

  if (gg) {
    
    if (is.null(attr)) s.melt <- melt_spectra(x)
    else s.melt <- melt_spectra(x, attr = attr)

    # force id colname
    names(s.melt)[1] <- 'id'

    p <- ggplot(s.melt) 

    if (is.null(attr)) {
      p <- p + geom_line(aes_string(x = 'wl', y = 'nir', group = 'id'))
    }
    else {
      p <- p + geom_line(aes_string(x = 'wl', y = 'nir', group = 'id', colour = attr))
    }
    p <- p +
      labs(x = paste("Wavelength (", wl_units(x), ")", sep = ""), y = "Value") +
      theme_bw()
    return(p)
  }
  else {
    # Fast implenmentation using matplot

    # inspect dots to check matplot args: type, lty, ylab, xlab, ylim
    dots <- list(...)
    nm_dts <- names(dots)

    # insert default values if no matplot args given by user
    if (!("type" %in% nm_dts)) dots$type <- 'l'
    if (!("lty" %in% nm_dts)) dots$lty <- 1
    if (!("ylab" %in% nm_dts)) dots$ylab <- "Value"
    if (!("xlab" %in% nm_dts)) dots$xlab <- paste("Wavelength (", wl_units(x), ")", sep = "")
    if (!("xlim" %in% nm_dts)) dots$xlim <- range(wl(x))
    if (!("ylim" %in% nm_dts)) dots$ylim <- range(spectra(x), na.rm = TRUE, finite = TRUE)
    if (!("col" %in% nm_dts)) dots$col <- .defaultSpectraColours
    
    # insert x and y values 
    dots$x <- wl(x)
    dots$y <- t(spectra(x))
    
    do.call("matplot", dots)
  }
}

# if (!isGeneric("plot_summary"))
    setGeneric("plot_summary", function(x, fun = mean, se = TRUE, ...)
        standardGeneric("plot_summary"))

#' @title Summary plot of a collection of spectra
#' @aliases plot_summary plot_summary,Spectra-method
#' @name plot_summary
#' @description Creates a summary plot of a collection of Spectra
#' @usage plot_summary(x, fun = mean, se = TRUE, ...)
#' @param x an object of class \code{"Spectra"} or inheriting from this class
#' @param fun an aggregation function
#' @param se if TRUE, plots the standard deviation around the summary spectra
#' (computed by function as given by \code{fun}). If FALSE, does not plot
#' dispersion information. If a function, uses this function instead of
#' \code{sd}.
#' @param ... additional parameters, currently ignored
#' @author Pierre Roudier
#' @examples 
#'  oz <- load_oz()
#'  plot_summary(oz)
setMethod("plot_summary", signature('Spectra'), 
  function(x, fun = mean, se = TRUE, ...) {

        # If sd is given as TRUE or FALSE
    if (is.logical(se)) {
      if (se) {
        plot.se <- TRUE
        fun.se <- sd
      }
      else {
        plot.se <- FALSE
      }
    }
    # If sd is being given a function
    else {
      # If the function is valid
      if (is.function(se)) {
        plot.se <- TRUE
        fun.se <- se
      }
      # Else stop
      else {
        stop('The se = ... option must evaluate to either logical or function.')
      }
    }

    s.melt <- melt_spectra(x)
    
  #   s.summary <- ddply(s.melt, 'wl', fun, ...)
    s.summary <- ddply(s.melt, 'wl', function(x) {do.call(fun, list(x$nir))})
    names(s.summary)[2] <- 'nir'

    if (plot.se) {
      # initiate dummy vars to pas R CMD check
      wl <- nir <- nir_se <- NULL

      s.se <- ddply(s.melt, 'wl', function(x) {do.call(fun.se, list(x$nir))})
      names(s.se)[2] <- 'nir_se'
      
      s <- join(s.summary, s.se, by = 'wl')

      p <- ggplot() + 
        geom_line(data = s, aes(x = wl, y = nir - nir_se), linetype = 2) +
        geom_line(data = s, aes(x = wl, y = nir + nir_se), linetype = 2) +
        geom_line(data = s, aes(x = wl, y = nir)) +
        labs(x = paste("Wavelength (", wl_units(x), ")", sep = ""), y = "Value") +
        theme_bw()
    } 
    else {
      p <- ggplot() + 
        geom_line(data = s.summary, aes(x = wl, y = nir)) +
        labs(x = paste("Wavelength (", wl_units(x), ")", sep = ""), y = "Value") +
        theme_bw()
    }

    p
  }
)

# if (!isGeneric("plot_stack"))
    setGeneric("plot_stack", function(x)
        standardGeneric("plot_stack"))

#' @title Stacked plot of a collection of spectra
#' @name plot_stack
#' @aliases plot_stack plot_stack,Spectra-method
#' @description Creates a stacked plot of a collection of Spectra
#' @usage plot_stack(x)
#' @param x an object of class \code{"Spectra"} or inheriting from this class
#' @author Pierre Roudier
#' @examples 
#'  oz <- load_oz(3)
#'  plot_stack(oz)
setMethod("plot_stack", signature('Spectra'), 
  function(x){
    
    m <- melt_spectra(x)
    idnm <- names(m)[1]
    m[[idnm]] <- as.factor(m[[idnm]])
    form_grid <- as.formula(paste(idnm, '~.'))
    ggplot(m) + 
      geom_line(aes_string(x = 'wl', y = 'nir', colour = idnm)) + 
      facet_grid(form_grid) + 
      labs(x = paste("Wavelength (", wl_units(x), ")", sep = ""), y = "Value") +
      theme_bw()
  }
)

# if (!isGeneric("plot_offset"))
    setGeneric("plot_offset", function(x, offset = 1)
        standardGeneric("plot_offset"))

#' @title Offset plot of a collection of spectra
#' @name plot_offset
#' @aliases plot_offset plot_offset,Spectra-method
#' @description Creates an offset plot of a collection of Spectra
#' @usage plot_offset(x, offset = 1)
#' @param x an object of class \code{"Spectra"} or inheriting from this class
#' @param offset Offset between spectra
#' @author Pierre Roudier
#' @examples 
#'  oz <- load_oz(3)
#'  plot_offset(oz)
#'  plot_offset(oz, 0.3)
#'  plot_offset(oz, 2)
setMethod("plot_offset", signature('Spectra'), 
  function(x, offset = 1){
    
    # offsets values
    offsets <- (seq_len(nrow(x)) - 1) * offset
    
    # affect spectra with offset values
    spectra(x) <- aaply(1:length(offsets), 1, function(i) {
      spectra(x)[i,] + offsets[i]
    })
    
    # melt spectra for data visualisation with ggplot2
    m <- melt_spectra(x)
    
    idnm <- names(m)[1]
    m[[idnm]] <- as.factor(m[[idnm]])
    
    ggplot(m) + 
      geom_line(aes_string(x = 'wl', y = 'nir', colour = idnm, group = idnm)) +
      labs(x = paste("Wavelength (", wl_units(x), ")", sep = ""), y = "") +
      theme_bw()

  }
)

## Code for adding NAs to potentially removed WLs
##
# ref reference wavelengths
# fill value to fill missing WLs with
#



# if (!isGeneric("fill_spectra")) 
  setGeneric("fill_spectra", function(obj, ...)
    standardGeneric("fill_spectra"))

.fill_spectra <- function(obj, ref = NULL, fill = NA, ...) {

  if (is.null(ref)) {
    # Trying to get the most common resolution values
    r <- as.numeric(names(which.max(table(diff(wl(obj))))))
    nb_gaps <- length(table(diff(wl(obj))))
    if (nb_gaps > 2)
      warning("Sorry, at this stage removing gaps does not work well with irreguarly spaced wavelengths. Results might be odd.")
    ref <- seq(from = min(wl(obj)), to = max(wl(obj)), by = r)
  }
  
  # Detect missing WLs
  missing_wl <- setdiff(ref, wl(obj))

  # If tehre is gaps in the data, we add these as NAs values
  if (length(missing_wl) > 0) {
    # Create matrix of NAs for the missing WLs
    new_nir <- matrix(fill, ncol = length(missing_wl), nrow = nrow(obj))
    colnames(new_nir) <- missing_wl
    
    # Collate the NA matrix with the rest of the spectra
    new_nir <- cbind(spectra(obj), new_nir)
    # Re-order the spectra matrix
    idx <- order(as.numeric(colnames(new_nir)))
    new_nir <- new_nir[, idx, drop = FALSE]
    
    spectra(obj) <- new_nir
  }

  obj
}

#' @title Fill missing wavelengths of a Spectra* object with a given value
#' @name fill_spectra
#' @aliases fill_spectra fill_spectra,Spectra-method
#' @description Fill missing wavelengths of a Spectra* object with a given value. This is
#' mostly usefull to include NA values in the spectra in order to show missing
#' bits in plots.
#' 
#' @details At this stage removing gaps does not work well with irreguarly spaced
#' wavelengths. Results might be odd for binned spectra.
#' 
#' @param obj an object inheriting from class \code{Spectra}
#' @param ref a numeric vector, giving the reference wavelengths (ie the entire
#' range of wavelengths expected to be in the spectra before some waveleng5ths
#' have been cut out). If NULL, the function is trying to guess it.
#' @param fill values to fill gaps in the data with
#' @param ... ignored
#' @return An object of the same class as \code{obj}
#' @author Pierre Roudier \email{pierre.roudier@@gmail.com}
#' @examples
#' 
#' # Loading example data
#' data(australia)
#' spectra(australia) <- sr_no ~ ... ~ 350:2500
#' 
#' # Cut wavelengths out of the collection
#' oz <- cut(australia, wl=-1*c(355:400, 2480:2499))
#' big.head(spectra(oz), , 7)
#' 
#' # Giving the wavelengths at which I want data
#' oz_filled <- fill_spectra(oz, ref = 350:2500, fill = NA)
#' big.head(spectra(oz_filled), , 7)
#' plot(oz_filled)
#' 
#' # Trying to guess ref values
#' oz_filled <- fill_spectra(oz, fill = -999)
#' big.head(spectra(oz_filled), , 7)
#' plot(oz_filled)
#' 
#' @export fill_spectra
setMethod("fill_spectra", signature('Spectra'), .fill_spectra)

Try the spectacles package in your browser

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

spectacles documentation built on July 10, 2023, 1:59 a.m.