Advanced visualization
Last compiled: 18 September 2024
advanced_visualization.Rmd
In this tutorial, we’ll have a look at different ways of creating
spatial plots with semla
. Functions such as
MapFeatures()
and MapLabels()
produce
patchworks (see R package patchwork) which are
easy to manipulate after they have been created. In addition to these
two fundamental plotting functions, there are also
MapFeaturesSummary()
and MapLabelsSummary()
,
which works just as their parent functions but also adds a subplot with
informative summary statistics attached.s
The patchwork
R package is extremely versatile and makes
it easy to customize your figures! Here we’ll have a look at some
options that comes with MapFeatures
and
MapLabels
a couple of tips and tricks for how to use the
patchwork
R package.
This tutorial is an extension of the ‘Map
numeric features’ and ‘Map
categorical features’ tutorials. Here you can find more in depth
details about how to manipulate various components of the plots
generated with MapFeatures
and MapLabels
as
well as some additional plot functions.
Load data
First we need to load some 10x Visium data. here we’ll use a mouse
brain tissue dataset and a mouse colon dataset that are shipped with
semla
.
# Load data
se_mbrain <- readRDS(file = system.file("extdata",
"mousebrain/se_mbrain",
package = "semla"))
se_mbrain$sample_id <- "mousebrain"
se_mcolon <- readRDS(file = system.file("extdata",
"mousecolon/se_mcolon",
package = "semla"))
se_mcolon$sample_id <- "mousecolon"
se <- MergeSTData(se_mbrain, se_mcolon)
We can use the functions MapFeatures
and
MapLabels
to make spatial plots showing the distribution of
numeric or categorical features. For those who are familiar with
Seurat
, these functions are similar to
SpatialFeaturePlot
and SpatialDimPlot
in the
sense that the first can be used to visualize numeric data and the
latter can be used to color data points based on categorical data.
Map numeric features
Let’s get started with MapFeatures
. The most basic usage
is to map gene expression spatially:
cols <- viridis::rocket(11, direction = -1)
p <- MapFeatures(se, features = c("Th", "Clu"), colors = cols)
p
Summary subplot
We can also use the function MapFeaturesSummary
to add a
subplot next to the spatial plot summarizing the gene expression in the
section using either a box plot, a violin plot, a histogram, or a
density plot.
This function only takes one gene at the time, but you can patch two plots together manually if desired.
p <- MapFeaturesSummary(se,
features = "Clu",
subplot_type = "violin",
colors = cols)
p
p1 <- MapFeaturesSummary(se,
section_number = 1,
features = "Clu",
subplot_type = "box",
colors = cols,
fill_color = "white")
p2 <- MapFeaturesSummary(se,
section_number = 1,
features = "Clu",
subplot_type = "histogram",
colors = cols)
p1|p2
genes_to_plot <- c("Clu", "Apod", "Cd79a")
plot_list <- lapply(genes_to_plot, function(g){
MapFeaturesSummary(se,
section_number = 1,
features = g,
subplot_type = "density",
pt_size = 0.8,
colors = cols)
})
wrap_plots(plot_list, nrow = 1)
Overlay maps on images
If we want to create a map with the H&E images we can do this by
setting image_use = raw
. But before we can do this, we need
to load the images into our Seurat object:
cols_he <- viridis::viridis(11)
se <- LoadImages(se, verbose = FALSE)
p <- MapFeatures(se,
features = c("Th", "Clu"),
image_use = "raw",
colors = cols_he)
p
Right now it’s quite difficult to see the tissue underneath the spots. We can add some opacity to the colors which is scaled by the feature values to make spots with low expression transparent:
p <- MapFeatures(se,
features = c("Th", "Clu"),
image_use = "raw",
colors = cols_he,
scale_alpha = TRUE)
p
Transpose patchwork layout
By default, MapFeatures
arranges features in columns and
samples in rows. We can transpose the plot by setting
arrange_features
to “row”:
p <- MapFeatures(se,
features = c("Th", "Clu"),
arrange_features = "row",
color = cols)
p
Independent color bars
The color bars are now identical for each feature.
MapFeatures
calculates the range for each feature and uses
this range to determine the limits of the color bars. If you want to
change this behavior to scale the values independently, you can set
scale = "free"
:
p <- MapFeatures(se,
features = c("Th", "Clu"),
scale = "free",
colors = cols)
p
Fit plot area to spots
MapFeatures
used the image dimensions to define the plot
area dimensions. Sometimes, if you have a very small piece of tissue,
you will end up with a lot of white space. You can override this
behavior by setting override_plot_dims = TRUE
which will
make MapFeatures
compute the dimensions based on your
coordinates. Notice how the tissues are expanded:
p <- MapFeatures(se,
features = c("Th", "Clu"),
override_plot_dims = TRUE,
color = cols)
p
Controlling themes
Themes can be modified by adding a new ggplot theme using the
&
operator. This operator will make sure that the theme
is added to each subplot in our patchwork. As an example, let’s say that
we want to place the legends on the right side of our plots instead:
p <- MapFeatures(se, features = c("Th", "Clu"), color = cols) &
theme(legend.position = "right", legend.text = element_text(angle = 0))
p
We can also remove the color legends entirely:
p <- MapFeatures(se, features = c("Th", "Clu"), colors = cols) &
theme(legend.position = "none")
p
Or remove everything except the spatial feature map:
p <- MapFeatures(se, features = c("Th", "Clu"), colors = cols) &
theme(legend.position = "none",
plot.title = element_blank(),
plot.subtitle = element_blank())
p
Collect color bars
If we don’t want to have the same color bar next to each tissue
section, we can collect identical color bars and place them on the side.
We can use plot_layout(guides = "collect")
to modify this.
Note that if we want to place the color bar on the right side of the
plots, we need to set arrange_features = "row"
` and adjust
the legend position:
p <- MapFeatures(se,
features = c("Th", "Clu"),
arrange_features = "row",
colors = cols) +
plot_layout(guides = "collect") &
theme(legend.position = "right", legend.text = element_text(angle = 0))
p
This already looks quite good, but the color bars are a bit misplaced. We can adjust their placement by modifying the legend margins:
p <- MapFeatures(se,
features = c("Th", "Clu"),
arrange_features = "row",
color = cols) +
plot_layout(guides = "collect") &
theme(legend.position = "right",
legend.text = element_text(angle = 0),
legend.margin = margin(t = 50, r = 0, b = 100, l = 0))
p
Do not do this
If we set scale = "free"
, the color bars are no longer
unique and it doesn’t make sense to collect the color bars anymore:
p <- MapFeatures(se,
features = c("Th", "Clu"),
arrange_features = "row",
scale = "free",
colors = cols) +
plot_layout(guides = "collect") &
theme(legend.position = "right",
legend.text = element_text(angle = 0),
legend.margin = margin(t = 50, r = 0, b = 100, l = 0))
p
Do not do this either
If we set arrange_features = "col"
, the placement of the
color bar will not make sense in this case either. The color bars are
now located on the right side, but the features are arranged by row:
p <- MapFeatures(se,
features = c("Th", "Clu"),
arrange_features = "col",
colors = cols) +
plot_layout(guides = "collect") &
theme(legend.position = "right",
legend.text = element_text(angle = 0),
legend.margin = margin(t = 50, r = 0, b = 100, l = 0))
p
Instead, if we want to arrange features by columns, it would make more sense to place the color bars on top of each column and adjust the legend margins accordingly:
p <- MapFeatures(se,
features = c("Th", "Clu"),
arrange_features = "col",
color = cols) +
plot_layout(guides = "collect") &
theme(legend.position = "top",
legend.text = element_text(angle = 0),
legend.margin = margin(t = 0, r = 100, b = 0, l = 10))
p
Remove plot margins
If you still think that there’s too much empty space around the
tissues in the patchwork, you try setting
override_plot_dims = TRUE
which will crop the plot area to
only fit the spots:
p <- MapFeatures(se,
features = c("Th", "Clu"),
override_plot_dims = TRUE,
color = cols)
p
Crop
H&E images will also be cropped to fit the new plot dimensions if
override_plot_dims = TRUE
. This can be particularly useful
when working with small tissue sections that only cover a small portion
of the 10x Visium capture area.
p <- MapFeatures(se,
features = c("Th", "Clu"),
image_use = "raw",
override_plot_dims = TRUE,
color = cols_he)
p
Note that the plots dimensions are calculated for the entire dataset
when override_plot_dims = TRUE
.
If the tissue sections are placed in very different part of the capture area, the resulting “crop” window might not be what you are looking for. If you want to avoid this behavior, it is better to make two separate plots. Notice the difference in the plot below compared to the previous plot. The previous plot had some empty space outside of the tissue.
p1 <- MapFeatures(se,
features = c("Th", "Clu"),
image_use = "raw",
override_plot_dims = TRUE,
color = cols_he,
section_number = 1)
p2 <- MapFeatures(se,
features = c("Th", "Clu"),
image_use = "raw",
override_plot_dims = TRUE,
color = cols_he,
section_number = 2)
p1 / p2
We can also crop the images manually by defining a
crop_area
. The crop_area
should be a vector of
length four defining the corners of a rectangle, where the x- and y-axes
are defined from 0-1.
In order to decide how this rectangle should be defined, you can get some help by adding a grid to the plot:
p <- MapFeatures(se,
features = c("Th", "Clu"),
image_use = "raw",
color = cols_he,
pt_alpha = 0.5) &
theme(panel.grid.major = element_line(linetype = "dashed"), axis.text = element_text())
p
Now if we want to crop out the GALT tissue in the mouse colon sample we can cut the image at left=0.45, bottom=0.55, right=0.65, top=0.7:
p <- MapFeatures(se,
features = c("Cd79a", "Clu"),
image_use = "raw",
pt_size = 3,
section_number = 2,
color = cols_he,
crop_area = c(0.45, 0.55, 0.65, 0.7))
p
And we can patch together a nice figure showing the expression both at a global level and inside the GALT:
p_global <- MapFeatures(se,
features = c("Cd79a", "Clu"),
image_use = "raw",
scale_alpha = TRUE,
pt_size = 1,
section_number = 2,
color = cols_he,
override_plot_dims = TRUE)
p_GALT <- MapFeatures(se,
features = c("Cd79a", "Clu"),
image_use = "raw",
scale_alpha = TRUE,
pt_size = 3,
section_number = 2,
color = cols_he,
crop_area = c(0.45, 0.55, 0.65, 0.7)) &
theme(plot.title = element_blank(),
plot.subtitle = element_blank(),
legend.position = "none")
(p_global / p_GALT)
Increase resolution
Right now, you can see that the resolution of zoomed in image is
quite low. The reason for this is that the images were loaded to have a
height of 400 pixels which is the default setting for
LoadImages
. If you want to, you can reload the images in
higher resolution given that you provided higher resolution images as
input. For example, if you used the “tissue_hires_image.png” files as
input for ReadVisiumData
, these images are roughly
2000x2000 pixels in size, so you can reload the images in higher
resolution. If we run LoadImages
again and set
image_height = 1000
we should be able to view the H%&E
images in slightly higher resolution.
NB: The “hires” images are not provided in semla
so we
will need to download them. We are currently using the “lowres” images
which are only about 600x600 pixels large. We can use
ReplaceImagePaths
to replace the current images. You need
to make sure that the new images are in the correct order and that they
represent scaled representations of the images already loaded into the
object.
# Download hires H&E images
he_imgs <- c(file.path("https://data.mendeley.com/public-files/datasets/kj3ntnt6vb",
"files/d97fb9ce-eb7d-4c1f-98e0-c17582024a40/file_downloaded"),
file.path("https://data.mendeley.com/public-files/datasets/kj3ntnt6vb",
"files/d33e8074-8d83-4003-b04a-9a78ff465279/file_downloaded"))
# Replace images
se <- ReplaceImagePaths(se, paths = he_imgs)
# Load images in higher resolution
se_high_res <- LoadImages(se, image_height = 1000)
##
## ── Loading H&E images ──
##
## ℹ Loading image from https://data.mendeley.com/public-files/datasets/kj3ntnt6vb/files/d97fb9ce-eb7d-4c1f-98e0-c17582024a40/file_downloaded
## ℹ Scaled image from 2000x1882 to 1000x941 pixels
## ℹ Loading image from https://data.mendeley.com/public-files/datasets/kj3ntnt6vb/files/d33e8074-8d83-4003-b04a-9a78ff465279/file_downloaded
## ℹ Scaled image from 8929x9901 to 1000x1109 pixels
## ℹ Saving loaded H&E images as 'rasters' in Seurat object
# Check object sizes
print(object.size(se), units = "MB")
## 16.4 Mb
print(object.size(se_high_res), units = "MB")
## 39.1 Mb
Note that these higher resolution images will take up more space and might slow down the plotting considerably.
p_global <- MapFeatures(se_high_res,
features = c("Cd79a", "Clu"),
image_use = "raw",
scale_alpha = TRUE,
pt_size = 1,
section_number = 2,
color = cols_he,
override_plot_dims = TRUE)
p_GALT <- MapFeatures(se_high_res,
features = c("Cd79a", "Clu"),
image_use = "raw",
scale_alpha = TRUE,
pt_size = 3,
section_number = 2,
color = cols_he,
crop_area = c(0.45, 0.55, 0.65, 0.7)) &
theme(plot.title = element_blank(),
plot.subtitle = element_blank(),
legend.position = "none")
(p_global / p_GALT)
Map categorical features
For categorical data, we use MapLabels()
instead of
MapFeatures()
. This function allows us to color our spots
based on some column of our Seurat object containing categorical
data.
Order of categories
Categorical data can be represented as character vectors or factors, but with factors it’s easier to control the order of the labels as well as their colors. If we want to customize the order, we can convert our column into a factor and set the levels as we please:
Colors
If we pass a named vector of colors we can control the coloring of our labels:
MapLabels(se, column_name = "sample_id_factor", ncol = 1,
colors = c("mousecolon" = "#4477AA", "mousebrain" = "#CC6677")) &
theme(legend.position = "right")
Let’s run unsupervised clustering on our data to get slightly more interesting results to work with.
NB: It doesn’t make much sense to run data-driven clustering on two
completely different tissue types, but here we are only interested in
demonstrating how you can use MapLabels
.
se <- se |>
NormalizeData() |>
ScaleData() |>
FindVariableFeatures() |>
RunPCA() |>
FindNeighbors(reduction = "pca", dims = 1:10) |>
FindClusters(resolution = 0.2)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
##
## Number of nodes: 5164
## Number of edges: 170850
##
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.9210
## Number of communities: 5
## Elapsed time: 0 seconds
Summary subplot
Similarly to MapFeaturesSummary()
, we also have
MapLabelsSummary()
, which adds a stacked barchart
displaying the cluster proportion (percentage) in the section. Passing
bar_display = "count"
will instead show the actual spot
count per cluster.
Let’s combine MapLabelsSummary
with some custom cluster
colors.
cluster_colors <- setNames(RColorBrewer::brewer.pal(length(levels(se$seurat_clusters)), "Set1"),
nm = levels(se$seurat_clusters))
MapLabelsSummary(se,
column_name = "seurat_clusters",
ncol = 1,
colors = cluster_colors)
We can adjust a few things, like width and text size, on the bar plot
with the arguments bar_width
and
bar_label_size
.
MapLabelsSummary(se,
column_name = "seurat_clusters",
section_number = 1,
label_by = "sample_id",
colors = cluster_colors,
pt_size = 2,
bar_display = "count",
bar_width = 2,
bar_label_size = 4) &
theme(plot.title = element_text(hjust=0.5, size = 14, face = "bold"))
Format legend
Point size
If you want to increase the size of the spots in the color legend,
you can override the fill
aesthetic that controls the
appearance of the points without changing the size of the points in the
plot. We can do this by using
guides(fill = guide_legend(override.aes = list(size = ...)))
:
MapLabels(se,
column_name = "seurat_clusters",
ncol = 1,
colors = cluster_colors) &
guides(fill = guide_legend(override.aes = list(size = 3))) &
theme(legend.position = "right")
Legend arrangement
Another useful option is to adjust the arrangement of the color legend. For example, if you have a lot of different categories, it might be easier to read the labels if they are arranged in multiple columns:
MapLabels(se,
column_name = "seurat_clusters",
ncol = 1,
colors = cluster_colors) &
guides(fill = guide_legend(override.aes = list(size = 3),
ncol = 2)) &
theme(legend.position = "right")
Collect legends
Since all sub plots share the same color legends, we can collect them and place them on the side of the plot:
MapLabels(se,
column_name = "seurat_clusters",
ncol = 1,
colors = cluster_colors) +
plot_layout(guides = "collect") &
guides(fill = guide_legend(override.aes = list(size = 3),
ncol = 2)) &
theme(legend.position = "right")
Overlay maps on images
And just as with MapFeatures
, we can add our H&E
images to the plots:
MapLabels(se,
column_name = "seurat_clusters",
image_use = "raw",
override_plot_dims = TRUE,
pt_alpha = 0.6,
colors = cluster_colors) +
plot_layout(guides = "collect") &
guides(fill = guide_legend(override.aes = list(size = 3),
ncol = 2)) &
theme(legend.position = "right")
Split labels
Sometimes it can get cluttered in the plot and difficult to see where
each cluster is located in the tissue, especially when there are many
clusters and some of them have very similar colors. In this case,
MapLabels
can split the data into separate panels, one for
each label:
MapLabels(se,
column_name = "seurat_clusters",
split_labels = TRUE,
colors = cluster_colors) +
plot_layout(guides = "collect") &
theme(legend.position = "right",
legend.title = element_blank(),
legend.margin = margin(-10,-10,-10, 0)) &
guides(fill = guide_legend(override.aes = list(size = 3)))
## Warning: No section_number selected. Selecting section 1.
When you split data, you can only do it for one section which is why
a warning is thrown and the first available section is selected. If you
want to use a different section, you can specify which one to use with
section_number
:
MapLabels(se,
column_name = "seurat_clusters",
split_labels = TRUE,
section_number = 2,
ncol = 1,
colors = cluster_colors) +
plot_layout(guides = "collect") &
theme(legend.position = "right")
Remove background
When mapping categorical features in split view it might be more
useful to see the underlying image. If you set
drop_na = TRUE
, the background spots will be removed:
MapLabels(se,
column_name = "seurat_clusters",
split_labels = TRUE,
image_use = "raw",
drop_na = TRUE,
override_plot_dims = TRUE,
colors = cluster_colors) +
plot_layout(guides = "collect") &
theme(legend.position = "right",
legend.title = element_blank(),
legend.margin = margin(-10, 10, -10, 10),
plot.title = element_blank(),
plot.margin = margin(0, 5, 5, 0)) &
guides(fill = guide_legend(override.aes = list(size = 3)))
## Warning: No section_number selected. Selecting section 1.
Scale bars
To add a scalebar, simply set add_scalebar = TRUE
and
the scalebar should appear in the top right corner.
se_mbrain <- LoadImages(se_mbrain)
##
## ── Loading H&E images ──
##
## ℹ Loading image from /Users/lovisafranzen/Library/Caches/org.R-project.R/R/renv/library/semla-41c099bc/R-4.4/aarch64-apple-darwin20/semla/extdata/mousebrain/spatial/tissue_lowres_image.jpg
## ℹ Scaled image from 600x565 to 400x377 pixels
## ℹ Saving loaded H&E images as 'rasters' in Seurat object
MapFeatures(se_mbrain, features = "nFeature_Spatial", pt_size = 1,
override_plot_dims = TRUE, image_use = "raw",
add_scalebar = TRUE)
## Loading required namespace: ggfittext
And with mapLabels
:
MapLabels(se_mbrain, column_name = "sample_id", pt_size = 1,
override_plot_dims = TRUE, image_use = "raw",
add_scalebar = TRUE)
You can also tweak the appearance and position of the scalebar with
scalebar_gg
, scalebar_position
and
scalebar_height
.
-
scalebar_gg
allows you to modify the width and breaks of the scalebar. The title of the scalebar will be adjusted from micrometers to millimeters if x > 1000. -
scalebar_position
sets the position of the scalebar relative to the entire plot. -
scalebar_height
can be useful to adjust the height of the scalebar to make the text fit. If the scalebar is too small, for instance when plotting many sections at once, increasing the height will make the text appear.
MapLabels(se_mbrain, column_name = "sample_id", pt_size = 1,
image_use = "raw",
add_scalebar = TRUE,
scalebar_gg = scalebar(x = 2000, breaks = 11, highlight_breaks = c(1, 6, 11)),
scalebar_position = c(0.55, 0.7),
scalebar_height = 0.07)
Here’s another example with image masking:
# Mask image
se_mbrain <- MaskImages(se_mbrain)
MapFeatures(se_mbrain, features = VariableFeatures(se_mbrain)[1:6],
image_use = "raw",
add_scalebar = TRUE,
scale_alpha = TRUE,
override_plot_dims = TRUE,
scalebar_gg = scalebar(x = 1000),
scalebar_position = c(0.55, 0.88),
scalebar_height = 0.14) &
theme(plot.title = element_blank(),
legend.position = "right", legend.text = element_text(angle = 0))
NB: We will include a higher resolution version of the H&E image for the next example. This is only relevant for this tutorial and will not be necessary if you created the object using “tissue_hires_image.png” files.
# Download hires H&E images
he_img <- file.path("https://data.mendeley.com/public-files/datasets/kj3ntnt6vb",
"files/d97fb9ce-eb7d-4c1f-98e0-c17582024a40/file_downloaded")
# Replace images
se_mbrain <- ReplaceImagePaths(se_mbrain, paths = he_img)
If you want to crop the data, you might want to adjust the width scalebar to a more appropriate length.
se_mbrain <- LoadImages(se_mbrain, image_height = 1.5e3)
MapFeatures(se_mbrain, features = "Th", pt_size = 5,
image_use = "raw", scale_alpha = TRUE,
colors = RColorBrewer::brewer.pal(n = 11, name = "Spectral") |> rev(),
crop_area = c(0.2, 0.6, 0.4, 0.75),
scalebar_position = c(0.7, 0.8), scalebar_height = 0.08,
add_scalebar = TRUE, scalebar_gg = scalebar(x = 400))
Rearrange patchworks
While MapLabels
and MapFeatures
do not
provide arguments to sort samples in a specific order, this can be
achieved by returning the data as a list of ggplot
objects
and define the order manually.
For instance, if we have plot two features and set
return_plot_list = TRUE
, we can rearrange the plots with
wrap_plots
:
p <- MapFeatures(se_mbrain, c("Th", "Calb2"), return_plot_list = TRUE)
wrap_plots(p$`1`$Calb2, p$`1`$Th)
If we have multiple sections, we can access the section-specific plots from the list. In the example below, the plots have been rearranged so that the columns and rows are flipped.
p <- MapFeatures(se, c("Th", "Calb2"), return_plot_list = TRUE)
wrap_plots(p$`2`$Calb2, p$`2`$Th, p$`1`$Th, p$`1`$Calb2)
Basic themes and plot annotation
semla
also provides a couple of utility functions to
make it easier to modify themes.
If you are plotting many features and sections at the same time, it
can be useful to remove all components of the theme with
ThemeClean
to get a quick overview:
MapFeatures(se, c("Th", "Calb2", "Clu", "Mgp", "Acta2", "Tagln"),
override_plot_dims = TRUE, pt_size = 0.5) &
ThemeClean()
If you like to move the color legends to the right hand side of the
plots, you can use ThemeLegendRight
:
MapFeatures(se, c("Th", "Calb2"),
override_plot_dims = TRUE) &
ThemeLegendRight()
The titles of individual plots are determined internally by
MapLabels
and MapFeatures
and it can be
difficult to change these titles after the plot has been generated.
ModifyPatchworkTitles
can help with this task. All you need
to do is to provide a character vector with new titles which should
match the number of subplots in the patchwork. In the example below we
have 2 genes and 2 sections, so we need to provide 4 titles:
p <- MapFeatures(se, c("Th", "Calb2"), override_plot_dims = TRUE)
ModifyPatchworkTitles(p, titles = c("First", "Second", "Third", "Fourth"))
Package version
-
semla
: 1.1.6
Session info
## R version 4.4.0 (2024-04-24)
## Platform: aarch64-apple-darwin20
## Running under: macOS Sonoma 14.5
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.0
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## time zone: Europe/Stockholm
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices datasets utils methods base
##
## other attached packages:
## [1] patchwork_1.3.0 tibble_3.2.1 semla_1.1.6 ggplot2_3.5.0
## [5] dplyr_1.1.4 SeuratObject_4.1.4 Seurat_4.3.0.1
##
## loaded via a namespace (and not attached):
## [1] RColorBrewer_1.1-3 rstudioapi_0.16.0 jsonlite_1.8.8
## [4] magrittr_2.0.3 magick_2.8.4 spatstat.utils_3.1-0
## [7] farver_2.1.2 rmarkdown_2.28 fs_1.6.4
## [10] ragg_1.3.3 vctrs_0.6.5 ROCR_1.0-11
## [13] spatstat.explore_3.3-2 forcats_1.0.0 htmltools_0.5.8.1
## [16] curl_5.2.2 sass_0.4.9 sctransform_0.4.1
## [19] parallelly_1.38.0 KernSmooth_2.23-24 bslib_0.8.0
## [22] htmlwidgets_1.6.4 desc_1.4.3 ica_1.0-3
## [25] plyr_1.8.9 plotly_4.10.4 zoo_1.8-12
## [28] cachem_1.1.0 ggfittext_0.10.2 igraph_2.0.3
## [31] mime_0.12 lifecycle_1.0.4 pkgconfig_2.0.3
## [34] Matrix_1.7-0 R6_2.5.1 fastmap_1.2.0
## [37] fitdistrplus_1.2-1 future_1.34.0 shiny_1.9.1
## [40] digest_0.6.37 colorspace_2.1-1 tensor_1.5
## [43] irlba_2.3.5.1 textshaping_0.4.0 labeling_0.4.3
## [46] progressr_0.14.0 fansi_1.0.6 spatstat.sparse_3.1-0
## [49] httr_1.4.7 polyclip_1.10-7 abind_1.4-8
## [52] compiler_4.4.0 withr_3.0.1 viridis_0.6.5
## [55] highr_0.11 MASS_7.3-60.2 tools_4.4.0
## [58] lmtest_0.9-40 httpuv_1.6.15 future.apply_1.11.2
## [61] goftest_1.2-3 glue_1.7.0 dbscan_1.2-0
## [64] nlme_3.1-164 promises_1.3.0 grid_4.4.0
## [67] Rtsne_0.17 cluster_2.1.6 reshape2_1.4.4
## [70] generics_0.1.3 gtable_0.3.5 spatstat.data_3.1-2
## [73] tidyr_1.3.1 data.table_1.16.0 sp_2.1-4
## [76] utf8_1.2.4 spatstat.geom_3.3-2 RcppAnnoy_0.0.22
## [79] ggrepel_0.9.6 RANN_2.6.2 pillar_1.9.0
## [82] stringr_1.5.1 spam_2.10-0 later_1.3.2
## [85] splines_4.4.0 lattice_0.22-6 renv_1.0.2
## [88] survival_3.6-4 deldir_2.0-4 tidyselect_1.2.1
## [91] miniUI_0.1.1.1 pbapply_1.7-2 knitr_1.48
## [94] gridExtra_2.3 scattermore_1.2 xfun_0.47
## [97] matrixStats_1.4.1 stringi_1.8.4 lazyeval_0.2.2
## [100] yaml_2.3.10 evaluate_0.24.0 codetools_0.2-20
## [103] BiocManager_1.30.25 cli_3.6.3 uwot_0.2.2
## [106] xtable_1.8-4 reticulate_1.39.0 systemfonts_1.1.0
## [109] munsell_0.5.1 jquerylib_0.1.4 Rcpp_1.0.13
## [112] globals_0.16.3 spatstat.random_3.3-1 zeallot_0.1.0
## [115] png_0.1-8 spatstat.univar_3.0-1 parallel_4.4.0
## [118] pkgdown_2.1.0 dotCall64_1.1-1 listenv_0.9.1
## [121] viridisLite_0.4.2 scales_1.3.0 ggridges_0.5.6
## [124] leiden_0.4.3.1 purrr_1.0.2 rlang_1.1.4
## [127] cowplot_1.1.3 shinyjs_2.1.0