---
title: "AnnData Stores"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{AnnData Stores}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  out.width = "100%"
)
library(pizzarr)

has_blosc <- requireNamespace("blosc", quietly = TRUE)

root <- paste0("https://data-1.vitessce.io/0.0.33/main/",
               "human-lymph-node-10x-visium/human_lymph_node_10x_visium.h5ad.zarr")

online <- tryCatch({
  con <- url(paste0(root, "/.zgroup"))
  on.exit(try(close(con), silent = TRUE))
  length(readLines(con, warn = FALSE)) > 0
}, error = function(e) FALSE, warning = function(w) FALSE)
```

[AnnData](https://anndata.readthedocs.io/) is the convention single-cell and
spatial biology tools use to keep a measurement matrix together with its
annotations. Written to Zarr, it becomes a group hierarchy with fixed member
names: `X` holds the main matrix, `obs` describes the observations (cells or
spots), `var` describes the variables (genes), and `obsm` holds alternative
per-observation representations such as embeddings.

Reading one is a matter of knowing those names. The example here is a 10x
Visium human lymph node section published by
[Vitessce](http://vitessce.io/), read over HTTPS — see
`vignette("remote-stores")` for connection details.

## Opening the store

This store publishes no consolidated metadata, so pizzarr cannot list what it
contains — an HTTP server offers no equivalent of `ls`. That is the normal case
for AnnData stores, and it is workable only because the member names are
fixed by the convention: you address `obs`, `obsm`, and `X` by name rather than
discovering them.

```{r, eval = has_blosc && online, message = FALSE}
g <- zarr_open_group(HttpStore$new(root))
```

## Observation annotations

`obs` is a group, not an array. Which of its members holds the observation
identifiers is recorded in its `_index` attribute rather than fixed by name,
so the lookup goes through the attributes:

```{r, eval = has_blosc && online}
obs_attrs <- g$get_item("obs")$get_attrs()$to_list()
index_colname <- obs_attrs[["_index"]]
index_colname
```

```{r, eval = has_blosc && online}
index_arr <- g$get_item(paste0("obs/", index_colname))$get_item("...")$data

length(index_arr)
head(index_arr, 3)
```

Those are barcodes identifying the 3861 spots on the slide. Cluster assignments
live alongside them under the same group:

```{r, eval = has_blosc && online}
cluster_arr <- g$get_item("obs/clusters")$get_item("...")$data

table(cluster_arr)
```

## Embeddings

`obsm` holds per-observation matrices — one row per spot, as many columns as the
representation needs. A UMAP embedding is two columns:

```{r, eval = has_blosc && online}
umap_arr <- g$get_item("obsm/X_umap")$get_item("...")$data

dim(umap_arr)
```

Plotting the embedding coloured by cluster gives the standard view of this kind
of dataset:

```{r, eval = has_blosc && online, fig.alt = "UMAP embedding of 3861 Visium spots, coloured by cluster assignment."}
plot(umap_arr[, 1], umap_arr[, 2],
     col = as.integer(as.factor(cluster_arr)),
     pch = 19, cex = 0.4,
     xlab = "UMAP 1", ylab = "UMAP 2")
```
