#' Make a MIC Plate Diagram
#'
#' \code{plot_mic_plate} makes a MIC Plate Diagram using the data supplied. This
#' data frame must contain the columns described below.
#'
#' @details The following columns must be present in the data:
#'
#' \describe{
#' \item{Drug}{3-letter code indicating the drug used for the MIC assay (e.g., 'RIF' or 'STR'). Must have one unique value.}
#' \item{Well}{Well in the microtiter plate (e.g., 'D6')}
#' \item{Pro.or.Des}{Strain information. Either 'Progenitor' or 'Descendant'}
#' \item{Drug.at.Isolation}{Drug used when strain was isolated, either 'None' or 3-letter code (e.g., 'RIF')}
#' \item{Absorbance}{Absorbance value for the well}
#' \item{Concentration}{Concentration of antibiotic present in the well}
#' }
#'
#' Additional columns that can be used:
#'
#' \describe{
#' \item{Section}{BIO 180 Section name (e.g., 'A'). Must have one unique value.}
#' \item{Group}{BIO 180 Group number. Must have one unique value.}
#' \item{Strain}{Strain name (e.g., 'RIF.R.D'). If not present, this will be created using \code{Pro.or.Des} and \code{Drug.at.Isolation}}
#' }
#'
#' @param d A data frame containing absorbance and antibiotic data
#' @param title A title for the plot. If not provided, one will be created using the \code{Section} and \code{Group}.
#' @param subtitle A subtitle for the plot. If not provided, one will be created using the \code{Drug}.
#' @param threshold Value above which values can be trusted. If a value is below the threshold, that well will be highlighted in red (default: \code{0}).
#' @param absorbance_max Value to be set as the maximum possible absorbance. Useful for having consistent greyscale across many plots (default: \code{3}, which is approx. where the spex maxes out).
#' @param show.values Whether or not to display the absorbance values for each well (default: \code{FALSE})
#' @param show.unused Whether or not to display unused wells. (default: \code{FALSE})
#' @param well_size Size of points used to represent each well. You may need to tweak this. (default: \code{13})
#' @param ... Additional arguments (not currently used)
#'
#' @return A \code{\link{ggplot2}} plot object
#' @import ggplot2
#' @export
#'
#' @examples
#' \dontrun{
#' plot_mic_plate(my_mic_data)
#' }
plot_mic_plate <- function(d, title = NULL, subtitle = NULL, threshold = 0, absorbance_max = 3, show.values = FALSE, show.unused = FALSE, well_size = 13, ...) {
assertthat::assert_that(
is.data.frame(d),
nrow(d) > 0,
all(assertthat::has_name(d, c("Drug", "Absorbance", "Well", "Concentration", "Pro.or.Des", "Drug.at.Isolation"))), # TODO: more
length(unique(d$Drug)) == 1,
threshold >= 0
)
if ("Section" %in% names(d)) {
if (length(unique(d$Section)) > 1) {
stop("'Section' column should contain only one value.")
}
}
if ("Group" %in% names(d)) {
if (length(unique(d$Group)) > 1) {
stop("'Group' column should contain only one value.")
}
}
if (!"Strain" %in% names(d)) {
message("'Strain' column not present. Creating Strain names from Pro.or.Des and Drug.at.Isolation.")
d <- dplyr::mutate(d, Strain = strain_name(Pro.or.Des, Drug.at.Isolation))
}
if (!"Row" %in% names(d)) {
message("'Row' column not present. Creating row numbers from Well.")
d <- dplyr::mutate(d, Row = well_row(Well))
}
if (!"Column" %in% names(d)) {
message("'Column' column not present. Creating column numbers from Well.")
d <- dplyr::mutate(d, Column = well_column(Well))
}
if ("Section" %in% names(d)) {
section = unique(d$Section)[[1]]
}
if ("Group" %in% names(d)) {
group = as.integer(unique(d$Group)[[1]])
} else {
group = -99
}
if (is.null(title) && "Section" %in% names(d) && "Group" %in% names(d)) {
title = sprintf("Section %s, Group %d", section, group)
}
if (is.null(subtitle)) {
subtitle = sprintf("%s", unique(d$Drug)[[1]])
}
concentration_labels <- rep("", 8)
concentrations <- d %>%
dplyr::group_by(Row) %>%
dplyr::summarize(Concentration = unique(Concentration)[1]) %>%
dplyr::arrange(Row)
concentration_labels[concentrations$Row] <- concentrations$Concentration
strain_labels <- rep("", 12)
strains <- d %>%
dplyr::group_by(Column) %>%
dplyr::summarize(
Strain = unique(Strain)[1]
) %>%
dplyr::arrange(Column)
strain_labels[strains$Column] <- ifelse(strains$Strain == "NANA", "", strains$Strain)
# Zero out any negative values
d$Absorbance <- pmax(0, d$Absorbance)
d$AboveThreshold <- d$Absorbance >= threshold
p <- ggplot(d, aes(x = Column, y = Row, fill = Absorbance)) +
coord_fixed(ratio = (13/12)/(9/8), xlim = c(0.5, 12.5), ylim = c(0.6, 8.4)) +
scale_x_continuous(
breaks = seq(1, 12),
position = "top",
sec.axis = dup_axis(labels = strain_labels)) +
scale_y_reverse(
breaks = seq(1, 8),
labels = LETTERS[1:8],
sec.axis = dup_axis(labels = concentration_labels, name = "Concentration")
) +
scale_fill_gradient(
limits = c(0, absorbance_max),
low = "#EEEEEE",
high = "#000000",
name = "Absorbance"
) +
theme_microtiter(base_size = 16)
p <- p + labs(title = title, subtitle = subtitle)
# Add points for the data
p <- p + geom_point(aes(color = AboveThreshold), size = well_size, shape = 21) +
scale_color_manual(values = c("TRUE" = "grey70", "FALSE" = "red"), guide = FALSE)
if (show.values) {
p <- p + geom_text(aes(label = substr(Absorbance, 0, 5)), color = "blue", size = 3)
}
# Show wells that are not included in the data set with an X
if (show.unused) {
unused <- expand.grid(Row = 1:8, Column = 1:12) %>%
dplyr::mutate(Well = as.factor(sprintf("%s%d", LETTERS[Row], Column))) %>%
dplyr::anti_join(d, by = "Well")
p <- p + geom_point(data = unused, color = "grey70", fill = "white", size = well_size, shape = 21)
p <- p + geom_point(data = unused, color = "grey70", fill = "grey70", shape = "x", size = 8)
}
p
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.