#' @rdname plotSpatialScatterpie
#' @name plotSpatialScatterpie
#' @title Spatial scatterpie
#'
#' @description This function takes in the coordinates of the spots and the
#' proportions of the cell types within each spot. It returns a plot where
#' each spot is a piechart showing proportions of the cell type composition.
#'
#' @param x Object containing the spots coordinates, it can be an object of class
#' SpatialExperiment, dataframe or matrix. For the latter two
#' rownames should have the spot barcodes to match x. If a matrix it has to
#' of dimensions nrow(y) x 2 where the columns are the x and y coordinates
#' in that order.
#' @param y Matrix or dataframe containing the deconvoluted spots. rownames
#' need to be the spot barcodes to match to x.
#' @param img Logical TRUE or FALSE indicating whether to plot the image or not.
#' Objects of classes accepted by \code{plotImage} can also be passed and
#' that image will be used. By default FALSE.
#' @param slice Character string indicating which slice to plot if img is TRUE.
#' By default uses the first image.
#' @param cell_types Vector of cell type names to plot. By default uses the
#' column names of y.
#' @param scatterpie_alpha Numeric scalar to set the alpha of the pie charts.
#' By default 1.
#' @param pie_scale Numeric scalar to set the size of the pie charts.
#' By default 0.4.
#' @param degrees From SpatialExperiment rotateImg. For clockwise (degrees > 0)
#' and counter-clockwise (degrees < 0) rotation. By default NULL.
#' @param axis From SpatialExperiment mirrorImg. When a SpatialExperiment object
#' is passed as the image return the mirror image. For horizontal (axis = "h")
#' and vertical (axis = "v") mirroring. By default NULL.
#' @param ... additional parameters to geom_scatterpie
#' @return \code{ggplot} object
#'
#' @author Marc Elosua Bayes & Helena L Crowell
#'
#' @examples
#' set.seed(321)
#'
#' # Coordinates
#' x <- replicate(2, rnorm(100))
#' rownames(x) <- paste0("spot", seq_len(nrow(x)))
#' colnames(x) <- c("imagecol", "imagerow")
#'
#' # Proportions
#' y <- replicate(m <- 5, runif(nrow(x), 0, 1))
#' y <- prop.table(y, 1)
#'
#' rownames(y) <- paste0("spot", seq_len(nrow(y)))
#' colnames(y) <- paste0("type", seq_len(ncol(y)))
#'
#' (plt <- plotSpatialScatterpie(x = x, y = y))
NULL
#' @rdname plotSpatialScatterpie
#' @import ggplot2
#' @export
plotSpatialScatterpie <- function(
x,
y,
cell_types = colnames(y),
img = FALSE,
slice = NULL,
scatterpie_alpha = 1,
pie_scale = 0.4,
degrees = NULL,
axis = NULL,
...) {
# Check necessary packages are installed and if not STOP
.test_installed("scatterpie")
# Class checks
stopifnot(
# Check x inputs
is.matrix(x) | is.data.frame(x) |
is(x, "SpatialExperiment"),
# Check y inputs
is.matrix(y) | is.data.frame(y),
# cell_types needs to be a character with max length = ncol(y)
is.character(cell_types) & length(cell_types) <= ncol(y),
# Check img
# img not checked since its checked in plotImage()
# Check slice name
is.character(slice) | is.null(slice),
# Check plotting parameters are numeric
is.numeric(scatterpie_alpha),
is.numeric(pie_scale),
is.numeric(degrees) | is.null(degrees),
axis %in% c("h", "v") | is.null(axis)
)
# If image is passed add it as the base layer, if not, no image
# Need to use isFALSE bc img can have many different inputs
# Set ymax to overlap image and piecharts
if (isFALSE(img)) {
p <- ggplot() +
coord_fixed()
ymax <- 0
} else {
# Extract image from Seurat or SE objects when img is TRUE
# If image is not TRUE and not FALSE an acceptable class for plotImage
# has been passed
if (is(x, "SpatialExperiment") & isTRUE(img)) {
img <- .extract_image(x, slice)
# Rotate or mirror image if dots don't overlay properly
if (is(x, "SpatialExperiment")) {
.test_installed("SpatialExperiment")
## Rotate image if needed
if (!is.null(degrees)) {
.test_installed("grDevices")
img <- SpatialExperiment::SpatialImage(
grDevices::as.raster(img))
img <- as(img, "LoadedSpatialImage")
img <- SpatialExperiment::rotateImg(img, degrees = degrees)
img <- grDevices::as.raster(img)
}
## Make mirror image if necessary
if (!is.null(axis)) {
.test_installed("grDevices")
img <- SpatialExperiment::SpatialImage(
grDevices::as.raster(img))
img <- as(img, "LoadedSpatialImage")
img <- SpatialExperiment::mirrorImg(img, axis = axis)
img <- grDevices::as.raster(img)
}
}
}
p <- plotImage(x = img)
ymax <- max(p$coordinates$limits$y)
}
# Extract coordinate matrix from x
if (!is.matrix(x))
x <- .extract_coord(x = x, slice = slice, img = img)
# Check colnames
x <- .x_cnames(x)
# Convert y to matrix format
if (!is.matrix(x)) {
y <- as.matrix(x)
}
# Stop if x and y don't have the same number of columns or if the
# rownames are not common between them
stopifnot(
nrow(x) == nrow(y),
all(rownames(x) %in% rownames(y)))
# merge by row names (by=0 or by="row.names")
df <- merge(x, y, by = 0, all = TRUE)
# make y negative
df$coord_y_i <- abs(df$coord_y - ymax)
# Plot
p + scatterpie::geom_scatterpie(
data = df,
aes_string(
x = "coord_x",
y = "coord_y_i"
),
cols = cell_types,
color = NA,
alpha = scatterpie_alpha,
pie_scale = pie_scale,
...) +
# Below not needed bc comes from plotImage
# coord_fixed() +
theme_void() +
theme(legend.key.size = unit(0.5, "lines"))
}
.x_cnames <- function(x) {
# If the column names of x aren't right fix them
cnames <- c("coord_y", "coord_x")
if (!all(colnames(x) %in% cnames)) {
colnames(x) <- cnames
}
x
}
# Coordinates and return a matrix object where each row is a spot and the
# columns are the x and y coordinates
.extract_coord <- function(x, slice, img) {
# Iterate over all the accepted classes and return spot coordinates
if (is.data.frame(x)) {
# Convert to matrix
x <- as.matrix(x)
} else if (is(x, "SpatialExperiment")) {
.test_installed(c("SpatialExperiment"))
# Stop if there are no images or the name selected doesn't exist
stopifnot(
# Stop if there are no images
!is.null(SpatialExperiment::getImg(x)),
# Stop if the image doesn't exist
slice %in% SpatialExperiment::imgData(x)[1, "sample_id"],
# Return error if there are no colnames in the object
!is.null(colnames(x))
)
# If slice is null use the first slice
img_df <- SpatialExperiment::imgData(x)
if (is.null(slice))
slice <- img_df[1, "sample_id"]
# Scale factor to scale the coordinates
sf <- img_df[img_df$sample_id == slice, "scaleFactor"]
## Extract spot barcodes
barcodes <- colnames(x)
## Extract spatial coordinates
# coord_df <- SpatialExperiment::spatialCoords(x)
x <- as.matrix(SpatialExperiment::spatialCoords(x)[, c(1, 2)])
## Scale coordinates
x <- x * sf
## Add barcodes to coord matrix & change colnames
rownames(x) <- barcodes
} else {
stop("Couldn't extract image coordinates.
Please check class(x) is SpatialExperiment,
dataframe or matrix")
}
return(x)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.