#' Create Bar Plots.
#'
#' @inheritParams doc_function
#' @param group.by \strong{\code{\link[base]{character}}} | Metadata column to compute the counts of. Has to be either a character or factor column.
#' @param split.by \strong{\code{\link[base]{character}}} | Metadata column to split the values of group.by by. If not used, defaults to the active idents.
#' @param facet.by \strong{\code{\link[base]{character}}} | Metadata column to gather the columns by. This is useful if you have other overarching metadata.
#' @param order \strong{\code{\link[base]{logical}}} | Whether to order the results in descending order of counts.
#' @param order.by \strong{\code{\link[base]{character}}} | When \strong{\code{split.by}} is used, value of \strong{\code{group.by}} to reorder the columns based on its value.
#' @param position \strong{\code{\link[base]{character}}} | Position function from \pkg{ggplot2}. Either stack or fill.
#' @param return_data \strong{\code{\link[base]{logical}}} | Returns a data.frame with the count and proportions displayed in the plot.
#' @param add.n \strong{\code{\link[base]{logical}}} | Whether to add the total counts on top of each bar.
#' @param add.n.face \strong{\code{\link[base]{character}}} | Font face of the labels added by \strong{\code{add.n}}.
#' @param add.n.size \strong{\code{\link[base]{numeric}}} | Size of the labels
#' @param add.n.expand \strong{\code{\link[base]{numeric}}} | Vector of two numerics representing the start and end of the scale. Minimum should be 0 and max should be above 1. This basically expands the Y axis so that the labels fit when \strong{\code{flip = TRUE}}.
#' \itemize{
#' \item \emph{\code{stack}}: Set the bars side by side, displaying the total number of counts. Uses \link[ggplot2]{position_stack}.
#' \item \emph{\code{fill}}: Set the bars on top of each other, displaying the proportion of counts from the total that each group represents. Uses \link[ggplot2]{position_fill}.
#' }
#' @return A ggplot2 object containing a Bar plot.
#' @export
#'
#' @example /man/examples/examples_do_BarPlot.R
do_BarPlot <- function(sample,
group.by,
order = FALSE,
add.n = FALSE,
add.n.face = "bold",
add.n.expand = c(0, 1.15),
add.n.size = 4,
order.by = NULL,
split.by = NULL,
facet.by = NULL,
position = "stack",
font.size = 14,
font.type = "sans",
legend.position = "bottom",
legend.title = NULL,
legend.ncol = NULL,
legend.nrow = NULL,
legend.byrow = FALSE,
axis.text.x.angle = 45,
xlab = NULL,
ylab = NULL,
colors.use = NULL,
flip = FALSE,
plot.title = NULL,
plot.subtitle = NULL,
plot.caption = NULL,
plot.grid = FALSE,
grid.color = "grey75",
grid.type = "dashed",
plot.title.face = "bold",
plot.subtitle.face = "plain",
plot.caption.face = "italic",
axis.title.face = "bold",
axis.text.face = "plain",
legend.title.face = "bold",
legend.text.face = "plain",
strip.text.face = "bold",
return_data = FALSE) {
# Add lengthy error messages.
withr::local_options(.new = list("warning.length" = 8170))
check_suggests(function_name = "do_BarPlot")
check_Seurat(sample)
`%>%` <- magrittr::`%>%`
`:=` <- rlang::`:=`
# Check logical parameters.
logical_list <- list("order" = order,
"flip" = flip,
"plot.grid" = plot.grid,
"legend.byrow" = legend.byrow,
"add.n" = add.n,
"return_data" = return_data)
check_type(parameters = logical_list, required_type = "logical", test_function = is.logical)
# Check numeric parameters.
numeric_list <- list("font.size" = font.size,
"axis.text.x.angle" = axis.text.x.angle,
"legend.ncol" = legend.ncol,
"legend.nrow" = legend.nrow,
"add.n.expand" = add.n.expand)
check_type(parameters = numeric_list, required_type = "numeric", test_function = is.numeric)
# Check character parameters.
character_list <- list("group.by" = group.by,
"split.by" = split.by,
"facet.by" = facet.by,
"order.by" = order.by,
"position" = position,
"font.type" = font.type,
"legend.position" = legend.position,
"legend.title" = legend.title,
"xlab" = xlab,
"ylab" = ylab,
"plot.title" = plot.title,
"plot.subtitle" = plot.subtitle,
"plot.caption" = plot.caption,
"grid.color" = grid.color,
"grid.type" = grid.type,
"legend.title" = legend.title,
"plot.title.face" = plot.title.face,
"plot.subtitle.face" = plot.subtitle.face,
"plot.caption.face" = plot.caption.face,
"axis.title.face" = axis.title.face,
"axis.text.face" = axis.text.face,
"legend.title.face" = legend.title.face,
"legend.text.face" = legend.text.face)
# Checks
check_type(parameters = character_list, required_type = "character", test_function = is.character)
check_colors(grid.color, parameter_name = "grid.color")
check_parameters(parameter = font.type, parameter_name = "font.type")
check_parameters(parameter = legend.position, parameter_name = "legend.position")
check_parameters(parameter = grid.type, parameter_name = "grid.type")
check_parameters(parameter = axis.text.x.angle, parameter_name = "axis.text.x.angle")
check_parameters(plot.title.face, parameter_name = "plot.title.face")
check_parameters(plot.subtitle.face, parameter_name = "plot.subtitle.face")
check_parameters(plot.caption.face, parameter_name = "plot.caption.face")
check_parameters(axis.title.face, parameter_name = "axis.title.face")
check_parameters(axis.text.face, parameter_name = "axis.text.face")
check_parameters(legend.title.face, parameter_name = "legend.title.face")
check_parameters(legend.text.face, parameter_name = "legend.text.face")
check_parameters(strip.text.face, parameter_name = "strip.text.face")
# Get the general table.
assertthat::assert_that(class(sample@meta.data[, group.by]) %in% c("character", "factor"),
msg = paste0(add_cross(), crayon_body("Please provide to "),
crayon_key("feature"),
crayon_body(" a "),
crayon_key(" metadta categorical "),
crayon_body(" variable.")))
assertthat::assert_that(base::isFALSE(position == "fill" & is.null(split.by)),
msg = paste0(add_cross(),
crayon_body("Please use "),
crayon_key("position = fill"),
crayon_body(" alongisde "),
crayon_key("split.by"),
crayon_body(".")))
assertthat::assert_that(base::isFALSE(position == "stack" & isTRUE(order) & !is.null(order.by)),
msg = paste0(add_cross(),
crayon_body("Please use "),
crayon_key("order.by"),
crayon_body(" alongisde "),
crayon_key("position = fill"),
crayon_body(".")))
assertthat::assert_that(base::isFALSE(position == "fill" & isTRUE(order) & is.null(order.by)),
msg = paste0(add_cross(),
crayon_body("Please use "),
crayon_key("order.by"),
crayon_body(" alongisde "),
crayon_key("position = fill"),
crayon_body(".")))
if (is.null(colors.use)){
colors.use <- generate_color_scale(names_use = if (is.factor(sample@meta.data[, group.by])) {levels(sample@meta.data[, group.by])} else {sort(unique(sample@meta.data[, group.by]))})
} else {
check_colors(colors.use, parameter_name = "colors.use")
check_consistency_colors_and_names(sample = sample, colors = colors.use, grouping_variable = group.by)
colors.use <- colors.use[unique(sample@meta.data[, group.by])]
}
# Check group.by.
out <- check_group_by(sample = sample,
group.by = group.by,
is.heatmap = FALSE)
sample <- out[["sample"]]
group.by <- out[["group.by"]]
data <- sample@meta.data %>%
tibble::as_tibble() %>%
dplyr::select(dplyr::all_of(c(group.by, split.by, facet.by)))
if (isTRUE(order)){
if (is.null(order.by)){
order.use <- sample@meta.data %>%
tibble::as_tibble() %>%
dplyr::select(dplyr::all_of(c(group.by, split.by))) %>%
dplyr::group_by(.data[[group.by]]) %>%
dplyr::summarise("n" = dplyr::n()) %>%
dplyr::arrange(if(base::isFALSE(flip)){dplyr::desc(.data[["n"]])} else {.data[["n"]]}) %>%
dplyr::pull(.data[[group.by]]) %>%
as.character()
data <- data %>%
dplyr::mutate("{group.by}" := factor(as.character(.data[[group.by]]),
levels = order.use))
} else {
order.use <- sample@meta.data %>%
tibble::as_tibble() %>%
dplyr::mutate(dplyr::across(dplyr::all_of(c(group.by, split.by)), as.character),
"count" = 1) %>%
tidyr::complete(.data[[split.by]], .data[[group.by]], explicit = FALSE) %>%
dplyr::group_by(.data[[split.by]], .data[[group.by]]) %>%
dplyr::summarise("n" = sum(.data$count, na.rm = TRUE),
"{split.by}" := unique(.data[[split.by]])) %>%
dplyr::reframe("freq" = .data$n / sum(.data$n),
"{split.by}" := unique(.data[[split.by]]),
"{group.by}" := unique(.data[[group.by]])) %>%
dplyr::filter(.data[[group.by]] == order.by) %>%
dplyr::arrange(if(base::isFALSE(flip)){dplyr::desc(.data[["freq"]])} else {.data[["freq"]]}) %>%
dplyr::pull(.data[[split.by]]) %>%
as.character()
data <- data %>%
dplyr::mutate("{split.by}" := factor(as.character(.data[[split.by]]),
levels = order.use))
}
}
if (isTRUE(add.n)){
assertthat::assert_that(position == "fill",
msg = paste0(add_cross(),
crayon_body("Parameter "),
crayon_key("add.n"),
crayon_body(" can only be used alongside "),
crayon_key("position = fill"),
crayon_body(".")))
if (is.null(split.by) & !is.null(group.by)){
data.n <- data %>%
dplyr::group_by(.data[[group.by]]) %>%
dplyr::summarise(n = dplyr::n()) %>%
dplyr::mutate(n = paste0("n = ", .data$n))
} else if (!is.null(split.by) & !is.null(group.by)){
data.n <- data %>%
dplyr::group_by(.data[[split.by]]) %>%
dplyr::summarise(n = dplyr::n()) %>%
dplyr::mutate(n = paste0("n = ", .data$n))
}
max.char <- max(vapply(data.n$n, nchar, FUN.VALUE = integer(1)))
data.n$n <- vapply(data.n$n, function(x){return(paste0(x, paste(rep(" ", (max.char - nchar(x))), collapse = "")))}, FUN.VALUE = character(1))
}
if (is.null(split.by)){
p <- data %>%
ggplot2::ggplot(mapping = ggplot2::aes(x = .data[[group.by]],
fill = .data[[group.by]]))
} else {
p <- data %>%
ggplot2::ggplot(mapping = ggplot2::aes(x = .data[[split.by]],
fill = .data[[group.by]]))
}
if (is.null(xlab)){
if (!is.null(group.by) & is.null(split.by)){
xlab <- group.by
} else if (!is.null(group.by) & !is.null(split.by)){
xlab <- split.by
}
}
if (is.null(ylab)){
ylab <- ifelse(position == "stack", "Count", "Proportion")
}
if (is.null(legend.title)){
if (position == "stack"){
if (is.null(split.by)){
legend.title <- NULL
} else {
legend.title <- split.by
}
} else {
legend.title <- group.by
}
}
p <- p +
ggplot2::stat_count(geom = "bar", position = position, color = "black")
if (isTRUE(add.n)){
if (is.null(split.by) & !is.null(group.by)){
p <- p +
ggplot2::geom_text(data = data.n,
mapping = ggplot2::aes(x = .data[[group.by]],
y = ifelse(base::isFALSE(flip), 1.03, 1.01),
label = .data$n,
fill = NULL),
hjust = ifelse(isTRUE(flip), 0, 0.5),
fontface = "plain",
size = add.n.size)
} else if (!is.null(split.by) & !is.null(group.by)){
p <- p +
ggplot2::geom_text(data = data.n,
mapping = ggplot2::aes(x = .data[[split.by]],
y = ifelse(base::isFALSE(flip), 1.03, 1.01),
label = .data$n,
fill = NULL),
hjust = ifelse(isTRUE(flip), 0, 0.5),
fontface = "plain",
size = add.n.size)
}
p <- p +
ggplot2::scale_y_continuous(limits = add.n.expand, labels = c("0", "0.25", "0.5", "0.75", "1"), breaks = c(0, 0.25, 0.5, 0.75, 1))
}
if (isTRUE(flip)){
p <- p + ggplot2::coord_flip()
}
if (!is.null(facet.by)){
if (base::isFALSE(flip)){
p <- p +
ggplot2::facet_grid(cols = ggplot2::vars(.data[[facet.by]]),
scales = "free",
space = "free",
drop = TRUE)
} else {
p <- p +
ggplot2::facet_grid(rows = ggplot2::vars(.data[[facet.by]]),
scales = "free",
space = "free",
drop = TRUE)
}
}
p <- p +
ggplot2::xlab(xlab) +
ggplot2::ylab(ylab) +
ggplot2::labs(title = plot.title,
subtitle = plot.subtitle,
caption = plot.caption) +
ggplot2::scale_fill_manual(values = colors.use) +
ggplot2::guides(fill = ggplot2::guide_legend(title = legend.title,
title.position = "top",
title.hjust = 0.5,
ncol = legend.ncol,
nrow = legend.nrow,
byrow = legend.byrow)) +
ggplot2::theme_minimal(base_size = font.size) +
ggplot2::theme(axis.title = ggplot2::element_text(color = "black",
face = axis.title.face),
panel.grid.major.y = if (base::isFALSE(flip)) {if (isTRUE(plot.grid)){ggplot2::element_line(color = grid.color, linetype = grid.type)}} else if (isTRUE(flip)) {ggplot2::element_blank()},
panel.grid.major.x = if (isTRUE(flip)) {if (isTRUE(plot.grid)){ggplot2::element_line(color = grid.color, linetype = grid.type)}} else if (base::isFALSE(flip)) {ggplot2::element_blank()},
axis.line.x = if (base::isFALSE(flip)) {ggplot2::element_line(color = "black")} else if (isTRUE(flip)) {ggplot2::element_blank()},
axis.line.y = if (isTRUE(flip)) {ggplot2::element_line(color = "black")} else if (base::isFALSE(flip)) {ggplot2::element_blank()},
axis.text.x = ggplot2::element_text(color = "black",
face = axis.text.face,
angle = get_axis_parameters(angle = axis.text.x.angle, flip = flip)[["angle"]],
hjust = get_axis_parameters(angle = axis.text.x.angle, flip = flip)[["hjust"]],
vjust = get_axis_parameters(angle = axis.text.x.angle, flip = flip)[["vjust"]]),
axis.text.y = ggplot2::element_text(color = "black", face = axis.text.face),
axis.ticks = ggplot2::element_line(color = "black"),
plot.title.position = "plot",
plot.title = ggplot2::element_text(face = plot.title.face, hjust = 0),
plot.subtitle = ggplot2::element_text(face = plot.subtitle.face, hjust = 0),
plot.caption = ggplot2::element_text(face = plot.caption.face, hjust = 1),
panel.grid = ggplot2::element_blank(),
text = ggplot2::element_text(family = font.type),
plot.caption.position = "plot",
legend.text = ggplot2::element_text(face = legend.text.face),
legend.position = legend.position,
legend.title = ggplot2::element_text(face = legend.title.face),
legend.justification = "center",
plot.margin = ggplot2::margin(t = 10, r = 10, b = 10, l = 10),
plot.background = ggplot2::element_rect(fill = "white", color = "white"),
panel.background = ggplot2::element_rect(fill = "white", color = "white"),
legend.background = ggplot2::element_rect(fill = "white", color = "white"),
strip.text = ggplot2::element_text(color = "black", face = strip.text.face),
strip.background = ggplot2::element_blank())
if (isTRUE(return_data)){
data <- sample@meta.data %>%
tibble::as_tibble() %>%
dplyr::mutate(dplyr::across(dplyr::all_of(c(group.by, split.by)), as.character),
"count" = 1) %>%
tidyr::complete(.data[[split.by]], .data[[group.by]], explicit = FALSE) %>%
dplyr::group_by(.data[[split.by]], .data[[group.by]]) %>%
dplyr::summarise("n" = sum(.data$count, na.rm = TRUE),
"{split.by}" := unique(.data[[split.by]])) %>%
dplyr::reframe("n" = .data$n,
"freq" = .data$n / sum(.data$n),
"{split.by}" := unique(.data[[split.by]]),
"{group.by}" := unique(.data[[group.by]]))
return(list("Plot" = p,
"Data" = data))
} else {
return(p)
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.