Skip to content

Commit 156df96

Browse files
committed
feat: add .h5ad file support for scRNA-seq data visualization
1 parent 925d6f7 commit 156df96

20 files changed

+8897
-42
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ jobs:
5656
any::clustree, any::ggwordcloud, any::ggalluvial, any::ggpubr, any::callr,
5757
any::ggforce, any::ggraph, any::ggridges, any::hexbin, any::igraph,
5858
any::scattermore, any::ggupset, any::iNEXT, any::metap, any::tidyprompt,
59-
any::terra, any::GiottoClass, any::GiottoData, any::proxyC, any::metR
59+
any::terra, any::GiottoClass, any::GiottoData, any::proxyC, any::metR,
60+
any::hdf5r
6061
6162
- uses: r-lib/actions/check-r-package@v2
6263
env:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ docs/
55
Rplots.pdf
66
inst/doc
77
notebooks/spatial/data/
8+
notebooks/data/

DESCRIPTION

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Suggests:
2929
ComplexHeatmap,
3030
Seurat (>= 5.0.0),
3131
data.table,
32+
Matrix,
3233
methods,
3334
ggalluvial,
3435
ggforce,
@@ -54,7 +55,8 @@ Suggests:
5455
knitr,
5556
terra,
5657
rmarkdown,
57-
learnr
58+
learnr,
59+
hdf5r
5860
LazyData: true
5961
LazyDataCompression: xz
6062
Remotes:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ test:
1515
# make notebooks EXECUTE=true to run the notebooks
1616
notebooks:
1717
jupyter nbconvert $(if $(DEBUG),--execute) -y \
18-
--to html notebooks/spatial/*.ipynb \
18+
--to html notebooks/*.ipynb notebooks/spatial/*.ipynb \
1919
--output-dir=pkgdown/assets $(if $(EXECUTE),--execute) \
2020
--template lab
2121

NAMESPACE

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
S3method(CellDimPlot,H5File)
34
S3method(CellDimPlot,Seurat)
5+
S3method(CellDimPlot,character)
46
S3method(CellDimPlot,giotto)
7+
S3method(CellStatPlot,H5File)
58
S3method(CellStatPlot,Seurat)
9+
S3method(CellStatPlot,character)
610
S3method(CellStatPlot,data.frame)
711
S3method(CellStatPlot,giotto)
12+
S3method(CellVelocityPlot,H5File)
813
S3method(CellVelocityPlot,Seurat)
14+
S3method(CellVelocityPlot,character)
915
S3method(CellVelocityPlot,giotto)
16+
S3method(FeatureStatPlot,H5File)
1017
S3method(FeatureStatPlot,Seurat)
18+
S3method(FeatureStatPlot,character)
1119
S3method(FeatureStatPlot,giotto)
1220
S3method(SpatDimPlot,Seurat)
1321
S3method(SpatDimPlot,giotto)

R/celldimplot.R

Lines changed: 152 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
#' Cell Dimension Reduction Plot
22
#'
33
#' @description This function creates a dimension reduction plot for a Seurat object
4-
#' or a Giotto object. It allows for various customizations such as grouping by metadata,
4+
#' a Giotto object, a path to an .h5ad file or an opened `H5File` by `hdf5r` package.
5+
#' It allows for various customizations such as grouping by metadata,
56
#' adding edges between cell neighbors, highlighting specific cells, and more.
67
#' This function is a wrapper around [plotthis::DimPlot()], which provides a
78
#' flexible way to visualize cell clusters in reduced dimensions. This function
89
#' extracts the necessary data from the Seurat or Giotto object and passes it to
910
#' [plotthis::DimPlot()].
10-
#' @param object A seurat object or a giotto object.
11+
#' @param object A seurat object, a giotto object, a path to an .h5ad file or an opened `H5File` by `hdf5r` package.
1112
#' @param reduction Name of the reduction to plot (for example, "umap").
1213
#' @param graph Specify the graph name to add edges between cell neighbors to the plot.
1314
#' @param velocity The name of velocity reduction to plot cell velocities.
@@ -30,7 +31,13 @@
3031
#' * <https://pwwang.github.io/scplotter/articles/Giotto_Visium.html>
3132
#' * <https://pwwang.github.io/scplotter/articles/Giotto_VisiumHD.html>
3233
#' * <https://pwwang.github.io/scplotter/articles/Giotto_Xenium.html>
34+
#'
3335
#' for examples of using this function with a Giotto object.
36+
#'
37+
#' And see:
38+
#' * <https://pwwang.github.io/scplotter/articles/Working_with_anndata_h5ad_files.html>
39+
#'
40+
#' for examples of using this function with .h5ad files.
3441
#' @examples
3542
#' \donttest{
3643
#' data(pancreas_sub)
@@ -157,7 +164,7 @@ CellDimPlot.giotto <- function(
157164
object, reduction = NULL, graph = NULL, group_by = NULL,
158165
spat_unit = NULL, feat_type = NULL, velocity = NULL, ...
159166
) {
160-
stopifnot("[CellDimPlot] 'group_by' is required." = !is.null(group_by))
167+
stopifnot("[CellDimPlot] 'group_by' is required for giotto objects." = !is.null(group_by))
161168

162169
spat_unit <- GiottoClass::set_default_spat_unit(
163170
gobject = object,
@@ -271,16 +278,84 @@ CellDimPlot.Seurat <- function(
271278
DimPlot(data, graph = graph, group_by = group_by, velocity = velocity, ...)
272279
}
273280

281+
#' @export
282+
CellDimPlot.character <- function(
283+
object, reduction = NULL, graph = NULL, group_by = NULL,
284+
spat_unit = NULL, feat_type = NULL, velocity = NULL,
285+
...
286+
) {
287+
if (!endsWith(object, ".h5ad")) {
288+
stop("[CellDimPlot] Currently only supports .h5ad files when called with a string/path.")
289+
}
290+
291+
object <- hdf5r::H5File$new(object, mode = "r")
292+
on.exit(object$close_all())
293+
294+
CellDimPlot.H5File(
295+
object = object, reduction = reduction, graph = graph,
296+
group_by = group_by, spat_unit = spat_unit, feat_type = feat_type,
297+
velocity = velocity, ...
298+
)
299+
}
300+
301+
#' @export
302+
CellDimPlot.H5File <- function(
303+
object, reduction = NULL, graph = NULL, group_by = NULL,
304+
spat_unit = NULL, feat_type = NULL, velocity = NULL,
305+
...
306+
) {
307+
stopifnot("[CellDimPlot] 'spat_unit' and 'feat_type' are not used for anndata." =
308+
is.null(spat_unit) && is.null(feat_type))
309+
stopifnot("[CellDimPlot] 'group_by' is required for anndata (h5ad) objects." = !is.null(group_by))
310+
311+
reductions <- names(object[['obsm']])
312+
if (is.null(reductions) || length(reductions) == 0) {
313+
stop("[CellDimPlot] The object does not have any reductions.")
314+
}
315+
316+
reduction <- reduction %||% reductions[1]
317+
if (!startsWith(reduction, "X_") && !reduction %in% reductions) {
318+
reduction <- paste0("X_", reduction)
319+
}
320+
321+
if (!reduction %in% reductions) {
322+
stop("[CellDimPlot] The object does not have reduction:", reduction)
323+
}
324+
325+
if (!is.null(graph)) {
326+
ggrp <- object[['obsp']][['connectivities']]
327+
if (is.null(ggrp)) {
328+
stop("[CellDimPlot] The object does not have any graph/connectivities.")
329+
}
330+
331+
graph <- h5group_to_matrix(ggrp)
332+
graph <- igraph::graph_from_adjacency_matrix(graph, mode = "undirected", weighted = TRUE)
333+
graph <- igraph::as_adjacency_matrix(graph, attr = "weight", sparse = TRUE)
334+
colnames(graph) <- rownames(graph) <- object[['obs']][['index']]$read()
335+
}
336+
337+
data <- cbind(t(object[["obsm"]][[reduction]]$read()), h5group_to_dataframe(object[['obs']]))
338+
rownames(data) <- object[['obs']][['index']]$read()
339+
340+
if (!is.null(velocity)) {
341+
velocity <- t(object[["obsm"]][[velocity]]$read())
342+
}
343+
344+
DimPlot(data, graph = graph, group_by = group_by, velocity = velocity, ...)
345+
}
346+
347+
274348
#' Cell Velocity Plot
275349
#'
276-
#' @description This function creates a cell velocity plot for a Seurat object
277-
#' or a Giotto object. It allows for various customizations such as grouping by metadata,
350+
#' @description This function creates a cell velocity plot for a Seurat object,
351+
#' a Giotto object, a path to an .h5ad file or an opened `H5File` by `hdf5r` package.
352+
#' It allows for various customizations such as grouping by metadata,
278353
#' adding edges between cell neighbors, highlighting specific cells, and more.
279354
#' This function is a wrapper around [plotthis::VelocityPlot()], which provides a
280355
#' flexible way to visualize cell velocities in reduced dimensions. This function
281356
#' extracts the cell embeddings and velocity embeddings from the Seurat or Giotto object
282357
#' and passes them to [plotthis::VelocityPlot()].
283-
#' @param object A seurat object or a giotto object.
358+
#' @param object A seurat object, a giotto object, a path to an .h5ad file or an opened `H5File` by `hdf5r` package.
284359
#' @param reduction Name of the reduction to plot (for example, "umap").
285360
#' @param v_reduction Name of the velocity reduction to plot (for example, "stochastic_umap").
286361
#' It should be the same as the reduction used to calculate the velocity.
@@ -293,6 +368,10 @@ CellDimPlot.Seurat <- function(
293368
#' @seealso [CellDimPlot()]
294369
#' @importFrom SeuratObject DefaultDimReduc Embeddings Reductions
295370
#' @importFrom plotthis VelocityPlot
371+
#' @details See:
372+
#' * <https://pwwang.github.io/scplotter/articles/Working_with_anndata_h5ad_files.html>
373+
#'
374+
#' for examples of using this function with .h5ad files.
296375
#' @examples
297376
#' \donttest{
298377
#' data(pancreas_sub)
@@ -402,3 +481,70 @@ CellVelocityPlot.Seurat <- function(
402481
...
403482
)
404483
}
484+
485+
486+
#' @export
487+
CellVelocityPlot.character <- function(
488+
object, reduction, v_reduction, spat_unit = NULL, feat_type = NULL, group_by = NULL, ...
489+
) {
490+
if (!endsWith(object, ".h5ad")) {
491+
stop("[CellVelocityPlot] Currently only supports .h5ad files when called with a string/path.")
492+
}
493+
494+
object <- hdf5r::H5File$new(object, mode = "r")
495+
on.exit(object$close_all())
496+
497+
CellVelocityPlot.H5File(
498+
object = object, reduction = reduction, v_reduction = v_reduction,
499+
spat_unit = spat_unit, feat_type = feat_type, group_by = group_by, ...
500+
)
501+
}
502+
503+
#' @export
504+
CellVelocityPlot.H5File <- function(
505+
object, reduction, v_reduction, spat_unit = NULL, feat_type = NULL, group_by = NULL, ...
506+
) {
507+
stopifnot("[CellVelocityPlot] 'spat_unit' and 'feat_type' are not used for anndata." =
508+
is.null(spat_unit) && is.null(feat_type))
509+
510+
reduc_info <- names(object[['obsm']])
511+
if (is.null(reduc_info) || length(reduc_info) == 0) {
512+
stop("[CellVelocityPlot] The object does not have any reductions.")
513+
}
514+
515+
if (!startsWith(reduction, "X_") && !reduction %in% reduc_info) {
516+
reduction <- paste0("X_", reduction)
517+
}
518+
519+
if (!reduction %in% reduc_info) {
520+
stop("[CellVelocityPlot] The object does not have reduction:", reduction)
521+
}
522+
if (!v_reduction %in% reduc_info) {
523+
stop("[CellVelocityPlot] The object does not have velocity reduction:", v_reduction)
524+
}
525+
526+
if (!is.null(group_by)) {
527+
metakeys <- names(object[['obs']])
528+
if (!group_by %in% metakeys) {
529+
stop("[CellVelocityPlot] The object does not have metadata column:", group_by)
530+
}
531+
if (inherits(object[['obs']][[group_by]], "H5Group") &&
532+
setequal(names(object[['obs']][[group_by]]), c("categories", "codes"))) {
533+
codes <- object[['obs']][[group_by]][['codes']]$read()
534+
categories <- object[['obs']][[group_by]][['categories']]$read()
535+
group_by <- factor(categories[codes + 1], levels = categories)
536+
} else if (inherits(object[['obs']][[group_by]], "H5Group")) {
537+
stop("[CellVelocityPlot] Complex data structure detected in metadata (obs) column:", group_by)
538+
} else {
539+
# Read the metadata column directly
540+
group_by <- object[['obs']][[group_by]]$read()
541+
}
542+
}
543+
544+
VelocityPlot(
545+
embedding = t(object[["obsm"]][[reduction]]$read())[, 1:2, drop = FALSE],
546+
v_embedding = t(object[["obsm"]][[v_reduction]]$read())[, 1:2, drop = FALSE],
547+
group_by = group_by,
548+
...
549+
)
550+
}

R/cellstatplot.R

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
#' Cell statistics plot
22
#'
3-
#' @description This function creates a plot to visualize the statistics of cells in a Seurat object or a Giotto object.
3+
#' @description This function creates a plot to visualize the statistics of cells in a Seurat object, a Giotto object,
4+
#' a path to an .h5ad file or an opened `H5File` by `hdf5r` package.
45
#' It can create various types of plots, including bar plots, circos plots, pie charts, pies (heatmap with cell_type = 'pie'), ring/donut plots, trend plots
56
#' area plots, sankey/alluvial plots, heatmaps, radar plots, spider plots, violin plots, and box plots.
67
#' The function allows for grouping, splitting, and faceting the data based on metadata columns.
78
#' It also supports calculating fractions of cells based on specified groupings.#'
8-
#' @param object A Seurat object, a Giotto object, or a data frame (for internal use) containing cell metadata.
9+
#' @param object A Seurat object, a Giotto object, a path to h5ad file or an opened `H5File` (from `hdf5r` package) object a data frame (for internal use) containing cell metadata.
910
#' @param ident The column with the cell identities. i.e. clusters. Default: NULL
1011
#' If NULL, the active identity of the Seurat object and the name "Identity" will be used.
1112
#' For 'pies', this will be used as the `pie_group_by`.
@@ -76,9 +77,15 @@
7677
#' @importFrom dplyr %>% summarise mutate ungroup n
7778
#' @importFrom tidyr drop_na pivot_wider pivot_longer
7879
#' @importFrom plotthis BarPlot CircosPlot PieChart RingPlot TrendPlot AreaPlot SankeyPlot Heatmap RadarPlot SpiderPlot ViolinPlot BoxPlot
79-
#' @details See
80+
#' @details See:
8081
#' * <https://pwwang.github.io/scplotter/articles/Giotto_Xenium.html>
82+
#'
8183
#' for examples of using this function with a Giotto object.
84+
#'
85+
#' And see:
86+
#' * <https://pwwang.github.io/scplotter/articles/Working_with_anndata_h5ad_files.html>
87+
#'
88+
#' for examples of using this function with .h5ad files.
8289
#' @export
8390
#' @examples
8491
#' \donttest{
@@ -231,6 +238,53 @@ CellStatPlot.Seurat <- function(
231238
)
232239
}
233240

241+
#' @export
242+
CellStatPlot.character <- function(
243+
object, ident = NULL, group_by = NULL, group_by_sep = "_", spat_unit = NULL, feat_type = NULL,
244+
split_by = NULL, split_by_sep = "_", facet_by = NULL, rows = NULL, columns_split_by = NULL,
245+
frac = c("none", "group", "ident", "cluster", "all"), rows_name = NULL, name = NULL,
246+
plot_type = c("bar", "circos", "pie", "pies", "ring", "donut", "trend", "area", "sankey", "alluvial", "heatmap", "radar", "spider", "violin", "box"),
247+
swap = FALSE, ylab = NULL, ...
248+
) {
249+
if (!endsWith(object, ".h5ad")) {
250+
stop("[CellStatPlot] Currently only supports .h5ad files when called with a string/path.")
251+
}
252+
253+
object <- hdf5r::H5File$new(object, mode = "r")
254+
on.exit(object$close_all())
255+
256+
CellStatPlot.H5File(
257+
object, ident = ident, group_by = group_by, group_by_sep = group_by_sep,
258+
spat_unit = spat_unit, feat_type = feat_type,
259+
split_by = split_by, split_by_sep = split_by_sep, facet_by = facet_by,
260+
rows = rows, columns_split_by = columns_split_by, frac = frac,
261+
rows_name = rows_name, name = name, plot_type = plot_type,
262+
swap = swap, ylab = ylab, ...
263+
)
264+
}
265+
266+
#' @export
267+
CellStatPlot.H5File <- function(
268+
object, ident = NULL, group_by = NULL, group_by_sep = "_", spat_unit = NULL, feat_type = NULL,
269+
split_by = NULL, split_by_sep = "_", facet_by = NULL, rows = NULL, columns_split_by = NULL,
270+
frac = c("none", "group", "ident", "cluster", "all"), rows_name = NULL, name = NULL,
271+
plot_type = c("bar", "circos", "pie", "pies", "ring", "donut", "trend", "area", "sankey", "alluvial", "heatmap", "radar", "spider", "violin", "box"),
272+
swap = FALSE, ylab = NULL, ...
273+
) {
274+
stopifnot("[CellStatPlot] 'ident' is required for anndata (h5ad) object." = !is.null(ident))
275+
276+
object <- h5group_to_dataframe(object[["obs"]])
277+
278+
CellStatPlot.data.frame(
279+
object, ident = ident, group_by = group_by, group_by_sep = group_by_sep,
280+
spat_unit = spat_unit, feat_type = feat_type,
281+
split_by = split_by, split_by_sep = split_by_sep, facet_by = facet_by,
282+
rows = rows, columns_split_by = columns_split_by, frac = frac,
283+
rows_name = rows_name, name = name, plot_type = plot_type,
284+
swap = swap, ylab = ylab, ...
285+
)
286+
}
287+
234288
#' @export
235289
CellStatPlot.data.frame <- function(
236290
object, ident = NULL, group_by = NULL, group_by_sep = "_", spat_unit = NULL, feat_type = NULL,

0 commit comments

Comments
 (0)