-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBoneMarrowMap_Tutorial.Rmd
More file actions
355 lines (272 loc) · 13.7 KB
/
Copy pathBoneMarrowMap_Tutorial.Rmd
File metadata and controls
355 lines (272 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
---
title: "Bone Marrow Reference Map Projection Tutorial"
output: html_notebook
---
Here we will map an example dataset from Roy et al (Cell Rep 2019) comprising 12,245 CD34+ cells from fetal and adult bone marrow.
Note that any human hematopoietic cells, regardless of tissue source or disease status, can be mapped to this reference.
This entire tutorial should take less than 10 minutes to run.
### Setup
```{r}
library(Seurat)
library(tidyverse)
library(symphony)
library(ggpubr)
library(patchwork)
```
Install package from github
```{r, eval=FALSE}
## install dependencies that are not on CRAN
if(!require(BiocManager, quietly = TRUE)) install.packages("BiocManager")
BiocManager::install(c("AUCell", "doMC", "BiocNeighbors"))
if(!require(devtools, quietly = TRUE)) install.packages("devtools")
devtools::install_github("jaredhuling/jcolors")
```
```{r, eval=FALSE}
## install BoneMarrowMap package
devtools::install_github('andygxzeng/BoneMarrowMap', force = TRUE)
```
```{r}
library(BoneMarrowMap)
```
#### Download reference object and UMAP model
Set projection folder, Download reference object and UMAP model
```{r, eval=FALSE}
# Set directory to store projection reference files
projection_path = './'
# Download Bone Marrow Reference - 344 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/BoneMarrowMap_SymphonyReference.rds',
destfile = paste0(projection_path, 'BoneMarrowMap_SymphonyReference.rds'))
# Download uwot model file - 221 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/BoneMarrow_RefMap_uwot_model.uwot',
destfile = paste0(projection_path, 'BoneMarrowMap_uwot_model.uwot'))
```
```{r}
projection_path = './'
# Load Symphony reference
ref <- readRDS(paste0(projection_path, 'BoneMarrowMap_SymphonyReference.rds'))
# Set uwot path for UMAP projection
ref$save_uwot_path <- paste0(projection_path, 'BoneMarrowMap_uwot_model.uwot')
```
#### Visualize Bone Marrow Reference
If we want to visualize celltype labels or metadata from the BM Reference, we can create a Seurat Object from the symphony reference This will be memory efficient as it will not include gene expression counts, only the UMAP coordinates and the metadata including cell labels and sorting information
```{r, fig.height=5, fig.width=11}
ReferenceSeuratObj <- create_ReferenceObject(ref)
DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CellType_Annotation_formatted',
raster=FALSE, label=TRUE, label.size = 4) + NoAxes()
```
We can also visualize broader cell type labels which may simplify interpretation and downstream analysis.
```{r, fig.height=5, fig.width=11}
DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CellType_Broad',
raster=FALSE, label=TRUE, label.size = 4)
```
We can visualize other annotations too, including cell cycle phase and lineage pseudotime estimates.
```{r, fig.height=3.5, fig.width=11}
p1 <- DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CyclePhase', raster=FALSE) + NoAxes()
p2 <- FeaturePlot(ReferenceSeuratObj, reduction = 'umap', features = 'Pseudotime', raster=FALSE) + NoAxes()
p1 + p2
```
#### Load Query Seurat object
Example Query object is a subset of data from Roy et al (Cell Reports 2021), incorporating CD34p cells from an adult bone marrow sample and a fetal bone marrow sample.
Here, we prefer raw count data without normalization.
Input does not need to be a seurat object, can load raw count matrix and metadata separately
```{r, eval=FALSE}
# Load example data from Roy et al - 141 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/ExampleQuery_Roy2021.rds',
destfile = paste0(projection_path, 'ExampleQuery_Roy2021.rds'))
```
```{r}
# Load seurat object
query <- readRDS(paste0(projection_path, 'ExampleQuery_Roy2021.rds'))
query
```
### Map the Query Data
Provide raw counts, metadata, and donor key. This should take <1 min.
Calculate mapping error and perform QC to remove low quality cells with high mapping error
```{r}
# batch variable to correct in the query data, set as NULL if no batches in query
batchvar <- 'sampleID'
# Map query dataset using Symphony (Kang et al 2021)
query <- map_Query(
exp_query = query[['RNA']]@counts,
metadata_query = query@meta.data,
ref_obj = ref,
vars = batchvar
)
```
Now that the data is mapped, we will evaluate the mapping QC metrics and flag cells with high mapping errors
```{r, fig.height=3, fig.width=10}
# Run QC based on mapping error score, flag cells with mapping error >= 2.5 MADs above median
query <- query %>% calculate_MappingError(., reference = ref, MAD_threshold = 2.5)
# Get QC Plots
QC_plots <- plot_MappingErrorQC(query)
# Plot together - If this is too crowded, can also just call "QC_plots" aloneto display one by one
patchwork::wrap_plots(QC_plots, ncol = 4, widths = c(0.8, 0.3, 0.8, 0.3))
```
This important step identifies a subset of cells with high mapping error from the query dataset that are either:
- not present within the reference, or
- have poor QC metrics (low RNA counts and low transcriptional diversity)
Sometimes, low quality cells may erroneously map to the orthochromatic erythroblast region as this cell type has very low transcriptional diversity.
These low quality query cells do not have hemoglobin expression and are in fact mis-mapped; they will be flagged by the QC filter and excluded from cell type assignments.
**Please adjust the MAD_threshold (typically between 1 and 3) based on the distribution of your dataset to identify the outliers with low quality and high mapping error scores. This will improve your classifications and any downstream composition analysis**
```{r}
# # Optional step - remove outliers with high mapping error
# query <- subset(query, mapping_error_QC == 'Pass')
```
Optionally, outlier cells with high mapping error can also be removed at this stage.
For ease of integrating these mapped annotations with the rest of your analysis, we also choose to skip this step.
If so, Final CellType and Pseudotime predictions will be assigned as NA for cells failing the mapping error QC threshold.
### Cell Type Assignments
We will next use a KNN classifier to assign cell identity based on the 30 K-Nearest Neighbours from the reference map.
Broader cell type labels will also be transferred automatically along with the precise cell type labels.
This label transfer step should take <1 min for 10,000 cells and <5 min for 100,000 cells.
```{r, fig.height=3.5, fig.width=7}
# Predict Hematopoietic Cell Types by KNN classification
query <- predict_CellTypes(
query_obj = query,
ref_obj = ref,
initial_label = 'initial_CellType', # celltype assignments before filtering on mapping QC
final_label = 'predicted_CellType' # celltype assignments with map QC failing cells assigned as NA
)
DimPlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap_projected', group.by = c('predicted_CellType'),
raster=FALSE, label=TRUE, label.size = 4)
```
We can also visualize the broader cell type categories in case precise labels are too granular.
These provide a simpler view of the data and can help guide cluster annotations if your dataset does not have many cells.
```{r, fig.height=3, fig.width=6}
DimPlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap_projected', group.by = c('predicted_CellType_Broad'),
raster=FALSE, label=TRUE, label.size = 4)
```
#### Pseudotime Annotations
We can also annotate each query cell based on their position along hematopoietic pseudotime.
Query cells will be assigned a pseudotime score based on the 30 K-Nearest Neighbours from the reference map.
Since our Pseudotime KNN assignments are performed in UMAP space (more accurate than KNN on harmony components), this step is very fast (< 10s)
```{r}
# Predict Pseudotime values by KNN
query <- predict_Pseudotime(
query_obj = query,
ref_obj = ref,
initial_label = 'initial_Pseudotime', # pseudotime assignments before filtering on mapping QC
final_label = 'predicted_Pseudotime' # pseudotime assignments with map QC failing cells assigned as NA
)
# Visualize Hematopoietic Pseudotime in query data
FeaturePlot(subset(query, mapping_error_QC == 'Pass'), features = c('predicted_Pseudotime'))
```
### Save projection results
This will save a csv file with the mapped annotations for each cell (mapping error scores, umap coordinates, predicted Cell Type, and predicted Pseudotime)
```{r}
# Save CellType Annotations and Projected UMAP coordinates
save_ProjectionResults(
query_obj = query,
celltype_label = 'predicted_CellType',
celltype_KNNprob_label = 'predicted_CellType_prob',
pseudotime_label = 'predicted_Pseudotime',
file_name = 'querydata_projected_labeled.csv')
```
### Visualize Projection Density
Now let's visualize the density distribution of query cells across the hematopoietic hierarchy
```{r, fig.height=3, fig.width=9}
# Set batch/condition to be visualized individually
batch_key <- 'sampleID'
# returns a list of plots for each donor from a pre-specified batch variable
projection_plots <- plot_Projection_byDonor(
query_obj = query,
batch_key = batch_key,
ref_obj = ref,
Hierarchy_only = FALSE, # Whether to exclude T/NK/Plasma/Stromal cells
downsample_reference = TRUE,
downsample_frac = 0.25, # down-sample reference cells to 25%; reduces figure file size
query_point_size = 0.2, # adjust size of query cells based on # of cells
saveplot = TRUE,
save_folder = 'projectionFigures/'
)
# show plots together with patchwork. Can also just call "projection_plots" object to display one-by-one
patchwork::wrap_plots(projection_plots, ncol = 2)
```
We can also set Hierarchy_only = TRUE to remove T/NK/Plasma/Stromal cells and focus solely on the hematopoietic hierarchy.
```{r, fig.height=3, fig.width=8}
# Set batch/condition to be visualized individually
batch_key <- 'sampleID'
# returns a list of plots for each donor from a pre-specified batch variable
projection_plots <- plot_Projection_byDonor(
query_obj = query,
batch_key = batch_key,
ref_obj = ref,
Hierarchy_only = TRUE, # Whether to exclude T/NK/Plasma/Stromal cells
downsample_reference = TRUE,
downsample_frac = 0.25, # down-sample reference cells to 25%; reduces figure file size
query_point_size = 0.2, # adjust size of query cells based on # of cells
saveplot = TRUE,
save_folder = 'projectionFigures/'
)
# show plots together with patchwork. Can also just call "projection_plots" object to display one-by-one
patchwork::wrap_plots(projection_plots, ncol = 2)
```
Note how cell composition differs between CD34p sorted Adult Bone Marrow and Fetal Bone Marrow within the Roy 2021 dataset.
Notably, Adult bone marrow is enriched for more early HSC/MPP and LMPP cells and also MEP & Early Erythroid lineages.
In contrast, Fetal bone marrow is highly enriched for MLP and B cell progenitors.
We can also compare the mapping of these samples along Hematopoietic pseudotime:
### Get Composition data for each donor
Here, to study the abundance of each cell type within each donor, I focus on cells that were classified with a KNN prob \> 0.5 (that is, \>50% of nearest neighbours from the reference map agree on the assigned cell type).
We can present this as a long table
```{r}
query_composition <- get_Composition(
query_obj = query,
donor_key = 'sampleID',
celltype_label = 'predicted_CellType',
mapQC_col = 'mapping_error_QC',
knn_prob_cutoff = NULL,
return_type = 'long')
query_composition
```
Or as a wide table with counts of \# of cells
```{r}
query_composition <- get_Composition(
query_obj = query,
donor_key = 'sampleID',
celltype_label = 'predicted_CellType',
mapQC_col = 'mapping_error_QC',
knn_prob_cutoff = NULL,
return_type = 'count')
query_composition
```
Or as a wide table with proportion of each cell type within each donor
```{r}
query_composition <- get_Composition(
query_obj = query,
donor_key = 'sampleID',
celltype_label = 'predicted_CellType',
mapQC_col = 'mapping_error_QC',
knn_prob_cutoff = NULL,
return_type = 'proportion')
query_composition
```
```{r}
# Simple heatmap to visualize composition of projected samples
p <- query_composition %>% column_to_rownames('sampleID') %>% data.matrix() %>% ComplexHeatmap::Heatmap()
p
```
### Guided Annotation of unsupervised UMAPs
Now you can perform standard dimensionality reduction and clustering on your sample in an unsupervised manner and use the projected labels to guide your analysis and cell type annotations.
Here we will start with a basic dimensionality reduction to visualize how the CD34+ cells are organized within these samples.
```{r, fig.height=3.5, fig.width=8}
query <- query %>%
SCTransform() %>%
RunPCA() %>%
harmony::RunHarmony(group.by.vars = 'sampleID') %>%
RunUMAP(reduction = 'harmony', dims = 1:20)
```
```{r, fig.height=3.5, fig.width=8}
DimPlot(query, reduction = 'umap', group.by = c('predicted_CellType'), label=TRUE, label.size = 4)
```
```{r, fig.height=3.5, fig.width=10}
p1 <- DimPlot(query, reduction = 'umap', group.by = c('sampleID'), label=TRUE, label.size = 4)
p2 <- FeaturePlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap', features = c('predicted_Pseudotime'), label.size = 4, max.cutoff = 'q95')
p1 | p2
```
We can identify a clear cluster of HSCs and observe maturation along the lymphoid, myeloid, and Mk/Ery lineages based on our projected Pseudotime estimates.
To simplify downstream analysis, we can also visualize broader cell type labels below on the new umap as well as the projected UMAP.
```{r, fig.height=3.5, fig.width=11}
p1 <- DimPlot(query, reduction = 'umap', group.by = c('predicted_CellType_Broad'), label=TRUE, label.size = 4)
p2 <- DimPlot(query, reduction = 'umap_projected', group.by = c('predicted_CellType_Broad'), label=TRUE, label.size = 4)
p1 | p2
```