library("dplyr")
library("Seurat")
library("knitr")
library("ggplot2")
library("BiocManager")
library("here")
#BiocManager::install("EnhancedVolcano")
library("EnhancedVolcano") #volcano plot
#install.packages('DESeq2') #for DEG
library("DESeq2")
library("tidyverse") #tidy up data
library("styler") #tidy up data
library("scCustomize") #for color scales)
library("qs") #for file compression
library("sf") #for spatial
library("rstatix") # For stats
library("FNN")


if (!require("kableExtra")) {install.packages("kableExtra"); require("kableExtra")} # for color brewer
if (!require("RColorBrewer")) {install.packages("RColorBrewer"); require("RColorBrewer")} # for color brewer
if (!require("sctransform")) {install.packages("sctransform"); require("sctransform")} # for data normalization
if (!require("glmGamPoi")) {BiocManager::install('glmGamPoi'); require("glmGamPoi")} # for data normalization, sctransform
if (!require("cowplot")) {install.packages("cowplot"); require("cowplot")} # for figure layout
if (!require("patchwork")) {install.packages("patchwork"); require("patchwork")} # for figure patching
if (!require("openxlsx")) {install.packages("openxlsx"); require("openxlsx")} # to save .xlsx files

# install.packages("styler")

set.seed(12345)
# here()

1 Welcome

Welcome to the Single-Cell Omics Research and Education Club!

If this is your first time to the club, I want to extend an extra-special welcome to you!

I’m Jonathan Nelson, an Assistant Professor at the University of Southern California. I’m a wet scientist turned wet+dry scientist. I’ve been working with single-cell RNAseq data for the past 5 years and I’m excited to share what I’ve learned with you.

2 BIG NEWS

2.1 We have a sponsorship!

Credit Jesse Goodrich for this AMAZING animation

Website

2.2 Support

AIMS@USC is supported by the USC Office of Research and Innovation Research Catalyst Program focused on Interdisciplinary Artificial Intelligence, and is part of the collaborative teams at the USC Kidney Research Center and the Center for Translational Exposomics Research.

AIMS@USC would not have been possible without investments from:

2.3 SCORE Values

2.3.1 Learning

We believe that bioinformatics is a constantly evolving field, and that ongoing learning and professional development is essential to staying up-to-date. We encourage members to share their knowledge and experiences with each other, and to seek out opportunities for continued learning.

2.3.2 Accessibility

We believe that access to bioinformatics support should be available to everyone. We strive to create a welcoming and inclusive environment where all members can feel comfortable asking for help and contributing to the group.

2.3.3 Collaboration

We believe that working together is key to achieving success in bioinformatics. We value the diversity of perspectives and backgrounds that each member brings, and we encourage open communication and the sharing of ideas.

2.3.4 Integrity

We believe in conducting ourselves with honesty and professionalism in all our interactions. We hold ourselves to high ethical standards and respect the privacy and confidentiality of all members.

2.3.5 Empathy

We believe in approaching each other with empathy and kindness. We understand that bioinformatics can be a challenging and sometimes frustrating field, and we strive to support each other through these difficulties.

Email me if you would like me to add anyone:

Today’s code (this html file) will be posted to the SCORE website (https://usckrc.github.io/website/score.html)

3 The Agenda!

3.1 Music and Memes

3.2 Main Theme: XE -> ggplot

4 Music and Memes

4.1 This Months Coding Music

4.1.1 Capital Cities: In A Tidal Wave of Mystery

4.1.2 “Safe and Sound” Hits

4.1.2.1 Bonus for listening to the Karen Souza Cover

4.2 The Memes

4.2.1 Slow Month!

4.2.2 NO MEMES!



5 Main Theme: XE -> ggplot

5.1 Introduction

Clear visualization is not just a finishing step in omics analysis—it’s central to how we understand and communicate complex biological data. High-dimensional datasets, especially in genomics and transcriptomics, contain patterns that are nearly impossible to interpret through tables or statistics alone. Effective visualizations transform these data into intuitive representations, making structure, variability, and biological meaning immediately accessible.

This becomes even more important in spatial omics, where data are not only high-dimensional but also embedded in physical space.

5.1.1 Definitions:

5.1.1.1 Vertex: a single point (x, y coordinate)

5.1.1.2 Vertices: connected points to form the outline of a cell (polygon)

5.1.1.3 Centroid: the geometric center of a cell polygon

5.1.2 Cell Vertices are stored in the cell_boundaries.parquet (or csv.gz) file

5.1.3 Nucleus Vertices are stored in the nucleus_boundaries.parquet (or csv.gz) file

5.2 Revision to LoadXenium

5.2.1 Old Way

I took this directly from the Satija website without reading the arguments list.

https://satijalab.org/seurat/articles/seurat5_spatial_vignette_2#mouse-brain-10x-genomics-xenium-in-situ

xenium.obj <- LoadXenium(here("output-XETG00402__0054800__Region_1__20250822__221946"), fov = "fov")

5.2.2 New Way

This captures the cell vertices that were stored in the xenium ranger outputs and attaches them to the Seurat Object.

segmentations = "cell"

5.2.2.1 Could also add segmentations = "nucleus"…but I haven’t yet

xenium.obj <- LoadXenium(here("output-XETG00402__0054800__Region_1__20250822__221946"), fov = "fov", segmentations = "cell")
xenium.obj <- qread(here("Week 10 XE to ggplot", "region1_xenium_distance_spatial.qs"))

5.2.3 Check Which Boundaries Are Loaded Into Seurat Object

5.2.3.1 Looking for a category other than centroids

names(xenium.obj[["fov"]])
## [1] "centroids"    "segmentation" "molecules"

5.2.4 Transfer Segmentations From One Object To Another

This is how I went back to my already annotated Xenium objects and added cell segmentations

# Extract segmentation from original object
seg <- obj1@images$fov@boundaries$segmentation

# Add it to the new object
obj2@images$fov@boundaries$segmentation <- seg

5.2.5 Xenium Explorer Is GREAT

Xenium Explorer stands out as an exceptionally intuitive and powerful visualization tool, transforming complex spatial transcriptomics data into something you can explore almost effortlessly. Its interactive interface allows you to move seamlessly from whole tissue architecture down to subcellular transcript localization, making it easy to interrogate gene expression, validate segmentation, and uncover spatial patterns in real time. With responsive controls, flexible annotation tools, and the ability to overlay molecular data with histology images, Xenium Explorer makes high-dimensional data feel tangible…enabling both quick insights and deep exploration without a steep learning curve.

5.2.6 Download Here

Let’s start with what an image looks like in Xenium Explorer.

5.2.6.1 Zoomed in FOV that I want to analyze in Seurat

5.2.6.2 How to Define Field of View (FOV)

5.2.6.2.1 I spent a lot of time trying to figure out how to transfer coordinates between Xenium Explorer and RStudio
5.2.6.2.2 I Failed

5.2.6.3 Easiest solution is to subset the cells that are in the FOV

5.2.6.4 Xenium Explorer Output

5.2.7 Read Cell in FOV and filter cells

cells <- read_csv(here("Week 10 XE to ggplot", "score10_selection_1_cells_stats.csv"), skip = 2) %>%
  as.data.frame() %>%                   # dplyr/tibble prefers explicit cols
  column_to_rownames(var = colnames(.)[1])   # use first column as rownames

cells_to_keep <- rownames(cells)

xenium.obj2 <- subset(xenium.obj, cells = cells_to_keep)

5.3 Default ImageFeaturePlot (From Satija Lab Tutorial)

5.3.1 FeaturePlot highlighting Nphs2 a podocyte-specific gene

ImageFeaturePlot(xenium.obj2, features = c("Nphs2"), cols = c("white", "red"))

5.3.2 Not Very Helpful…Plotting Centroid

5.4 Segmentation instead of Centroids

boundaries = "segmentation"

ImageFeaturePlot(xenium.obj2, features = c("Nphs2"), cols = c("white", "red"), boundaries = "segmentation")

5.4.1 Better…but Wrong Orientation from Xenium Explorer

5.4.1.1 XE FOV

5.4.1.2 The Correction!

coord_flip() + scale_x_reverse()

ImageFeaturePlot(
  xenium.obj2,
  features = "Nphs2",
  cols = c("white", "red"),
  boundaries = "segmentation"
) +
  coord_flip() +
  scale_x_reverse()

5.5 White Background with Black Outlines

5.5.1 Trying to make the plot a bit more publication ready

ImageFeaturePlot(
  xenium.obj2,
  features = "Nphs2",
  cols = c("white", "red"),
  boundaries = "segmentation",
  border.color = "black"
) +
  coord_flip() +
  scale_x_reverse() +
  theme_void() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    plot.background  = element_rect(fill = "white", color = NA)
  ) + ggtitle("")

5.6 Fill Cells by Spatial Cell Type

ImageDimPlot(
  xenium.obj2,
  group.by = "spatial",
  boundaries = "segmentation",
  border.color = "black"
) +
  coord_flip() +
  scale_x_reverse() +
  theme_void() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    plot.background  = element_rect(fill = "white", color = NA)
  ) + ggtitle("")

5.6.1 Fill Cells by Expression of Ligand

ImageFeaturePlot(
  xenium.obj2,
  features = "Vegfa",
  cols = c("white", "red"),
  boundaries = "segmentation",
  border.color = "black"
) +
  coord_flip() +
  scale_x_reverse() +
  theme_void() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    plot.background  = element_rect(fill = "white", color = NA)
  ) + ggtitle("")

5.6.2 Fill Cells by Expression of Ligand

ImageFeaturePlot(
  xenium.obj2,
  features = "Kdr",
  cols = c("white", "blue"),
  boundaries = "segmentation",
  border.color = "black"
) +
  coord_flip() +
  scale_x_reverse() +
  theme_void() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    plot.background  = element_rect(fill = "white", color = NA)
  ) + ggtitle("")

5.6.3 This is great!

5.6.3.1 Combine Tissue/UMAP/MDD/Prop plot into one!

5.6.3.1.1 Define Cells and Color Palette
xenium.obj@meta.data <- dplyr::mutate(
    xenium.obj@meta.data,
    spatial_region = dplyr::case_when(
      spatial == "PTS1"  ~ "cortex",
      spatial == "PTS2"  ~ "cortex",
      spatial == "PTS3"  ~ "outer_medulla",
      spatial == "DTL"   ~ "papilla",
      spatial == "ATL"   ~ "papilla",
      spatial == "TALA"  ~ "outer_medulla",
      spatial == "TALB"  ~ "inner_medulla",
      spatial == "MD"    ~ "cortex",
      spatial == "DCT1"  ~ "cortex",
      spatial == "DCT2"  ~ "cortex",
      spatial == "CNT"   ~ "cortex",
      spatial == "CPC"   ~ "cortex",
      spatial == "MPC"   ~ "papilla",
      spatial == "ICA"   ~ "outer_medulla",
      spatial == "ICB"   ~ "cortex",
      spatial == "PODO"  ~ "glomerulus",
      spatial == "PEC"   ~ "cortex",
      spatial == "URO"   ~ "other",
      spatial == "CEC"   ~ "cortex",
      spatial == "GEC"   ~ "glomerulus",
      spatial == "MEC"   ~ "inner_medulla",
      spatial == "VEC"   ~ "inner_medulla",
      spatial == "LYMPH" ~ "other",
      spatial == "CFIB"  ~ "cortex",
      spatial == "MFIB"  ~ "inner_medulla",
      spatial == "PFIB"  ~ "papilla",
      spatial == "VFIB"  ~ "cortex",
      spatial == "IMFIB" ~ "papilla",
      spatial == "PVFIB" ~ "other",
      spatial == "MES"   ~ "glomerulus",
      spatial == "VSMC"  ~ "cortex",
      spatial == "REN"   ~ "cortex",
      spatial == "FAT"   ~ "other",
      spatial == "MACRO" ~ "other",
      spatial == "BCELL" ~ "other",
      spatial == "TCELL" ~ "other",
      TRUE ~ NA_character_
    )
  )


spatial_region_colors_color_blind <- c(
  "glomerulus"    = "#00C853",
  "cortex"        = "#0072B2",
  "outer_medulla" = "#E69F00",
  "inner_medulla" = "#D55E00",
  "papilla"       = "#CC79A7",
  "other"         = "#999999"
)
f1 <- ImageDimPlot(
  object = xenium.obj,
  group.by = "spatial_region",
  cols = spatial_region_colors_color_blind,
  dark.background = FALSE,
  border.color = NA,
  boundaries = "segmentation"
) + 
  coord_flip() +
  scale_x_reverse() +
  ggtitle("Tissue") +
  theme(
    legend.position = "none",
    plot.title = element_text(hjust = 0.5)
  )

f1

f2 <- DimPlot(
  object = xenium.obj,
  reduction = "umap",
  group.by = "spatial_region",
  cols = spatial_region_colors_color_blind
) + 
  ggtitle("UMAP") +
  theme_void() +
  theme(
    legend.position = "none",
    plot.title = element_text(hjust = 0.5)
  )

f2

prop_df <- xenium.obj@meta.data %>%
  dplyr::count(spatial_region) %>%
  dplyr::mutate(prop = n / sum(n))
  
  prop_df$spatial_region <- factor(
    prop_df$spatial_region,
    levels = c("glomerulus", "cortex", "outer_medulla", "inner_medulla", "papilla", "other")
  )
  
  
f3 <- ggplot(prop_df, aes(x = "", y = prop, fill = spatial_region)) +
  geom_col(width = 0.3) +
  scale_fill_manual(values = spatial_region_colors_color_blind, drop = FALSE) +
  scale_y_continuous(labels = scales::percent_format()) +
  theme_void() +
  ggtitle("Proportions") +
  theme(
    legend.title = element_blank(),
    legend.text = element_text(size = 12),
    legend.key.size = grid::unit(1, "cm"),
    legend.spacing.y = grid::unit(0.4, "cm"),
    plot.background = element_rect(fill = "white"),
    plot.title = element_text(hjust = 0.5)
  )

f3

library(patchwork)

title_style <- theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

f1 <- f1 + title_style
f2 <- f2 + title_style
f3 <- f3 + title_style

combined_plot <- f1 + f2 + f3

ggsave(
  filename = here("Week 10 XE to ggplot", "combined_plot.png"),
  plot = combined_plot,
  width = 12,
  height = 4,
  dpi = 600
)

5.7 But…what if you want more customizations?

5.7.1 Graph expression of multiple genes at once?

5.7.1.1 Receptor/Ligand Visualization

5.7.2 What if you want to color outline of cells by cell type?

5.7.3 What if you want to highlight certain cell populations?

5.7.4 I wanted more flexibility in graphing so I converted EVERYTHING into ggplot

5.7.4.1 Two Critical Functions

5.7.4.2 Extract Seurat Object Polygons and place into an sf object

library(sf)
library(dplyr)
library(purrr)
library(sp)   # for Polygons -> SpatialPolygons conversion

img <- xenium.obj2@images[["fov"]]
seg <- img@boundaries[["segmentation"]]
polygons_list <- seg@polygons

#-----------------------------------------------------------
# Helper: convert one element to an sfc geometry
#-----------------------------------------------------------
element_to_sfc <- function(elem) {
  #---------------------------------------------------------
  # Case A: sp::Polygons or sp::Polygon
  #---------------------------------------------------------
  if (inherits(elem, "Polygons") || inherits(elem, "Polygon")) {
    # Wrap in SpatialPolygons, then convert to sfc
    sp_obj <- tryCatch({
      if (inherits(elem, "Polygons")) {
        SpatialPolygons(list(elem))
      } else {
        SpatialPolygons(list(Polygons(list(elem), ID = "1")))
      }
    }, error = function(e) {
      stop("Failed to coerce sp Polygons to SpatialPolygons: ", e$message)
    })

    sfc <- st_as_sfc(sp_obj)
    return(sfc[[1]])   # return single sfc geometry
  }

  #---------------------------------------------------------
  # Case B: data.frame or matrix with coordinates
  #---------------------------------------------------------
  if (is.matrix(elem) || is.data.frame(elem)) {
    df <- as.data.frame(elem)
    colnames(df)[1:2] <- c("x", "y")

    # Ensure closed ring
    if (!(df$x[1] == df$x[nrow(df)] && df$y[1] == df$y[nrow(df)])) {
      df <- rbind(df, df[1, ])
    }

    return(
      st_polygon(list(as.matrix(df[, c("x", "y")])))
    )
  }

  #---------------------------------------------------------
  # Case C: list of coordinate matrices (multi-part polygon)
  #---------------------------------------------------------
  if (is.list(elem) && all(sapply(elem, function(x) is.matrix(x) || is.data.frame(x)))) {
    rings <- lapply(elem, function(x) {
      m <- as.matrix(as.data.frame(x)[, 1:2])

      # Close ring if needed
      if (!(m[1, 1] == m[nrow(m), 1] && m[1, 2] == m[nrow(m), 2])) {
        m <- rbind(m, m[1, , drop = FALSE])
      }

      m
    })

    # Single polygon
    if (length(rings) == 1) {
      return(st_polygon(list(rings[[1]])))
    } else {
      # Multi-polygon
      mp <- st_multipolygon(list(rings))
      return(mp)
    }
  }

  #---------------------------------------------------------
  # Unknown type
  #---------------------------------------------------------
  stop("Unknown polygon element type: ", class(elem)[1])
}

#-----------------------------------------------------------
# Convert named polygons_list -> sf
#-----------------------------------------------------------
polygons_to_sf <- function(polygons_list) {
  # Ensure names exist
  if (is.null(names(polygons_list))) {
    stop("polygons_list has no names; cannot assign cell IDs.")
  }

  rows <- imap(polygons_list, function(elem, cell_id) {
    sfc_geom <- element_to_sfc(elem)

    st_sf(
      cell_id = cell_id,
      geometry = st_sfc(sfc_geom)
    )
  })

  # Bind into one sf object
  polys_sf <- do.call(rbind, rows)

  # Most image coordinates are pixel space -> no CRS
  st_crs(polys_sf) <- NA

  polys_sf
}

#-----------------------------------------------------------
# Run conversion
#-----------------------------------------------------------
polys_sf <- polygons_to_sf(polygons_list)

#-----------------------------------------------------------
# Quick sanity checks
#-----------------------------------------------------------

# print(head(polys_sf$cell_id))

5.7.4.3 Rotate sf object coordinates

swap_xy <- function(sf_obj) {
  geom <- st_geometry(sf_obj)
  swapped <- lapply(geom, function(g) {
    coords <- st_coordinates(g)
    # rebuild geometry with swapped columns (Y becomes X, X becomes Y)
    st_polygon(list(cbind(coords[, "Y"], coords[, "X"])))
  })
  st_set_geometry(sf_obj, st_sfc(swapped, crs = st_crs(sf_obj)))
}

rot <- function(a) matrix(c(cos(a), sin(a), -sin(a), cos(a)), 2, 2)

5.7.5 Extract Expression Features From Seurat Object

meta <- xenium.obj2@meta.data
meta$cell_id <- rownames(meta)

polys_sf <- left_join(
  polys_sf,
  meta %>% select(cell_id, spatial),
  by = "cell_id"
)

expr <- FetchData(
  xenium.obj2,
  vars = c("Nphs2", "Flt1", "Piezo2", "Vegfa", "Kdr")
)

head(expr)

5.7.6 Join Expression Features to sf object

expr <- expr %>%
  rownames_to_column("cell_id")

poly_sf_matched <- polys_sf %>%
  left_join(expr, by = "cell_id")

#-----------------------------------------------------------
# Plot polygons colored by spatial cluster
#-----------------------------------------------------------\

poly_sf_swapped <- swap_xy(poly_sf_matched)

poly_sf_rot <- poly_sf_swapped
st_geometry(poly_sf_rot) <- st_geometry(poly_sf_swapped) * rot(pi/2)

5.7.7 Explore sf object

head(poly_sf_rot)

5.8 Plot in ggplot

ggplot() +
  geom_sf(
    data = poly_sf_rot,
    aes(fill = spatial),
    color = "black",
    size = 0.25
  ) +
  theme_void()

5.8.1 Wait…this is exactly the same as Seurat ImageDimPlot!

ImageDimPlot(
  xenium.obj2,
  group.by = "spatial",
  boundaries = "segmentation",
  border.color = "black"
) +
  coord_flip() +
  scale_x_reverse() +
  theme_void() +
  theme(
    panel.background = element_rect(fill = "white", color = NA),
    plot.background  = element_rect(fill = "white", color = NA)
  ) + ggtitle("")

5.8.2 WHY DID WE DO ALL THIS?

5.8.2.1 Graph outlines by cell type

ggplot() +
  geom_sf(
    data = poly_sf_rot,
    aes(color = spatial),
    fill = NA,
    size = 0.25
  ) +
  theme_void()

5.8.2.2 Layer on Expression of a Gene

ggplot() +
  geom_sf(
    data = poly_sf_rot,
    aes(fill = Nphs2, color = spatial),
    size = 0.25
  ) +
  scale_fill_gradient(
    low = "white",
    high = "red",
    name = "Nphs2"
  ) +
  theme_void()

5.8.2.3 Highlight Certain Cells by Bolding Outline?

ggplot() +
  geom_sf(
    data = poly_sf_rot,
    aes(fill = Nphs2, color = spatial, size = spatial == "PODO")
  ) +
  scale_fill_gradient(low = "white", high = "red", name = "Nphs2") +
  scale_size_manual(values = c(`TRUE` = .75, `FALSE` = 0.25), guide = "none") +
  coord_sf(expand = FALSE) +
  theme_void()

5.8.2.4 Plot Three Genes at Once!

library(ggnewscale)

ggplot() +
  # 1) Nphs2 fill layer (semi-transparent)
  geom_sf(
    data = poly_sf_rot,
    aes(fill = Nphs2),
    color = NA,
    alpha = 0.5
  ) +
  scale_fill_gradient(low = "white", high = "red", name = "Nphs2") +
  ggnewscale::new_scale_fill() +

  # 2) Flt1 fill layer
  geom_sf(
    data = poly_sf_rot,
    aes(fill = Flt1),
    color = NA,
    alpha = 0.5
  ) +
  scale_fill_gradient(low = "white", high = "blue", name = "Flt1") +
  ggnewscale::new_scale_fill() +

  # 3) Piezo2 fill layer
  geom_sf(
    data = poly_sf_rot,
    aes(fill = Piezo2),
    color = NA,
    alpha = 0.5
  ) +
  scale_fill_gradient(low = "white", high = "green", name = "Piezo2") +

  # Outline layer
  geom_sf(
    data = poly_sf_rot,
    aes(color = spatial, size = (spatial == "GEC")),
    fill = NA
  ) +
  scale_size_manual(values = c(`TRUE` = .5, `FALSE` = 0.5), guide = "none") +
  scale_color_manual(values = c("PODO" = "black", "other" = "gray40")) + 
  coord_sf(expand = FALSE) +
  theme_void()

5.8.2.5 Receptor Ligand Pairs?

ggplot() +

  # Vegfa fill layer (GREEN)
  geom_sf(
    data = poly_sf_rot,
    aes(fill = Vegfa),
    color = NA,
    alpha = 0.45
  ) +
  scale_fill_gradient(low = "white", high = "green", name = "Vegfa") +
  ggnewscale::new_scale_fill() +

  # Kdr fill layer (RED)
  geom_sf(
    data = poly_sf_rot,
    aes(fill = Kdr),
    color = NA,
    alpha = 0.45
  ) +
  scale_fill_gradient(low = "white", high = "red", name = "Kdr") +

  # Outline layer (thicker for PODO and GEC)
  geom_sf(
    data = poly_sf_rot,
    aes(size = spatial %in% c("PODO", "GEC")),
    fill = NA,
    color = "black"
  ) +
  scale_size_manual(
    values = c(`TRUE` = 0.9, `FALSE` = 0.25),
    guide = "none"
  ) +

  coord_sf(expand = FALSE) +
  theme_void()

5.8.3 So Many Possibilities!

5.8.4 I can’t wait to see what visualizations this group creates!

5.8.4.1 Please Share!

5.9 Questions?

5.10 Community Questions

Do you have a coding problem that you’d like some support on?
Do you have a topic you’d like covered at a future meeting?

Email me:

5.11 Upcoming Schedule

5.12 Brainstorming Upcoming Topics

5.12.1 Potential Future Topics on Xenium Analysis

5.12.1.1 Spatial Cell Segmentation Algorithms

5.12.1.2 Label Transfer between snRNAseq and Xenium

5.12.1.3 Integration of Xenium and snRNAseq Datasets

5.12.1.4 Comparing Xenium and snRNAseq DEG Analysis

6 Session Info

sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=English_United States.utf8 
## [2] LC_CTYPE=English_United States.utf8   
## [3] LC_MONETARY=English_United States.utf8
## [4] LC_NUMERIC=C                          
## [5] LC_TIME=English_United States.utf8    
## 
## time zone: America/Los_Angeles
## tzcode source: internal
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] ggnewscale_0.5.2            openxlsx_4.2.8             
##  [3] patchwork_1.3.2             cowplot_1.2.0              
##  [5] glmGamPoi_1.20.0            sctransform_0.4.2          
##  [7] RColorBrewer_1.1-3          kableExtra_1.4.0           
##  [9] FNN_1.1.4.1                 rstatix_0.7.2              
## [11] sf_1.0-24                   qs_0.27.3                  
## [13] scCustomize_3.2.0           styler_1.10.3              
## [15] lubridate_1.9.4             forcats_1.0.0              
## [17] stringr_1.5.1               purrr_1.1.0                
## [19] readr_2.1.5                 tidyr_1.3.1                
## [21] tibble_3.3.0                tidyverse_2.0.0            
## [23] DESeq2_1.48.2               SummarizedExperiment_1.38.1
## [25] Biobase_2.68.0              MatrixGenerics_1.20.0      
## [27] matrixStats_1.5.0           GenomicRanges_1.60.0       
## [29] GenomeInfoDb_1.44.2         IRanges_2.42.0             
## [31] S4Vectors_0.46.0            BiocGenerics_0.54.0        
## [33] generics_0.1.4              EnhancedVolcano_1.26.0     
## [35] ggrepel_0.9.6               here_1.0.1                 
## [37] BiocManager_1.30.27         ggplot2_4.0.1              
## [39] knitr_1.50                  Seurat_5.3.0               
## [41] SeuratObject_5.2.0          sp_2.2-0                   
## [43] dplyr_1.1.4                
## 
## loaded via a namespace (and not attached):
##   [1] spatstat.sparse_3.1-0   httr_1.4.7              tools_4.5.1            
##   [4] backports_1.5.0         R6_2.6.1                lazyeval_0.2.2         
##   [7] uwot_0.2.3              withr_3.0.2             gridExtra_2.3          
##  [10] progressr_0.15.1        cli_3.6.5               textshaping_1.0.3      
##  [13] spatstat.explore_3.5-2  fastDummies_1.7.5       labeling_0.4.3         
##  [16] sass_0.4.10             S7_0.2.0                spatstat.data_3.1-8    
##  [19] proxy_0.4-29            ggridges_0.5.7          pbapply_1.7-4          
##  [22] systemfonts_1.2.3       svglite_2.2.1           R.utils_2.13.0         
##  [25] dichromat_2.0-0.1       parallelly_1.45.1       rstudioapi_0.17.1      
##  [28] shape_1.4.6.1           RApiSerialize_0.1.4     vroom_1.6.5            
##  [31] ica_1.0-3               spatstat.random_3.4-1   car_3.1-3              
##  [34] zip_2.3.3               Matrix_1.7-3            ggbeeswarm_0.7.2       
##  [37] abind_1.4-8             R.methodsS3_1.8.2       lifecycle_1.0.4        
##  [40] yaml_2.3.10             snakecase_0.11.1        carData_3.0-5          
##  [43] SparseArray_1.8.1       Rtsne_0.17              paletteer_1.6.0        
##  [46] grid_4.5.1              promises_1.3.3          crayon_1.5.3           
##  [49] miniUI_0.1.2            lattice_0.22-7          beachmat_2.24.0        
##  [52] pillar_1.11.0           future.apply_1.20.0     codetools_0.2-20       
##  [55] glue_1.8.0              spatstat.univar_3.1-4   data.table_1.17.8      
##  [58] vctrs_0.6.5             png_0.1-8               spam_2.11-1            
##  [61] gtable_0.3.6            rematch2_2.1.2          cachem_1.1.0           
##  [64] xfun_0.53               S4Arrays_1.8.1          mime_0.13              
##  [67] survival_3.8-3          units_1.0-0             fitdistrplus_1.2-4     
##  [70] ROCR_1.0-11             nlme_3.1-168            bit64_4.6.0-1          
##  [73] RcppAnnoy_0.0.22        rprojroot_2.1.1         R.cache_0.17.0         
##  [76] bslib_0.9.0             irlba_2.3.5.1           vipor_0.4.7            
##  [79] KernSmooth_2.23-26      colorspace_2.1-1        DBI_1.2.3              
##  [82] ggrastr_1.0.2           tidyselect_1.2.1        bit_4.6.0              
##  [85] compiler_4.5.1          xml2_1.4.0              DelayedArray_0.34.1    
##  [88] plotly_4.11.0           stringfish_0.17.0       scales_1.4.0           
##  [91] classInt_0.4-11         lmtest_0.9-40           digest_0.6.37          
##  [94] goftest_1.2-3           spatstat.utils_3.1-5    rmarkdown_2.29         
##  [97] XVector_0.48.0          htmltools_0.5.8.1       pkgconfig_2.0.3        
## [100] fastmap_1.2.0           rlang_1.1.6             GlobalOptions_0.1.2    
## [103] htmlwidgets_1.6.4       UCSC.utils_1.4.0        shiny_1.11.1           
## [106] farver_2.1.2            jquerylib_0.1.4         zoo_1.8-14             
## [109] jsonlite_2.0.0          BiocParallel_1.42.2     R.oo_1.27.1            
## [112] magrittr_2.0.3          Formula_1.2-5           GenomeInfoDbData_1.2.14
## [115] dotCall64_1.2           Rcpp_1.1.0              reticulate_1.43.0      
## [118] stringi_1.8.7           MASS_7.3-65             plyr_1.8.9             
## [121] parallel_4.5.1          listenv_0.9.1           deldir_2.0-4           
## [124] splines_4.5.1           tensor_1.5.1            hms_1.1.3              
## [127] circlize_0.4.16         locfit_1.5-9.12         igraph_2.1.4           
## [130] spatstat.geom_3.5-0     RcppHNSW_0.6.0          reshape2_1.4.4         
## [133] evaluate_1.0.5          RcppParallel_5.1.11-1   ggprism_1.0.7          
## [136] tzdb_0.5.0              httpuv_1.6.16           RANN_2.6.2             
## [139] polyclip_1.10-7         future_1.67.0           scattermore_1.2        
## [142] janitor_2.2.1           broom_1.0.9             xtable_1.8-4           
## [145] e1071_1.7-17            RSpectra_0.16-2         later_1.4.4            
## [148] ragg_1.5.0              viridisLite_0.4.2       class_7.3-23           
## [151] beeswarm_0.4.0          cluster_2.1.8.1         timechange_0.3.0       
## [154] globals_0.18.0