There are a wide range of packages for working for scRNA-Seq data in R, but it sometimes feels like we are working with the data at arm’s length. In this vignette, I would like to demonstrate some interactive features spanning several R packages that allow more direct engagement with data using HTML widgets. My selfish hope is that more people will use these features, and in the process work out some of the rough edges and develop more tools in this space.
The key package I want to demonstrate is crosstalk which allows
selections and filters to be shared between widgets. A number of
packages support crosstalk
, notably including plotly for plots and DT for tables. I will also
demonstrate my own langevitour package for
exploring high-dimensional data.
These widgets work in a static HTML page, which can be created using rmarkdown and shared as a file with collaborators or put on the web. A shiny server is not required.
## 2022-11-07 This code requires the development versions of langevitour and plotly.R
# remotes::install_github("pfh/langevitour")
# remotes::install_github("plotly/plotly.R")
library(Seurat)
library(ggbeeswarm)
library(crosstalk) # SharedData objects allow widgets to share selections and filters
library(ggplot2) # Can use SharedData object in place of data frame
library(plotly) # Can convert ggplots into widgets
library(DT) # Table widget, can use SharedData object
library(langevitour) # Tour high-dimensional data, can link with SharedData object
library(htmltools) # Create HTML, lay out widgets
# Use the Seurat object created during the workshop
pbmc <- readRDS("pbmc3k_final.rds")
# plotly's crosstalk implementation requires a little tweaking to work right.
plotlify <- function(plot) {
ggplotly(plot, tooltip="text") |>
style(unselected=list(marker=list(opacity=1))) |> # Don't double-fade unselected points
highlight(on="plotly_selected", off="plotly_deselect")
}
# A data frame with the data for most of the widgets we will show
df <- cbind(
pbmc@meta.data[,c("nCount_RNA","nFeature_RNA","percent.mt")],
ident=Idents(pbmc),
pbmc$umap@cell.embeddings)
# Turning a data frame into a crosstalk SharedData object allows widgets to communicate
shared <- SharedData$new(df)
# The SharedData object is used instead of the data frame
p_count <- ggplot(shared) +
aes(x="PBMC", y=nCount_RNA) +
geom_beeswarm(size=0.5, cex=0.75) +
labs(x="")
p_feature <- ggplot(shared) +
aes(x="PBMC", y=nFeature_RNA) +
geom_beeswarm(size=0.5, cex=0.75) +
labs(x="")
p_mt <- ggplot(shared) +
aes(x="PBMC", y=percent.mt) +
geom_beeswarm(size=0.5, cex=0.75) +
labs(x="")
colors <- scales::hue_pal()(9)
p_umap <- ggplot(shared) +
aes(x=UMAP_1, y=UMAP_2, color=ident) +
geom_point(size=0.5) +
scale_color_manual(values=colors) +
coord_fixed()
# A table widget, again using the SharedData object
w_table <- datatable(
shared,
width="100%", class='compact cell-border hover', extensions='Buttons',
options=list(dom='Bfrtip',buttons=c('copy','csv','excel')))
# A high-dimensional tour widget
w_langevitour <- langevitour(
pbmc$pca@cell.embeddings[,1:8], Idents(pbmc),
levelColors=colors, pointSize=1.5, link=shared,
width=800, height=500)
f_counts <- filter_slider("f_counts", "nCount_RNA filter", shared, "nCount_RNA")
# Arrange everything in a nice layout using HTML
browsable(div(
f_counts,
div(style="display: grid; grid-template-columns: 150px 150px 150px 500px;",
plotlify(p_count),
plotlify(p_feature),
plotlify(p_mt),
plotlify(p_umap)),
w_langevitour,
w_table))
Things to try:
Things to notice:
High count cells end up on a particular side of each cluster in the UMAP. We might wonder if this is real biology or an artifact of our normalization steps.
DC cells form a small protrusion in the UMAP, but they are spread out in PC space. A feature of UMAP is that it erases differences in how spread out each cluster is.
Platelets are very far from all other clusters in PC space. UMAP has also hidden this.