OSCR

A scalable human-zebrafish xenotransplantation model reveals gastrosome-mediated processing of dying neurons by human microglia.

Code ↔ Paper

2 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 2 matches
  1. [1] § Materials and methods › Data analysis › RNA sequencing ↔ 03_custom/01_custom_analysis_postmeeting.Rmd, lines 104–171 · score 1.00 · online pathway databases, Multi dimensional scaling, leading fold change, GRCh38 assembly, domain expert, unsupervised manner
  2. [2] § Materials and methods › Maintenance of iPSC and generation of fluorescent reporter lines ↔ 00_get_data/00_get_data.sh, lines 53–101 · score 0.70 · pluripotent stem cell, iPSC, Nat, WTC, days, human

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

R Markdown · 2,286 lines · 78 KB · GPL-3.0 · 1 match

  1. ---
  2. title: "Ambra Villani's microglia RNA-seq"
  3. author: "Izaskun Mallona, Mark D. Robinson's lab, UZH"
  4. date: "`r format(Sys.time(), '%d %B, %Y')`"
  5. output:
  6. html_document:
  7. toc: true
  8. toc_float: true
  9. code_folding: hide
  10. code_download: true
  11. number_sections: true
  12. df_print: kable
  13. theme: lumen
  14. params:
  15. seed: 665
  16. ---
  17. ```{r warning = FALSE, message = FALSE}
  18. ## .libPaths('/home/imallona/R/R4_bioc314')
  19. library(ggplot2)
  20. ## library(EnhancedVolcano)
  21. ## library(iSEE)
  22. ## library(knitr)
  23. ## library(DRIMSeq)
  24. ## library(EnsDb.Hsapiens.v86)
  25. library(SingleCellExperiment)
  26. library(corrplot)
  27. #library(GGally)
  28. #library(scuttle)
  29. #library(knitr)
  30. #library(greybox) # cramer's distance
  31. #library(viridis)
  32. #library(regioneR)
  33. #library(GenomicRanges)
  34. #library(rtracklayer)
  35. ## camera start
  36. ## library(dplyr)
  37. ## library(msigdbr)
  38. ## library(CAMERA)
  39. #require(topGO)
  40. #require(org.Hs.eg.db)
  41. ## library("ggVennDiagram")
  42. library(recount3)
  43. library(tibble)
  44. library(dplyr)
  45. ## library(tidyr)
  46. library(limma)
  47. library(edgeR)
  48. library(sva)
  49. library(DT)
  50. ## library(reshape2)
  51. ```
  52. ```{r edgeR-load-pkg}
  53. suppressPackageStartupMessages({
  54. library(dplyr)
  55. library(tximport)
  56. library(tximeta)
  57. library(SingleCellExperiment)
  58. library(edgeR)
  59. library(ggplot2)
  60. library(msigdbr)
  61. library(EnhancedVolcano)
  62. })
  63. ```
  64. ```{r}
  65. ## render on error
  66. knitr::knit_hooks$set(error = function(x, options) {
  67. knitr::knit_exit()
  68. })
  69. ```
  70. ```{r}
  71. knitr::opts_chunk$set(fig.width = 5,
  72. fig.height = 5,
  73. cache = TRUE,
  74. include = TRUE,
  75. fig.path = "plots_post/",
  76. dev = c("png", "svg"),
  77. cache.lazy = FALSE,
  78. warning = TRUE,
  79. message = TRUE)
  80. ## plan("multiprocess", workers = NTHREADS)
  81. ## options(future.globals.maxSize= 1.4e9)
  82. ## options(bitmapType='cairo')
  83. ```
  84. # Aim
  85. > Compare to published datasets (see block1, 2 and 3). The highest priority,m is to compare to those in block 1 (from Abud et al.).
  86. Methods: We processed our RNA-seq, McQuade's TREM raw reads with ARMOR (PMID 31088905). Briefly, reads were aligned and counted to the human genome (GRCh38 assembly and Gencode release 43) with salmon v1.4.0 and with STAR 2.7.7a. Differential expression analysis was carried out with edgeR v3.36.0 using salmon outputs.
  87. We downloaded Abud's and McQuade's iPSCs data from the Sequence Read Archive (SRA) accessions SRP092075 and SRP155574, respectively, using recount3 (PMID 34844637) and GRCh38 as reference genome and Gencode's annotation.
  88. Results: We detect noticeable age effects (within Villani's WTs), relatively weaker mutation effects (within Villani's mutant), and batch effects when comparing to Abud's, McQuade's data. The MDS plot after batch correction of Villani's WT + Villani's mutants + Abud's looks the best; the same MDS without Villani's mutants not so.
  89. See `Comparison of Villani's, Abud's, McQuade's data`.
  90. > Do they express typical microglial genes (I have a list)
  91. Methods: We generated multi-dimensional scaling (MDS) plots based on normalized count data using edgeR v3.36.0. Briefly, MDS plots depict similarities between samples (including replicates and batches) in an unsupervised manner, depicting the two leading fold-change dimensions which explain the largest proportion of variation in gene expression across samples.
  92. Results: Please use the iSEE deployments:
  93. - http://imlspenticton.uzh.ch:3740/ambra_villani_microglia_only_2023/
  94. - http://imlspenticton.uzh.ch:3740/ambra_villani_microglia_plus_abud_2023/
  95. - http://imlspenticton.uzh.ch:3740/ambra_villani_microglia_plus_abud_and_mcquade_2023/
  96. Mind the iSEE deployments show 'counts', 'CPMs' and 'logCPMs'. My recommendation is to check 'CPMs' or 'logCPMs', so readouts are already normalized/don't depend on sequencing depth. CPMs and logCPMs were calculated with `edgeR::cpm` and with `prior.count = 2`.
  97. > They express genes relevant in neurodegenerative disorders (I have a list)
  98. Results: See the iSEE deployments above.
  99. > 1. The different mutants still becomes microglia (add to the MDS?)
  100. > a. Slc37a2 I have 2 different mutants (clones) same gene (but different mutation)
  101. > b. TREM2 I just have 1 mutant
  102. >2. They are indeed mutants
  103. > a. RNA level reduced compared to WT. As WT for comparison use the 2 from av165 (same round of differentiation)
  104. > b. Alignment to WT sequence?
  105. Results: please browse the STAR BAM files.
  106. > 3. The TREM2 mutant has the typical signature of microglia missing this gene according to literature
  107. > a. Compare to McQuade TREM2 KO (highlighted in yellow in the spreadsheet)
  108. > b. Lipid metabolism affected (expected)
  109. Methods: We ran a geneset enrichment analysis using `camera` from limma v3.50.3 (PMID PMC3458527) and [mSigDB](http://software.broadinstitute.org/gsea/msigdb) annotations on differentially expressed genes. Selected genesets included:
  110. <!-- - H, hallmark gene sets -->
  111. <!-- - C1, positional gene sets (cytobands) -->
  112. - C2, curated gene sets from online pathway databases, publications in PubMed, and knowledge of domain experts
  113. <!-- - C3, regulatory target gene sets based on gene target predictions for microRNA seed sequences and predicted transcription factor binding sites -->
  114. - C5, Gene Ontology
  115. - C8, cell type signatures
  116. Results: please check sections named `Geneset analysis`.
  117. # Notes
  118. Plots in PNG and SVG format <a href="plots_post/">can be browsed and downloaded here</a>.
  119. # Comparison of Villani's, Abud's, McQuade's data
  120. ## Metadata
  121. ```{r}
  122. options(ggplot2.discrete.fill = function() scale_fill_brewer(palette = "Set3"))
  123. options(ggplot2.discrete.color = function() scale_color_brewer(palette = "Set3"))
  124. ```
  125. ```{r}
  126. meta <- read.csv(text="treatment,names,age
  127. iPSC_MG_(Abud),SRR4450428,unknown
  128. iPSC_MG_(Abud),SRR4450429,unknown
  129. iPSC_MG_(Abud),SRR4450430,unknown
  130. iPSC_MG_(Abud),SRR4450431,unknown
  131. iPSC_MG_(Abud),SRR4450432,unknown
  132. iPSC_MG_(Abud),SRR4450433,unknown
  133. iHPC_(Abud),SRR4450434,unknown
  134. iHPC_(Abud),SRR4450435,unknown
  135. iHPC_(Abud),SRR4450436,unknown
  136. iPSC_(Abud),SRR4450437,unknown
  137. iPSC_(Abud),SRR4450438,unknown
  138. iPSC_(Abud),SRR4450439,unknown
  139. iPSC_(Abud),SRR4450440,unknown
  140. CD14_M_(Abud),SRR4450441,unknown
  141. CD14_M_(Abud),SRR4450442,unknown
  142. CD14_M_(Abud),SRR4450443,unknown
  143. CD14_M_(Abud),SRR4450444,unknown
  144. CD14_M_(Abud),SRR4450445,unknown
  145. CD16_M_(Abud),SRR4450446,unknown
  146. CD16_M_(Abud),SRR4450447,unknown
  147. CD16_M_(Abud),SRR4450448,unknown
  148. CD16_M_(Abud),SRR4450449,unknown
  149. Fetal_MG_(Abud),SRR4450450,unknown
  150. Fetal_MG_(Abud),SRR4450451,unknown
  151. Fetal_MG_(Abud),SRR4450452,unknown
  152. Adult_MG_(Abud),SRR4450453,unknown
  153. Adult_MG_(Abud),SRR4450454,unknown
  154. Adult_MG_(Abud),SRR4450455,unknown
  155. Blood_DC_(Abud),SRR4450456,unknown
  156. Blood_DC_(Abud),SRR4450457,unknown
  157. Blood_DC_(Abud),SRR4450458,unknown
  158. iPSC_MG_WT_(McQuade),SRR12608405,unknown
  159. iPSC_MG_WT_(McQuade),SRR12608406,unknown
  160. iPSC_MG_WT_(McQuade),SRR12608407,unknown
  161. iPSC_MG_WT_(McQuade),SRR12608408,unknown
  162. iPSC_MG_TREM2_KO_(McQuade),SRR12608409,unknown
  163. iPSC_MG_TREM2_KO_(McQuade),SRR12608410,unknown
  164. iPSC_MG_TREM2_KO_(McQuade),SRR12608411,unknown
  165. iPSC_MG_TREM2_KO_(McQuade),SRR12608412,unknown
  166. iPSC_MG_2.0_(McQuade),SRR7613924,unknown
  167. iPSC_MG_2.0_(McQuade),SRR7613925,unknown
  168. iPSC_MG_2.0_(McQuade),SRR7613926,unknown
  169. iHPC_2.0_(McQuade),SRR8180288,unknown
  170. iHPC_2.0_(McQuade),SRR8180289,unknown
  171. iHPC_2.0_(McQuade),SRR8180290,unknown
  172. iPSC_MG,20220209.A-av111_WT_001_R1,day_34
  173. iPSC_MG,20220209.A-av111_WT_002_R1,day_34
  174. iPSC_MG,20220209.A-av111_WT_003_R1,day_34
  175. iPSC_MG,20220209.A-tw029_WT_2_R1,day_38
  176. iPSC_MG,20220209.A-tw029_WT_3_R1,day_38
  177. iPSC_MG,20220504.B-WTC_1_R1,day_28
  178. iPSC_MG,20220504.B-WTC_2_R1,day_28
  179. iPSC_MG_Slc37a2_mut1,20220504.B-SLC-EX2B1_1_R1,day_28
  180. iPSC_MG_Slc37a2_mut1,20220504.B-SLC-EX2B1_2_R1,day_28
  181. iPSC_MG_Slc37a2_mut2,20220504.B-SLC-EX6C5_1_R1,day_28
  182. iPSC_MG_Slc37a2_mut2,20220504.B-SLC-EX6C5_2_R1,day_28
  183. iPSC_MG_TREM2_mut,20220504.B-TREM2-A1_1_R1,day_28
  184. iPSC_MG_TREM2_mut,20220504.B-TREM2-A1_2_R1,day_28")
  185. ```
  186. ```{r}
  187. meta$origin <- 'Villani'
  188. meta$origin[grepl('Abud', meta$treatment)] <- 'Abud'
  189. meta$origin[grepl('McQuade', meta$treatment)] <- 'McQuade iPSC'
  190. meta$origin[grepl('SRR126', meta$names)] <- 'McQuade TREM'
  191. ```
  192. Data included in this analysis:
  193. ```{r, results = 'asis'}
  194. (DT::datatable(meta, rownames = FALSE,
  195. extensions = 'Buttons',
  196. filter = "none",
  197. options = list(pageLength = 5, autowidth = TRUE,
  198. dom = 'Blftip',
  199. buttons = c('copy', 'csv', 'excel'))))
  200. ```
  201. ## Overview of Villani's data {.tabset .tabset-pills}
  202. ```{r}
  203. d <- readRDS('/home/imallona/avillani_microglia/ambra_only_output/outputR/shiny_sce.rds')$sce_gene
  204. ```
  205. ```{r}
  206. logcpms <- assay(d, "logcpm")
  207. mds <- limma::plotMDS(logcpms, top = 500, labels = NULL, pch = NULL,
  208. cex = 1, dim.plot = c(1, 2), ndim = 7,
  209. gene.selection = "common",
  210. xlab = NULL, ylab = NULL, plot = FALSE,
  211. var.explained = TRUE)
  212. vexp1 <- round(mds$var.explained[1]*100)
  213. vexp2 <- round(mds$var.explained[2]*100)
  214. if (!is.null(mds$cmdscale.out)) {
  215. ## Bioc 3.12 and earlier
  216. mds <- mds$cmdscale.out
  217. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  218. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  219. } else {
  220. mds <- data.frame(names = colnames(logcpms),
  221. MDS1 = mds$x,
  222. MDS2 = mds$y)
  223. }
  224. mds <- mds %>%
  225. dplyr::full_join(data.frame(colData(d)), by = "names")
  226. ```
  227. ### MDS {.tabset .tabset-pills}
  228. Multidimensional scaling (MDS) plots samples on a two-dimensional scatterplot so that distances on the plot approximate the typical log2 fold changes between the samples.
  229. ```{r, fig.width = 10, fig.height = 4, results = 'asis'}
  230. for (item in c('treatment', 'timepoint', 'names')) {
  231. cat('#### ', item, '\n\n')
  232. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = get(item)), alpha=1) +
  233. geom_point(size=3) +
  234. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  235. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  236. title= paste("MDS", item)) +
  237. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  238. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  239. theme_bw() +
  240. labs(color = item) +
  241. theme(aspect.ratio=1) +
  242. theme(legend.position='right')) #, legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11),
  243. #axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)))
  244. cat('\n\n')
  245. }
  246. ```
  247. <!-- ### Correlation {.tabset .tabset-pills} -->
  248. <!-- #### All genes -->
  249. <!-- ```{r, fig.width = 10, fig.height = 10} -->
  250. <!-- corrplot(cor(logcpms, method= 'spearman'), -->
  251. <!-- method = 'circle', type = 'lower', insig = 'blank', -->
  252. <!-- number.cex = 0.8, order = 'hclust', diag = TRUE , addCoef.col = 'white') -->
  253. <!-- ``` -->
  254. <!-- #### Top 500 most variable genes -->
  255. <!-- ```{r, fig.width = 10, fig.height = 10} -->
  256. <!-- corrplot(cor(logcpms[head(order(rowVars(logcpms), decreasing = TRUE), 500),], method= 'spearman'), -->
  257. <!-- method = 'circle', type = 'lower', insig = 'blank', -->
  258. <!-- addCoef.col = 'white', number.cex = 0.8, order = 'hclust', diag = TRUE) -->
  259. <!-- ``` -->
  260. ```{r}
  261. assay(d, 'cpms') <- cpm(d)
  262. saveRDS(d, file = 'ambra_villani_only_sce.rds')
  263. ```
  264. ## Villani's wildtypes and mutants and Abud's data {.tabset .tabset-pills}
  265. <!-- ### Correlations -->
  266. ```{r, message = FALSE, warning = FALSE}
  267. srp <- "SRP092075"
  268. abud <- recount3::create_rse_manual(
  269. project = srp,
  270. project_home = "data_sources/sra",
  271. organism = "human",
  272. annotation = "gencode_v29",
  273. type = "gene")
  274. assay(abud, "counts") <- transform_counts(abud)
  275. ```
  276. ```{r, eval = TRUE}
  277. abud <- subset(abud,
  278. select = colData(abud)$external_id %in% meta$names)
  279. ## abud_annot <- merge(abud_annot, colData(abud), by.x = 'srr', by.y = 'external_id')
  280. ```
  281. ```{r}
  282. abudlcpm <- edgeR::cpm(assay(abud, 'counts'),
  283. log = TRUE,
  284. prior.count = 2)
  285. ```
  286. ```{r}
  287. ## table(rowData(d)$gene_id %in% rowData(abud)$gene_id)
  288. rowData(d)$unversioned_gene_id <- sapply(strsplit(rowData(d)$gene_id, '\\.'), function(x) return(x[[1]]))
  289. rowData(abud)$unversioned_gene_id <- sapply(strsplit(rowData(abud)$gene_id, '\\.'), function(x) return(x[[1]]))
  290. ## table(rowData(d)$unversioned_gene_id %in% rowData(abud)$unversioned_gene_id)
  291. shared <- intersect(rowData(d)$unversioned_gene_id, rowData(abud)$unversioned_gene_id)
  292. dict <- data.frame(unversioned_gene_id = shared,
  293. armor = rownames(rowData(d)[rowData(d)$unversioned_gene_id %in% shared,]))
  294. dict <- merge(dict, rowData(abud)[rowData(abud)$unversioned_gene_id %in% shared,
  295. c('unversioned_gene_id', 'gene_id')],
  296. by = 'unversioned_gene_id')
  297. colnames(dict)[3] <- 'recount'
  298. dict <- dict[grep('PAR_Y', dict$recount, invert = TRUE),]
  299. ```
  300. ```{r}
  301. ## remove the PAR_Y recount3 which introduce duplicates, i.e.
  302. ## dim(dict)
  303. ## print(grep('PAR_Y', dict$recount, value = TRUE))
  304. ## dim(dict)
  305. dict <- dict[grep('PAR_Y', dict$recount, invert = TRUE),]
  306. ```
  307. <!-- ```{r, fig.width = 25, fig.height = 25} -->
  308. <!-- ## dim(cbind(logcpms[dict$armor,], abudlcpm[dict$recount,])) -->
  309. <!-- corrplot(cor(cbind(logcpms[dict$armor,], -->
  310. <!-- abudlcpm[dict$recount,]), method= 'spearman'), -->
  311. <!-- method = 'circle', type = 'lower', insig = 'blank', -->
  312. <!-- number.cex = 0.8, order = 'hclust', diag = TRUE , addCoef.col = 'white') -->
  313. <!-- ``` -->
  314. ### MDS before batch correction
  315. ```{r, fig.width = 8, fig.height = 5}
  316. mds <- limma::plotMDS(cbind(logcpms[dict$armor,],
  317. abudlcpm[dict$recount,]), top = 500, labels = NULL, pch = NULL,
  318. cex = 1, dim.plot = c(1, 2), ndim = 7,
  319. gene.selection = "common",
  320. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  321. vexp1 <- round(mds$var.explained[1]*100)
  322. vexp2 <- round(mds$var.explained[2]*100)
  323. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  324. MDS1 = mds$x,
  325. MDS2 = mds$y)
  326. rownames(mds) <- mds$names
  327. ## mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'Abud', no = 'Ambra')
  328. mds <- merge(mds, meta, by = 'names')
  329. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  330. ```
  331. ```{r, fig.width = 10, fig.height = 4, results = 'asis'}
  332. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = origin), alpha=1) +
  333. geom_point(size=3) +
  334. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  335. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  336. title= paste("MDS before ComBat")) +
  337. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  338. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  339. theme_bw() +
  340. theme(aspect.ratio=1) +
  341. theme(legend.position='right')) #, legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11),
  342. #axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)))
  343. ```
  344. ### Batch correction
  345. ```{r}
  346. # parametric adjustment
  347. combat_abud <- sva::ComBat(dat=cbind(logcpms[dict$armor,],
  348. abudlcpm[dict$recount,]),
  349. batch = c(rep('ambra', ncol(logcpms)),
  350. rep('abud', ncol(abudlcpm))),
  351. mod = NULL, par.prior = TRUE, prior.plots = TRUE)
  352. ```
  353. ### MDS after batch correction {.tabset .tabset-pills}
  354. #### Batches
  355. ```{r, fig.width = 8, fig.height = 5}
  356. mds <- limma::plotMDS(combat_abud, top = 500, labels = NULL, pch = NULL,
  357. cex = 1, dim.plot = c(1, 2), ndim = 7,
  358. gene.selection = "common",
  359. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  360. vexp1 <- round(mds$var.explained[1]*100)
  361. vexp2 <- round(mds$var.explained[2]*100)
  362. if (!is.null(mds$cmdscale.out)) {
  363. ## Bioc 3.12 and earlier
  364. mds <- mds$cmdscale.out
  365. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  366. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  367. } else {
  368. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  369. MDS1 = mds$x,
  370. MDS2 = mds$y)
  371. }
  372. ## mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'Abud', no = 'Ambra')
  373. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  374. mds <- merge(mds, meta, by = 'names')
  375. rownames(mds) <- mds$names
  376. ```
  377. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  378. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = origin), alpha=1) +
  379. geom_point(size=3) +
  380. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  381. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  382. title= paste("MDS after ComBat")) +
  383. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  384. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  385. theme_bw() +
  386. theme(aspect.ratio=1) +
  387. theme(legend.position='right')) #, legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11),
  388. #axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)))
  389. ```
  390. #### Annotation
  391. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  392. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = treatment, shape = origin), alpha=1) +
  393. geom_point(size=3) +
  394. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  395. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  396. title= paste("MDS after ComBat")) +
  397. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  398. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  399. theme_bw() +
  400. theme(legend.position='right') +
  401. guides(fill=guide_legend(ncol=2)) +
  402. theme(aspect.ratio=1) +
  403. scale_color_brewer(palette = "Set3"))
  404. ```
  405. ```{r}
  406. tmp <- list(logcpms = cbind(logcpms[dict$armor,],
  407. abudlcpm[dict$recount,]),
  408. counts = cbind(counts(d)[dict$armor,],
  409. assay(abud, 'raw_counts')[dict$recount,]))
  410. rownames(meta) <- meta$names
  411. tmp2 <- meta[colnames(tmp$counts),]
  412. tmp$cpms <- edgeR::cpm(tmp$counts, log = FALSE, prior.count = 2)
  413. rownames(mds) <- mds[,1]
  414. mds <- mds[colnames(tmp$counts),]
  415. tmp3 <- SingleCellExperiment(assays = tmp,
  416. colData = data.frame(tmp2),
  417. rowData = rowData(d)[dict$armor,],
  418. metadata = list(study = "Villani, Abud"),
  419. reducedDims = list(mds = mds[,2:3]))
  420. saveRDS(object = tmp3,
  421. file = 'villani_plus_abud_sce.rds')
  422. rm(tmp, tmp2)
  423. ```
  424. <!-- # Test -->
  425. <!-- ```{r} -->
  426. <!-- se <- tmp3 -->
  427. <!-- red.dim <- reducedDim(tmp3, "mds"); -->
  428. <!-- plot.data <- data.frame(X=red.dim[, 1], Y=red.dim[, 2], row.names=colnames(se)); -->
  429. <!-- plot.data$ColorBy <- colData(se)[, "names"]; -->
  430. <!-- print(head(plot.data$ColorBy)) -->
  431. <!-- plot.data[["ColorBy"]] <- factor(plot.data[["ColorBy"]]); -->
  432. <!-- # Avoid visual biases from default ordering by shuffling the points -->
  433. <!-- set.seed(44); -->
  434. <!-- plot.data <- plot.data[sample(nrow(plot.data)),,drop=FALSE]; -->
  435. <!-- dot.plot <- ggplot() + -->
  436. <!-- geom_point(aes(x=X, y=Y, color=ColorBy), alpha=1, plot.data, size=1) + -->
  437. <!-- labs(x="Dimension 1", y="Dimension 2", color="names", title="mds") + -->
  438. <!-- coord_cartesian(xlim=range(plot.data$X, na.rm=TRUE), -->
  439. <!-- ylim=range(plot.data$Y, na.rm=TRUE), expand=TRUE) + -->
  440. <!-- theme_bw() + -->
  441. <!-- theme(legend.position='bottom', legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11), -->
  442. <!-- axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)) -->
  443. <!-- print(dot.plot) -->
  444. <!-- red.dim <- reducedDim(se, "mds"); -->
  445. <!-- plot.data <- data.frame(X=red.dim[, 1], Y=red.dim[, 2], row.names=colnames(se)); -->
  446. <!-- plot.data$ColorBy <- colData(se)[, "names"]; -->
  447. <!-- plot.data$ColorBy <- as.numeric(as.factor(plot.data$ColorBy)); -->
  448. <!-- # Avoid visual biases from default ordering by shuffling the points -->
  449. <!-- set.seed(44); -->
  450. <!-- plot.data <- plot.data[sample(nrow(plot.data)),,drop=FALSE]; -->
  451. <!-- dot.plot <- ggplot() + -->
  452. <!-- geom_point(aes(x=X, y=Y, color=ColorBy), alpha=1, plot.data, size=1) + -->
  453. <!-- labs(x="Dimension 1", y="Dimension 2", color="names", title="mds") + -->
  454. <!-- coord_cartesian(xlim=range(plot.data$X, na.rm=TRUE), -->
  455. <!-- ylim=range(plot.data$Y, na.rm=TRUE), expand=TRUE) + -->
  456. <!-- scale_color_gradientn(colors=colDataColorMap(colormap, "names", discrete=FALSE)(21), na.value='grey50', limits=range(plot.data$ColorBy, na.rm=TRUE)) + -->
  457. <!-- theme_bw() + -->
  458. <!-- theme(legend.position='bottom', legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11), -->
  459. <!-- axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)) -->
  460. <!-- # Saving data for transmission -->
  461. <!-- all_contents[['ReducedDimensionPlot1']] <- plot.data -->
  462. <!-- knitr::knit_exit('') -->
  463. <!-- ``` -->
  464. ```{r}
  465. rm(tmp3)
  466. ```
  467. <!-- ```{r, cache = FALSE} -->
  468. <!-- knitr::knit_exit() -->
  469. <!-- ``` -->
  470. ## Villani's wildtypes (no mutants) and Abud's data {.tabset .tabset-pills}
  471. ```{r}
  472. ambra_wts <- meta[meta$treatment == 'iPSC_MG', 'names']
  473. ```
  474. <!-- ```{r, fig.width = 25, fig.height = 25} -->
  475. <!-- ## dim(cbind(logcpms[dict$armor,], abudlcpm[dict$recount,])) -->
  476. <!-- corrplot(cor(cbind(logcpms[dict$armor, ambra_wts], -->
  477. <!-- abudlcpm[dict$recount,]), method= 'spearman'), -->
  478. <!-- method = 'circle', type = 'lower', insig = 'blank', -->
  479. <!-- number.cex = 0.8, order = 'hclust', diag = TRUE , addCoef.col = 'white') -->
  480. <!-- ``` -->
  481. ### MDS before batch correction {.tabset .tabset-pills}
  482. ```{r fids, fig.width = 8, fig.height = 5}
  483. mds <- limma::plotMDS(cbind(logcpms[dict$armor,ambra_wts],
  484. abudlcpm[dict$recount,]), top = 500, labels = NULL, pch = NULL,
  485. cex = 1, dim.plot = c(1, 2), ndim = 7,
  486. gene.selection = "common",
  487. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  488. vexp1 <- round(mds$var.explained[1]*100)
  489. vexp2 <- round(mds$var.explained[2]*100)
  490. if (!is.null(mds$cmdscale.out)) {
  491. ## Bioc 3.12 and earlier
  492. mds <- mds$cmdscale.out
  493. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  494. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  495. } else {
  496. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  497. MDS1 = mds$x,
  498. MDS2 = mds$y)
  499. }
  500. ## mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'Abud', no = 'Ambra')
  501. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  502. mds <- merge(mds, meta, by = 'names')
  503. rownames(mds) <- mds$names
  504. ```
  505. ```{r, fig.width = 10, fig.height = 4, results = 'asis'}
  506. for (item in c('treatment', 'age', 'names', 'origin')) {
  507. cat('#### ', item, '\n\n')
  508. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = get(item)), alpha=1) +
  509. geom_point(size=3) +
  510. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  511. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  512. title= paste("MDS before ComBat")) +
  513. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  514. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  515. theme_bw() +
  516. labs(color = item) +
  517. theme(aspect.ratio=1) +
  518. theme(legend.position='right')) #, legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11),
  519. #axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)))
  520. cat('\n\n')
  521. }
  522. ```
  523. ### Batch correction
  524. ```{r}
  525. # parametric adjustment; explore the parametric too
  526. combat_abud <- sva::ComBat(dat=cbind(logcpms[dict$armor,ambra_wts],
  527. abudlcpm[dict$recount,]),
  528. batch = c(rep('ambra', length(ambra_wts)),
  529. rep('abud', ncol(abudlcpm))),
  530. mod = NULL, par.prior = TRUE, prior.plots = FALSE)
  531. ```
  532. ### MDS after batch correction {.tabset .tabset-pills}
  533. ```{r, fig.width = 8, fig.height = 5}
  534. mds <- limma::plotMDS(combat_abud, top = 500, labels = NULL, pch = NULL,
  535. cex = 1, dim.plot = c(1, 2), ndim = 7,
  536. gene.selection = "common",
  537. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  538. vexp1 <- round(mds$var.explained[1]*100)
  539. vexp2 <- round(mds$var.explained[2]*100)
  540. if (!is.null(mds$cmdscale.out)) {
  541. ## Bioc 3.12 and earlier
  542. mds <- mds$cmdscale.out
  543. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  544. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  545. } else {
  546. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  547. MDS1 = mds$x,
  548. MDS2 = mds$y)
  549. }
  550. ## mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'Abud', no = 'Ambra')
  551. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  552. mds <- merge(mds, meta, by = 'names')
  553. rownames(mds) <- mds$names
  554. ```
  555. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  556. for (item in c('origin', 'treatment', 'names', 'age')) {
  557. cat('#### ', item, '\n\n')
  558. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = get(item)), alpha=1) +
  559. geom_point(size=3) +
  560. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  561. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  562. title= paste("MDS after ComBat")) +
  563. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  564. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  565. theme_bw() +
  566. theme(aspect.ratio=1) +
  567. theme(legend.position='right')) #, legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11),
  568. #axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)))
  569. cat('\n\n')
  570. }
  571. ```
  572. #### Annotation
  573. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  574. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = treatment, shape = age), alpha=1) +
  575. geom_point(size=3) +
  576. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  577. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  578. title= paste("MDS after ComBat")) +
  579. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  580. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  581. theme_bw() +
  582. theme(legend.position='right') +
  583. guides(fill=guide_legend(ncol=2)) +
  584. theme(aspect.ratio=1) +
  585. scale_color_brewer(palette = "Set3"))
  586. ```
  587. ## Villani's wildtypes and mutants and McQuade TREM data {.tabset .tabset-pills}
  588. <!-- ```{r} -->
  589. <!-- srp <- 'SRP281230' -->
  590. <!-- trem <- recount3::create_rse_manual( -->
  591. <!-- project = srp, -->
  592. <!-- project_home = "data_sources/sra", -->
  593. <!-- organism = "human", -->
  594. <!-- annotation = "gencode_v29", -->
  595. <!-- type = "gene") -->
  596. <!-- trem <- subset(trem, -->
  597. <!-- select = colData(trem)$external_id %in% meta$names) -->
  598. <!-- assay(trem, "counts") <- transform_counts(trem) -->
  599. <!-- ``` -->
  600. ```{r}
  601. trem <- readRDS('/home/imallona/avillani_microglia/mcquade_trem_only_output/outputR/shiny_sce.rds')$sce_gene
  602. trem <- subset(trem,
  603. select = colData(trem)$names %in% meta$names)
  604. tremlcpm <- assay(trem, "logcpm")
  605. ```
  606. <!-- ### Correlations -->
  607. <!-- ```{r, fig.width = 25, fig.height = 25} -->
  608. <!-- corrplot(cor(cbind(logcpms[dict$armor,], -->
  609. <!-- tremlcpm[dict$armor,]), method= 'spearman'), -->
  610. <!-- method = 'circle', type = 'lower', insig = 'blank', -->
  611. <!-- number.cex = 0.8, order = 'hclust', diag = TRUE , addCoef.col = 'white') -->
  612. <!-- ``` -->
  613. ### MDS before batch correction
  614. ```{r, fig.width = 8, fig.height = 5}
  615. mds <- limma::plotMDS(cbind(logcpms[dict$armor,],
  616. tremlcpm[dict$armor,]), top = 500, labels = NULL, pch = NULL,
  617. cex = 1, dim.plot = c(1, 2), ndim = 7,
  618. gene.selection = "common",
  619. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  620. vexp1 <- round(mds$var.explained[1]*100)
  621. vexp2 <- round(mds$var.explained[2]*100)
  622. if (!is.null(mds$cmdscale.out)) {
  623. ## Bioc 3.12 and earlier
  624. mds <- mds$cmdscale.out
  625. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  626. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  627. } else {
  628. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  629. MDS1 = mds$x,
  630. MDS2 = mds$y)
  631. }
  632. ## mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'McQuade TREM', no = 'Ambra')
  633. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  634. mds <- merge(mds, meta, by = 'names')
  635. rownames(mds) <- mds$names
  636. ```
  637. ```{r, fig.width = 10, fig.height = 4, results = 'asis'}
  638. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = origin), alpha=1) +
  639. geom_point(size=3) +
  640. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  641. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  642. title= paste("MDS before ComBat")) +
  643. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  644. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  645. theme_bw() +
  646. theme(aspect.ratio=1) +
  647. theme(legend.position='right')) #, legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11),
  648. #axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)))
  649. ```
  650. ### Batch correction
  651. ```{r}
  652. # parametric adjustment
  653. combat_trem <- sva::ComBat(dat=cbind(logcpms[dict$armor,],
  654. tremlcpm[dict$armor,]),
  655. batch = c(rep('ambra', ncol(logcpms)),
  656. rep('trem', ncol(tremlcpm))),
  657. mod = NULL, par.prior = TRUE, prior.plots = TRUE)
  658. ```
  659. ### MDS after batch correction {.tabset .tabset-pills}
  660. #### Batch
  661. ```{r, fig.width = 8, fig.height = 5}
  662. mds <- limma::plotMDS(combat_trem, top = 500, labels = NULL, pch = NULL,
  663. cex = 1, dim.plot = c(1, 2), ndim = 7,
  664. gene.selection = "common",
  665. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  666. vexp1 <- round(mds$var.explained[1]*100)
  667. vexp2 <- round(mds$var.explained[2]*100)
  668. if (!is.null(mds$cmdscale.out)) {
  669. ## Bioc 3.12 and earlier
  670. mds <- mds$cmdscale.out
  671. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  672. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  673. } else {
  674. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  675. MDS1 = mds$x,
  676. MDS2 = mds$y)
  677. }
  678. # mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'Trem', no = 'Ambra')
  679. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  680. mds <- merge(mds, meta, by = 'names')
  681. rownames(mds) <- mds$names
  682. ```
  683. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  684. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = origin), alpha=1) +
  685. geom_point(size=3) +
  686. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  687. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  688. title= paste("MDS after ComBat")) +
  689. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  690. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  691. theme_bw() +
  692. theme(aspect.ratio=1) +
  693. theme(legend.position='right')) #, legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11),
  694. #axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)))
  695. ```
  696. #### Annotation
  697. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  698. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = treatment, shape = origin), alpha=1) +
  699. geom_point(size=3) +
  700. labs(x = sprintf("Dim. 1, var. exp. %s%%",vexp1),
  701. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  702. title= paste("MDS after ComBat")) +
  703. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  704. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  705. theme_bw() +
  706. theme(legend.position='right') +
  707. guides(fill=guide_legend(ncol=2)) +
  708. theme(aspect.ratio=1) +
  709. scale_color_brewer(palette = "Set3"))
  710. ```
  711. ## Villani's wildtypes and mutants and McQuade's iPSCs data {.tabset .tabset-pills}
  712. ```{r, message = FALSE, warning = FALSE}
  713. srp <- 'SRP155574'
  714. mc <- recount3::create_rse_manual(
  715. project = srp,
  716. project_home = "data_sources/sra",
  717. organism = "human",
  718. annotation = "gencode_v29",
  719. type = "gene")
  720. assay(mc, "counts") <- transform_counts(mc)
  721. ```
  722. ```{r}
  723. mc <- subset(mc,
  724. select = colData(mc)$external_id %in% meta$names)
  725. assay(mc, "counts") <- transform_counts(mc)
  726. ```
  727. ```{r}
  728. mclcpm <- edgeR::cpm(assay(mc, 'counts'),
  729. log = TRUE,
  730. prior.count = 2)
  731. ```
  732. ```{r}
  733. ## table(rowData(d)$gene_id %in% rowData(mc)$gene_id)
  734. rowData(d)$unversioned_gene_id <- sapply(strsplit(rowData(d)$gene_id, '\\.'), function(x) return(x[[1]]))
  735. rowData(mc)$unversioned_gene_id <- sapply(strsplit(rowData(mc)$gene_id, '\\.'), function(x) return(x[[1]]))
  736. ## table(rowData(d)$unversioned_gene_id %in% rowData(mc)$unversioned_gene_id)
  737. shared <- intersect(rowData(d)$unversioned_gene_id, rowData(mc)$unversioned_gene_id)
  738. dict <- data.frame(unversioned_gene_id = shared,
  739. armor = rownames(rowData(d)[rowData(d)$unversioned_gene_id %in% shared,]))
  740. dict <- merge(dict, rowData(mc)[rowData(mc)$unversioned_gene_id %in% shared, c('unversioned_gene_id', 'gene_id')],
  741. by = 'unversioned_gene_id')
  742. colnames(dict)[3] <- 'recount'
  743. dict <- dict[grep('PAR_Y', dict$recount, invert = TRUE),]
  744. ```
  745. <!-- ### Correlations -->
  746. <!-- ```{r, fig.width = 25, fig.height = 25} -->
  747. <!-- ## dim(cbind(logcpms[dict$armor,], mclcpm[dict$recount,])) -->
  748. <!-- corrplot(cor(cbind(logcpms[dict$armor,], -->
  749. <!-- mclcpm[dict$recount,]), method= 'spearman'), -->
  750. <!-- method = 'circle', type = 'lower', insig = 'blank', -->
  751. <!-- number.cex = 0.8, order = 'hclust', diag = TRUE , addCoef.col = 'white') -->
  752. <!-- ``` -->
  753. ### MDS before batch correction
  754. ```{r, fig.width = 8, fig.height = 5}
  755. mds <- limma::plotMDS(cbind(logcpms[dict$armor,],
  756. mclcpm[dict$recount,]), top = 500, labels = NULL, pch = NULL,
  757. cex = 1, dim.plot = c(1, 2), ndim = 7,
  758. gene.selection = "common",
  759. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  760. vexp1 <- round(mds$var.explained[1]*100)
  761. vexp2 <- round(mds$var.explained[2]*100)
  762. if (!is.null(mds$cmdscale.out)) {
  763. ## Bioc 3.12 and earlier
  764. mds <- mds$cmdscale.out
  765. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  766. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  767. } else {
  768. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  769. MDS1 = mds$x,
  770. MDS2 = mds$y)
  771. }
  772. #mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'Mc', no = 'Ambra')
  773. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  774. mds <- merge(mds, meta, by = 'names')
  775. rownames(mds) <- mds$names
  776. ```
  777. ```{r, fig.width = 10, fig.height = 4, results = 'asis'}
  778. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = origin), alpha=0.5) +
  779. geom_point(size=3) +
  780. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  781. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  782. title= paste("MDS before ComBat")) +
  783. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  784. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  785. theme_bw() +
  786. theme(aspect.ratio = 1) +
  787. theme(legend.position='right'))
  788. ```
  789. ### Batch correction
  790. ```{r}
  791. # nonparametric adjustment
  792. combat_mc <- sva::ComBat(dat=cbind(logcpms[dict$armor,],
  793. mclcpm[dict$recount,]),
  794. batch = c(rep('ambra', ncol(logcpms)),
  795. rep('mc', ncol(mclcpm))),
  796. mod = NULL, par.prior = TRUE, prior.plots = TRUE)
  797. ```
  798. ### MDS after batch correction {.tabset .tabset-pills}
  799. #### Batch
  800. ```{r, fig.width = 8, fig.height = 5}
  801. mds <- limma::plotMDS(combat_mc, top = 500, labels = NULL, pch = NULL,
  802. cex = 1, dim.plot = c(1, 2), ndim = 7,
  803. gene.selection = "common",
  804. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  805. vexp1 <- round(mds$var.explained[1]*100)
  806. vexp2 <- round(mds$var.explained[2]*100)
  807. if (!is.null(mds$cmdscale.out)) {
  808. ## Bioc 3.12 and earlier
  809. mds <- mds$cmdscale.out
  810. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  811. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  812. } else {
  813. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  814. MDS1 = mds$x,
  815. MDS2 = mds$y)
  816. }
  817. #mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'Mc', no = 'Ambra')
  818. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  819. mds <- merge(mds, meta, by = 'names')
  820. rownames(mds) <- mds$names
  821. ```
  822. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  823. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = origin), alpha=1) +
  824. geom_point(size=3) +
  825. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  826. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  827. title= paste("MDS after ComBat")) +
  828. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  829. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  830. theme_bw() +
  831. theme(aspect.ratio=1) +
  832. theme(legend.position='right')) #, legend.box='vertical', legend.text=element_text(size=9), legend.title=element_text(size=11),
  833. #axis.text=element_text(size=10), axis.title=element_text(size=12), title=element_text(size=12)))
  834. ```
  835. #### Annotation
  836. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  837. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = treatment, shape = origin), alpha=1) +
  838. geom_point(size=3) +
  839. labs(x = sprintf("Dim. 1, var. exp. %s%%",vexp1),
  840. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),title= paste("MDS after ComBat")) +
  841. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  842. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  843. theme_bw() +
  844. theme(legend.position='right') +
  845. guides(fill=guide_legend(ncol=2)) +
  846. theme(aspect.ratio=1) +
  847. scale_color_brewer(palette = "Set3"))
  848. ```
  849. # Joint embedding of Villani's plus public data, combined {.tabset .tabset-pills}
  850. ```{r}
  851. pre <- cbind(logcpms[dict$armor,],
  852. abudlcpm[dict$recount,],
  853. tremlcpm[dict$armor,],
  854. mclcpm[dict$recount,])
  855. ```
  856. <!-- ## Correlations (all genes) -->
  857. ```{r, fig.width = 30, fig.height = 30}
  858. ## dim(cbind(logcpms[dict$armor,], mclcpm[dict$recount,]))
  859. cnames <- colnames(pre)
  860. colnames(pre) <- meta[meta$names == colnames(pre), 'treatment']
  861. ## corrplot(cor(pre, method= 'spearman'),
  862. ## method = 'circle', type = 'lower', insig = 'blank',
  863. ## number.cex = 0.8, order = 'hclust', diag = TRUE , addCoef.col = 'white')
  864. ```
  865. <!-- ## Correlations (top 500 variable genes) -->
  866. <!-- ```{r, fig.width = 30, fig.height = 30} -->
  867. <!-- corrplot(cor(pre[head(order(rowVars(pre), decreasing = TRUE), 500),], method= 'spearman'), -->
  868. <!-- method = 'circle', type = 'lower', insig = 'blank', -->
  869. <!-- addCoef.col = 'white', number.cex = 0.8, order = 'hclust', diag = TRUE) -->
  870. <!-- ``` -->
  871. ```{r}
  872. ## take the original colnames back
  873. colnames(pre) <- cnames
  874. ```
  875. ## MDS before batch correction {.tabset .tabset-pills}
  876. ### Batch
  877. ```{r firstmds, fig.width = 8, fig.height = 5}
  878. mds <- limma::plotMDS(pre, top = 500, labels = NULL, pch = NULL,
  879. cex = 1, dim.plot = c(1, 2), ndim = 7,
  880. gene.selection = "common",
  881. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  882. vexp1 <- round(mds$var.explained[1]*100)
  883. vexp2 <- round(mds$var.explained[2]*100)
  884. if (!is.null(mds$cmdscale.out)) {
  885. ## Bioc 3.12 and earlier
  886. mds <- mds$cmdscale.out
  887. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  888. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  889. } else {
  890. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  891. MDS1 = mds$x,
  892. MDS2 = mds$y, var.explained = mds$var.explained)
  893. }
  894. ## mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'public', no = 'Ambra')
  895. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  896. mds <- merge(mds, meta, by = 'names')
  897. rownames(mds) <- mds$names
  898. ```
  899. ```{r, fig.width = 10, fig.height = 4, results = 'asis'}
  900. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = origin), alpha=1) +
  901. geom_point(size=3) +
  902. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  903. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  904. title= paste("MDS before ComBat")) +
  905. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  906. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  907. theme_bw() +
  908. theme(aspect.ratio = 1) +
  909. theme(legend.position='right'))
  910. ```
  911. ## Annotation
  912. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  913. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = treatment, shape = origin), alpha=0.5) +
  914. geom_point(size=3) +
  915. labs(x = sprintf("Dim. 1, var. exp. %s%%",vexp1),
  916. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),title= paste("MDS after ComBat")) +
  917. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  918. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  919. theme_bw() +
  920. theme(legend.position='right') +
  921. guides(color=guide_legend(ncol=2)) +
  922. theme(aspect.ratio=1))
  923. ```
  924. ## Batch correction
  925. ```{r}
  926. pre <- pre[,meta$names]
  927. stopifnot(all(colnames(pre) == meta$names))
  928. # nonparametric adjustment is as bad
  929. post <- sva::ComBat(dat=pre,
  930. batch = as.factor(meta$origin),
  931. mod = NULL, par.prior = TRUE, prior.plots = TRUE)
  932. ```
  933. ## MDS after batch correction {.tabset .tabset-pills}
  934. ### Batch
  935. ```{r, fig.width = 8, fig.height = 5}
  936. rm(mds)
  937. mds <- limma::plotMDS(post, top = 500, labels = NULL, pch = NULL,
  938. cex = 1, dim.plot = c(1, 2), ndim = 7,
  939. gene.selection = "common",
  940. xlab = NULL, ylab = NULL, plot = FALSE, var.explained = TRUE)
  941. vexp1 <- round(mds$var.explained[1]*100)
  942. vexp2 <- round(mds$var.explained[2]*100)
  943. if (!is.null(mds$cmdscale.out)) {
  944. ## Bioc 3.12 and earlier
  945. mds <- mds$cmdscale.out
  946. colnames(mds) <- paste0("MDS", seq_len(ncol(mds)))
  947. mds <- as.data.frame(mds) %>% tibble::rownames_to_column(var = "names")
  948. } else {
  949. mds <- data.frame(names = rownames(mds$distance.matrix.squared),
  950. MDS1 = mds$x,
  951. MDS2 = mds$y)
  952. }
  953. # mds$origin <- ifelse(grepl('SRR', mds$names), yes = 'Mc', no = 'Ambra')
  954. ## mds <- na.omit( mds %>% dplyr::full_join(data.frame(meta), by = "names"))
  955. mds <- merge(mds, meta, by = 'names')
  956. rownames(mds) <- mds$names
  957. ```
  958. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  959. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = origin), alpha=0.5) +
  960. geom_point(size=3) +
  961. labs(x = sprintf("Dim. 1, var. exp. %s%%", vexp1),
  962. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),
  963. title= paste("MDS after ComBat")) +
  964. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  965. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  966. theme_bw() +
  967. theme(aspect.ratio=1) +
  968. theme(legend.position='right'))
  969. ```
  970. ### Annotation
  971. ```{r, fig.width = 8, fig.height = 4, results = 'asis'}
  972. print(ggplot(mds, aes(x=MDS1, y=MDS2, color = treatment, shape = origin), alpha=0.5) +
  973. geom_point(size=3) +
  974. labs(x = sprintf("Dim. 1, var. exp. %s%%",vexp1),
  975. y = sprintf("Dim. 2, var. exp. %s%%", vexp2),title= paste("MDS after ComBat")) +
  976. coord_cartesian(xlim=range(mds$MDS1, na.rm=TRUE),
  977. ylim=range(mds$MDS2, na.rm=TRUE), expand=TRUE) +
  978. theme_bw() +
  979. theme(legend.position='right') +
  980. guides(color=guide_legend(ncol=2)) +
  981. theme(aspect.ratio=1))
  982. ```
  983. ```{r}
  984. pre_counts <- cbind(assay(d, 'counts')[dict$armor,],
  985. assay(abud, 'counts')[dict$recount,],
  986. assay(trem, 'counts')[dict$armor,],
  987. assay(mc, 'counts')[dict$recount,])
  988. pre_counts <- pre_counts[,colnames(pre)]
  989. pre_cpms <- edgeR::cpm(pre_counts,
  990. log = FALSE,
  991. prior.count = 2)
  992. rownames(mds) <- mds[,1]
  993. mds <- mds[colnames(pre_counts),]
  994. saveRDS(object = SingleCellExperiment(list(logcpms = pre,
  995. counts = pre_counts,
  996. cpms = pre_cpms),
  997. colData = data.frame(meta, row.names = meta$names),
  998. rowData = rowData(d)[dict$armor,],
  999. metadata = list(study = "Villani, Abud, McQuade"),
  1000. reducedDims = list(mds = data.frame(mds[,2:3], row.names = mds[,1]))),
  1001. file = 'all_data_sce.rds')
  1002. ```
  1003. # Differential expression: mutants vs WTs (regardless of the WTs age)
  1004. Original ARMOR run, duplicated here to consolidate outputs.
  1005. Here, we perform differential gene expression analysis with edgeR
  1006. [@Robinson2010edgeR] followed by gene set analysis with camera [@Wu2012camera],
  1007. based on abundance estimates from Salmon. For more detailed information of each
  1008. step, please refer to the
  1009. [edgeR user guide](https://www.bioconductor.org/packages/release/bioc/vignettes/edgeR/inst/doc/edgeRUsersGuide.pdf).
  1010. ```{r edgeR-print-se}
  1011. ## List of SummarizedExperiment objects (gene/transcript level)
  1012. se <- readRDS('/home/imallona/avillani_microglia/ambra_only_output/outputR/tximeta_se.rds')
  1013. ## Get gene-level SummarizedExperiment object
  1014. sg <- se$sg
  1015. metadata <- colData(sg)
  1016. sg
  1017. ```
  1018. ## Plot total number of reads per sample
  1019. ```{r edgeR-plot-totalcount}
  1020. ggplot(data.frame(totCount = colSums(assay(sg, "counts")),
  1021. sample = colnames(assay(sg, "counts")),
  1022. stringsAsFactors = FALSE),
  1023. aes(x = sample, y = totCount)) + geom_bar(stat = "identity") +
  1024. theme_bw() + xlab("") + ylab("Total read count") +
  1025. theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5))
  1026. ```
  1027. ## Create DGEList and include average transcript length offsets
  1028. A `DGEList` is the main object `edgeR` requires to perform the DGE analysis. It
  1029. is designed to store read counts and associated information. After creating this
  1030. object, we add offsets, which are average transcript length correction terms
  1031. [@Soneson2016tximport],
  1032. and scale them so they are consistent with library sizes (sequencing depth for
  1033. each sample).
  1034. Then we calculate normalization factors to scale the raw library sizes and
  1035. minimize the log-fold changes between the samples for most genes. Here the
  1036. trimmed mean of M-values between each pair of samples (TMM) is used by default
  1037. [@Robinson2010TMM].
  1038. Finally we add gene annotation information.
  1039. ```{r edgeR-dge-generate}
  1040. dge0 <- tximeta::makeDGEList(sg)
  1041. dge0$genes <- as.data.frame(rowRanges(sg))
  1042. ```
  1043. ## Calculate logCPMs and add as an assay
  1044. We calculate log-counts per million (CPMs) because they are useful descriptive
  1045. measures for the expression level of a gene. Note, however, that the normalized
  1046. values are not used for the differential expression analysis. By default, the
  1047. normalized library sizes are used in the computation.
  1048. We add the logCPMs to one of the fields (or assay) of the first gene-level
  1049. `SummarizedExperiment` object `sg`. At the end of the analysis, we will use this
  1050. object again to export the results of all the genes we started with.
  1051. ```{r edgeR-add-logcpm}
  1052. logcpms <- edgeR::cpm(dge0, offset = dge0$offset, log = TRUE,
  1053. prior.count = 2)
  1054. dimnames(logcpms) <- dimnames(dge0$counts)
  1055. stopifnot(all(rownames(logcpms) == rownames(sg)),
  1056. all(colnames(logcpms) == colnames(sg)))
  1057. assay(sg, "logcpm") <- logcpms
  1058. ```
  1059. Next, we specify the design matrix of the experiment, defining which sample
  1060. annotations will be taken into account in the statistical modeling.
  1061. ```{r edgeR-define-design}
  1062. design <- '~ 0 + treatment'
  1063. metadata <- read.table('/home/imallona/avillani_microglia/ARMOR/metadata_ambra_only.tsv', header = TRUE)
  1064. stopifnot(all(colnames(dge0) == metadata$names))
  1065. des <- model.matrix(as.formula(design), data = metadata)
  1066. ```
  1067. ## Filter out lowly expressed genes
  1068. Next we determine which genes have sufficiently large counts to be retained in
  1069. the statistical analysis, and remove the rest. After removing genes, we
  1070. recalculate the normalization factors.
  1071. ```{r edgeR-filter-genes}
  1072. dim(dge0)
  1073. keep <- edgeR::filterByExpr(dge0, design = des)
  1074. dge <- dge0[keep, ]
  1075. dim(dge)
  1076. ```
  1077. ## Estimate dispersion and fit QL model
  1078. We model the count data using a quasi-likelihood (QL) negative binomial (NB)
  1079. generalized log-linear model, which accounts for gene-specific variability from
  1080. both biological and technical sources. Before fitting the model, we estimate
  1081. the NB dispersion (overall biological variability across all genes), and the QL
  1082. dispersion (gene-specific) using the `estimateDisp()` function.
  1083. It is also good practice to look at the relationship between the biological
  1084. coefficient of variation (NB dispersion) and the gene abundance (in logCPMs).
  1085. ```{r edgeR-estimate-disp}
  1086. ## Estimate dispersion and fit model
  1087. dge <- estimateDisp(dge, design = des)
  1088. qlfit <- glmQLFit(dge, design = des)
  1089. ## Plot dispersions
  1090. plotBCV(dge)
  1091. ```
  1092. ## Define contrasts
  1093. Before testing for differences in gene expression, we define the contrasts
  1094. we wish to test for. Here we represent the constrasts as a numeric matrix:
  1095. ```{r edgeR-define-contrasts}
  1096. contrast <- c('treatmentiPSC_MG_Slc37a2_mut1-treatmentiPSC_MG',
  1097. 'treatmentiPSC_MG_Slc37a2_mut2-treatmentiPSC_MG',
  1098. 'treatmentiPSC_MG_TREM2_mut-treatmentiPSC_MG',
  1099. 'treatmentiPSC_MG_Slc37a2_mut1-treatmentiPSC_MG_TREM2_mut',
  1100. 'treatmentiPSC_MG_Slc37a2_mut2-treatmentiPSC_MG_TREM2_mut',
  1101. 'treatmentiPSC_MG_Slc37a2_mut2-treatmentiPSC_MG_Slc37a2_mut1')
  1102. (contrasts <- as.data.frame(makeContrasts(contrasts = contrast, levels = des)))
  1103. ```
  1104. ```{r edgeR-perform-tests}
  1105. signif3 <- function(x) signif(x, digits = 3)
  1106. edgeR_res <- lapply(contrasts, function(cm) {
  1107. qlf <- glmQLFTest(qlfit, contrast = cm)
  1108. tt <- topTags(qlf, n = Inf, sort.by = "none")$table
  1109. tt %>%
  1110. dplyr::mutate(mlog10PValue = -log10(PValue)) %>%
  1111. dplyr::mutate_at(vars(one_of(c("logFC", "logCPM", "F",
  1112. "PValue", "FDR", "mlog10PValue"))),
  1113. list(signif3))
  1114. })
  1115. ```
  1116. ## DGE tests and MA plots {.tabset .tabset-pills}
  1117. We can visualize the test results by plotting the logCPM (average) vs the logFC,
  1118. and coloring genes with an adjusted p-value below 0.05 (or another specificed
  1119. FDR threshold). A plot is drawn for every contrast.
  1120. ```{r, results = 'asis'}
  1121. if (is(edgeR_res, "data.frame")) {
  1122. print(ggplot(edgeR_res, aes(x = logCPM, y = logFC, color = FDR <= 0.05)) +
  1123. geom_point() + theme_bw() +
  1124. scale_color_manual(values = c("TRUE" = "red", "FALSE" = "black")))
  1125. } else {
  1126. for (nm in names(edgeR_res)) {
  1127. cat('### ', nm, '\n\n')
  1128. print(ggplot(edgeR_res[[nm]], aes(x = logCPM, y = logFC, color = FDR <= 0.05)) +
  1129. geom_point() + theme_bw() +
  1130. scale_color_manual(values = c("TRUE" = "red", "FALSE" = "black")) +
  1131. ggtitle(nm))
  1132. cat('\n\n')
  1133. }
  1134. }
  1135. ```
  1136. ## Explore DGE results {.tabset .tabset-pills}
  1137. We export the results into text files that can be opened using any text editor.
  1138. The volcanoes plot the uncorrected p-values, but the coloring (significance) is based on FDR-adjusted p-values.
  1139. Independently of the FDRs, genes with an absolute log2 fold-change greater than 0.5 are also reported (coloring and vertical lines will at the negative and positive values of the FC cut-off).
  1140. ```{r edgeR-save-results, results = 'asis', fig.width = 8, fig.height = 8}
  1141. ## Write results to text files and make MA plots
  1142. if (is(edgeR_res, "data.frame")) {
  1143. write.table(edgeR_res %>% dplyr::arrange(PValue) %>%
  1144. dplyr::select(-dplyr::any_of("tx_ids")),
  1145. file = "edgeR_dge_results.txt",
  1146. sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1147. } else {
  1148. for (nm in names(edgeR_res)) {
  1149. cat('### ', nm, '\n\n')
  1150. edgeR_res[[nm]]$simple_gene_name <- edgeR_res[[nm]]$gene_name
  1151. edgeR_res[[nm]]$simple_gene_name[is.na(edgeR_res[[nm]]$simple_gene_name)] <- edgeR_res[[nm]]$gene_id[is.na(edgeR_res[[nm]]$simple_gene_name)]
  1152. print(EnhancedVolcano(edgeR_res[[nm]],
  1153. lab = edgeR_res[[nm]]$simple_gene_name,
  1154. x = 'logFC',
  1155. y = 'PValue',
  1156. pCutoffCol = 'FDR',
  1157. title = 'differential gene expression',
  1158. subtitle = nm,
  1159. pCutoff = 0.05,
  1160. FCcutoff = 0.5,
  1161. pointSize = 3.0,
  1162. drawConnectors = TRUE,
  1163. labSize = 6.0))
  1164. fn <- paste0("edgeR_dge_results_", nm, "_all_ages.txt")
  1165. write.table(edgeR_res[[nm]] %>% dplyr::arrange(PValue) %>%
  1166. dplyr::select(-dplyr::any_of("tx_ids")),
  1167. file = fn,
  1168. sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1169. cat(sprintf('\n\n<a href="%s">Download results</a>\n\n', fn))
  1170. ## (DT::datatable(edgeR_res[[nm]], rownames = FALSE,
  1171. ## extensions = 'Buttons',
  1172. ## filter = "none",
  1173. ## options = list(pageLength = 5, autowidth = TRUE,
  1174. ## dom = 'Blftip',
  1175. ## buttons = c('copy', 'csv', 'excel'))))
  1176. cat('\n\n')
  1177. ## write.table(edgeR_res[[nm]] %>% dplyr::arrange(PValue) %>%
  1178. ## dplyr::select(-dplyr::any_of("tx_ids")),
  1179. ## file = paste0("edgeR_dge_results_", nm, ".txt"),
  1180. ## sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1181. }
  1182. }
  1183. ```
  1184. <!-- # Output DGE results as list of `SingleCellExperiment` objects -->
  1185. <!-- Here, we store the analysis results with the original data. The results are -->
  1186. <!-- appended on the `rowData` of the original gene-level `SummarizedExperiment` -->
  1187. <!-- object `sg`. For genes that were filtered out, `NA` values are used in the -->
  1188. <!-- result columns. The updated `sg` could be fed to the R package `iSEE` to -->
  1189. <!-- perform more exploratory and visual analysis. -->
  1190. <!-- ```{r edgeR-se} -->
  1191. <!-- ## add rows (NA) for genes that are filtered out (if any) -->
  1192. <!-- edgeR_resA <- lapply(seq_along(edgeR_res), FUN = function(x) { -->
  1193. <!-- ## All genes -->
  1194. <!-- geneA <- rowData(sg)$gene_id -->
  1195. <!-- ## Genes that are not filtered out -->
  1196. <!-- resX <- edgeR_res[[x]] -->
  1197. <!-- resX <- resX %>% -->
  1198. <!-- dplyr::select(c("gene_id", "gene_name", "logFC", "logCPM", -->
  1199. <!-- "F", "FDR", "PValue", "mlog10PValue")) -->
  1200. <!-- rownames(resX) <- resX$gene_id -->
  1201. <!-- ## Genes that are filtered out -->
  1202. <!-- geneO <- setdiff(geneA, resX$gene_id) -->
  1203. <!-- ## results for all genes -->
  1204. <!-- if (length(geneO) > 0) { -->
  1205. <!-- ## create a data frame with values NA as the results of the genes that -->
  1206. <!-- ## are filtered out -->
  1207. <!-- matO <- matrix(NA, nrow = length(geneO), -->
  1208. <!-- ncol = ncol(resX), -->
  1209. <!-- dimnames = list(geneO, -->
  1210. <!-- colnames(resX))) -->
  1211. <!-- resO <- data.frame(matO) -->
  1212. <!-- resO$gene_id <- geneO -->
  1213. <!-- resO$gene_name <- rowData(sg)$gene_name[match(geneO, rowData(sg)$gene_id)] -->
  1214. <!-- ## Combine the result tables -->
  1215. <!-- resA <- resO %>% -->
  1216. <!-- dplyr::bind_rows(resX) %>% -->
  1217. <!-- dplyr::arrange(match(gene_id, geneA)) %>% -->
  1218. <!-- dplyr::mutate(contrast = names(edgeR_res)[[x]]) -->
  1219. <!-- } else { -->
  1220. <!-- resA <- resX %>% -->
  1221. <!-- dplyr::arrange(match(gene_id, geneA)) %>% -->
  1222. <!-- dplyr::mutate(contrast = names(edgeR_res)[[x]]) -->
  1223. <!-- } -->
  1224. <!-- ## Use gene column as rownames -->
  1225. <!-- rownames(resA) <- paste(resA$gene_id, resA$gene_name, sep = "__") -->
  1226. <!-- ## convert to DataFrame -->
  1227. <!-- resA <- S4Vectors::DataFrame(resA) -->
  1228. <!-- return(resA) -->
  1229. <!-- }) -->
  1230. <!-- names(edgeR_resA) <- names(edgeR_res) -->
  1231. <!-- ## Put the result tables in rowData -->
  1232. <!-- for (i in seq_along(edgeR_resA)) { -->
  1233. <!-- nam <- names(edgeR_resA)[i] -->
  1234. <!-- namI <- paste("edgeR:", nam, sep = "") -->
  1235. <!-- stopifnot(all(rownames(sg) == rownames(edgeR_resA[[i]]))) -->
  1236. <!-- rowData(sg)[[namI]] <- edgeR_resA[[i]] -->
  1237. <!-- } -->
  1238. <!-- ``` -->
  1239. <!-- The output is saved as a list. Compared to the input data `se`, the element `sg` -->
  1240. <!-- is updated and `st` stays the same. -->
  1241. <!-- ```{r edgeR-save-se} -->
  1242. <!-- analysis_se <- list(sg = sg, st = se$st) -->
  1243. <!-- saveRDS(analysis_se, file = "edgeR_dge.rds") -->
  1244. <!-- ``` -->
  1245. <!-- ```{r check-gene_names-column, eval = !is.null(genesets), include = FALSE} -->
  1246. <!-- if(!("gene_name" %in% colnames(rowData(sg)))) { -->
  1247. <!-- genesets <- NULL -->
  1248. <!-- } -->
  1249. <!-- ``` -->
  1250. ## Geneset analysis {.tabset .tabset-pills}
  1251. We will use `camera` to perform an enrichment analysis for a collection of
  1252. gene sets from the [mSigDB](http://software.broadinstitute.org/gsea/msigdb),
  1253. packaged in the `msigdbr` R package. Here, we load the gene set definitions
  1254. and select which ones to include in the analysis. Genesets included:
  1255. <!-- - H, hallmark gene sets -->
  1256. <!-- - C1, positional gene sets (cytobands) -->
  1257. - C2, curated gene sets from online pathway databases, publications in PubMed, and knowledge of domain experts
  1258. <!-- - C3, regulatory target gene sets based on gene target predictions for microRNA seed sequences and predicted transcription factor binding sites -->
  1259. - C5, Gene Ontology
  1260. - C8, cell type signatures
  1261. ```{r camera-load-genesets, eval = !is.null(genesets), include = !is.null(genesets)}
  1262. ## genesets <- 'H,C1,C2,C3,C5,C8'
  1263. genesets <- 'C2,C5,C8'
  1264. genesets <- strsplit(gsub(" ","",genesets), ",")[[1]]
  1265. organism <- 'human'
  1266. ## Retrieve gene sets and combine in a tibble
  1267. m_df <- bind_rows(lapply(genesets,
  1268. function(x) msigdbr(species = organism, category = x)))
  1269. ```
  1270. ```{r camera-text2, echo= FALSE, results = 'asis', eval = !is.null(genesets)}
  1271. cat("We consider only gene sets where the
  1272. number of genes shared with the data set is not too small and not too large.
  1273. `camera` is a competitive gene set test that accounts for correlations among
  1274. the genes within a gene set.")
  1275. ```
  1276. ```{r camera-filter-gene-sets, eval = !is.null(genesets), include = !is.null(genesets)}
  1277. minSize <- 3
  1278. maxSize <- 500
  1279. ## Get index for genes in each gene set in the DGEList
  1280. indexList <- limma::ids2indices(
  1281. gene.sets = lapply(split(m_df, f = m_df$gs_name), function(w) w$gene_symbol),
  1282. identifiers = dge$genes$gene_name,
  1283. remove.empty = TRUE
  1284. )
  1285. ## Filter out too small or too large gene sets
  1286. gsSizes <- vapply(indexList, length, 0)
  1287. indexList <- indexList[gsSizes >= minSize & gsSizes <= maxSize]
  1288. ```
  1289. ```{r camera-check-indexList-length, eval = !is.null(genesets), include = FALSE}
  1290. ## Check if the index list is empty after filtering
  1291. if (length(indexList) == 0){
  1292. genesets <- NULL
  1293. empty <- TRUE
  1294. } else {
  1295. empty <- FALSE
  1296. }
  1297. ```
  1298. ```{r, echo = FALSE, results = 'asis', eval = !is.null(genesets) && empty}
  1299. cat("**NOTE:**
  1300. The index list is empty after filtering and `camera` cannot be run. Either try
  1301. different gene categories, try different filtering parameters or disable the
  1302. gene set analysis in the `config.yaml` file by setting `run_camera: False`.")
  1303. ```
  1304. ```{r, eval = !is.null(genesets), include = !is.null(genesets)}
  1305. camera_res <- lapply(contrasts, function(cm) {
  1306. camera(dge, index = indexList, design = des, contrast = cm,
  1307. inter.gene.cor = NA)
  1308. })
  1309. ```
  1310. ```{r camera-save-results, results = 'asis', eval = !is.null(genesets), include = !is.null(genesets)}
  1311. ## Write results to text files
  1312. if (is(camera_res, "data.frame")) {
  1313. ## write.table(camera_res %>% tibble::rownames_to_column("GeneSet") %>%
  1314. ## dplyr::arrange(PValue),
  1315. ## file = "camera_dge_results.txt",
  1316. ## sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1317. } else {
  1318. for (nm in names(camera_res)) {
  1319. cat('### ', nm, '\n\n')
  1320. cat('Significantly (before multiple testing correction, pvalue 0.05) enriched/depleted genesets for this contrast:')
  1321. print(sum(camera_res[[nm]]$PValue < 0.05))
  1322. cat('\n\n')
  1323. cat('Significantly (FDR < 0.05) enriched/depleted genesets for this contrast:')
  1324. print(sum(camera_res[[nm]]$FDR < 0.05))
  1325. cat('\n\n')
  1326. fn <- paste0("camera_dge_results_", nm, "_all_ages.txt")
  1327. write.table(camera_res[[nm]] %>%
  1328. tibble::rownames_to_column("GeneSet") %>%
  1329. dplyr::arrange(PValue),
  1330. file = fn,
  1331. sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1332. cat(sprintf('\n\n<a href="%s">Download geneset analysis results</a>\n\n', fn))
  1333. cat('\n\n')
  1334. }
  1335. }
  1336. ```
  1337. ```{r camera-save-se, eval = !is.null(genesets), include = !is.null(genesets)}
  1338. geneSets <- lapply(indexList, function(i) dge$genes$gene_name[i])
  1339. saveRDS(list(cameraRes = camera_res,
  1340. geneSets = geneSets), file = "camera_gsa_all_ages.rds")
  1341. ```
  1342. # Differential expression: mutants vs 28-days-old WTs
  1343. ```{r}
  1344. sg <- sg[,colData(sg)$timepoint == 28]
  1345. metadata <- colData(sg)
  1346. ```
  1347. ## Plot total number of reads per sample
  1348. ```{r fig.width = 4, fig.height = 4}
  1349. ggplot(data.frame(totCount = colSums(assay(sg, "counts")),
  1350. sample = colnames(assay(sg, "counts")),
  1351. stringsAsFactors = FALSE),
  1352. aes(x = sample, y = totCount)) + geom_bar(stat = "identity") +
  1353. theme_bw() + xlab("") + ylab("Total read count") +
  1354. theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5))
  1355. ```
  1356. ## Create DGEList and include average transcript length offsets
  1357. A `DGEList` is the main object `edgeR` requires to perform the DGE analysis. It
  1358. is designed to store read counts and associated information. After creating this
  1359. object, we add offsets, which are average transcript length correction terms
  1360. [@Soneson2016tximport],
  1361. and scale them so they are consistent with library sizes (sequencing depth for
  1362. each sample).
  1363. Then we calculate normalization factors to scale the raw library sizes and
  1364. minimize the log-fold changes between the samples for most genes. Here the
  1365. trimmed mean of M-values between each pair of samples (TMM) is used by default
  1366. [@Robinson2010TMM].
  1367. Finally we add gene annotation information.
  1368. ```{r}
  1369. dge0 <- tximeta::makeDGEList(sg)
  1370. dge0$genes <- as.data.frame(rowRanges(sg))
  1371. ```
  1372. ## Calculate logCPMs and add as an assay
  1373. We calculate log-counts per million (CPMs) because they are useful descriptive
  1374. measures for the expression level of a gene. Note, however, that the normalized
  1375. values are not used for the differential expression analysis. By default, the
  1376. normalized library sizes are used in the computation.
  1377. We add the logCPMs to one of the fields (or assay) of the first gene-level
  1378. `SummarizedExperiment` object `sg`. At the end of the analysis, we will use this
  1379. object again to export the results of all the genes we started with.
  1380. ```{r}
  1381. logcpms <- edgeR::cpm(dge0, offset = dge0$offset, log = TRUE,
  1382. prior.count = 2)
  1383. dimnames(logcpms) <- dimnames(dge0$counts)
  1384. stopifnot(all(rownames(logcpms) == rownames(sg)),
  1385. all(colnames(logcpms) == colnames(sg)))
  1386. assay(sg, "logcpm") <- logcpms
  1387. ```
  1388. Next, we specify the design matrix of the experiment, defining which sample
  1389. annotations will be taken into account in the statistical modeling.
  1390. ```{r}
  1391. design <- '~ 0 + treatment'
  1392. ## metadata <- read.table('/home/imallona/avillani_microglia/ARMOR/metadata_ambra_only.tsv', header = TRUE)
  1393. stopifnot(all(colnames(dge0) == metadata$names))
  1394. des <- model.matrix(as.formula(design), data = metadata)
  1395. ```
  1396. ## Filter out lowly expressed genes
  1397. Next we determine which genes have sufficiently large counts to be retained in
  1398. the statistical analysis, and remove the rest. After removing genes, we
  1399. recalculate the normalization factors.
  1400. ```{r}
  1401. dim(dge0)
  1402. keep <- edgeR::filterByExpr(dge0, design = des)
  1403. dge <- dge0[keep, ]
  1404. dim(dge)
  1405. ```
  1406. ## Estimate dispersion and fit QL model
  1407. We model the count data using a quasi-likelihood (QL) negative binomial (NB)
  1408. generalized log-linear model, which accounts for gene-specific variability from
  1409. both biological and technical sources. Before fitting the model, we estimate
  1410. the NB dispersion (overall biological variability across all genes), and the QL
  1411. dispersion (gene-specific) using the `estimateDisp()` function.
  1412. It is also good practice to look at the relationship between the biological
  1413. coefficient of variation (NB dispersion) and the gene abundance (in logCPMs).
  1414. ```{r}
  1415. ## Estimate dispersion and fit model
  1416. dge <- estimateDisp(dge, design = des)
  1417. qlfit <- glmQLFit(dge, design = des)
  1418. ## Plot dispersions
  1419. plotBCV(dge)
  1420. ```
  1421. ## Define contrasts
  1422. Before testing for differences in gene expression, we define the contrasts
  1423. we wish to test for. Here we represent the constrasts as a numeric matrix:
  1424. ```{r}
  1425. contrast <- c('treatmentiPSC_MG_Slc37a2_mut1-treatmentiPSC_MG',
  1426. 'treatmentiPSC_MG_Slc37a2_mut2-treatmentiPSC_MG',
  1427. 'treatmentiPSC_MG_TREM2_mut-treatmentiPSC_MG',
  1428. 'treatmentiPSC_MG_Slc37a2_mut1-treatmentiPSC_MG_TREM2_mut',
  1429. 'treatmentiPSC_MG_Slc37a2_mut2-treatmentiPSC_MG_TREM2_mut',
  1430. 'treatmentiPSC_MG_Slc37a2_mut2-treatmentiPSC_MG_Slc37a2_mut1')
  1431. (contrasts <- as.data.frame(makeContrasts(contrasts = contrast, levels = des)))
  1432. ```
  1433. ```{r}
  1434. signif3 <- function(x) signif(x, digits = 3)
  1435. edgeR_res <- lapply(contrasts, function(cm) {
  1436. qlf <- glmQLFTest(qlfit, contrast = cm)
  1437. tt <- topTags(qlf, n = Inf, sort.by = "none")$table
  1438. tt %>%
  1439. dplyr::mutate(mlog10PValue = -log10(PValue)) %>%
  1440. dplyr::mutate_at(vars(one_of(c("logFC", "logCPM", "F",
  1441. "PValue", "FDR", "mlog10PValue"))),
  1442. list(signif3))
  1443. })
  1444. ```
  1445. ## DGE tests and MA plots {.tabset .tabset-pills}
  1446. We can visualize the test results by plotting the logCPM (average) vs the logFC,
  1447. and coloring genes with an adjusted p-value below 0.05 (or another specificed
  1448. FDR threshold). A plot is drawn for every contrast.
  1449. ```{r, results = 'asis'}
  1450. if (is(edgeR_res, "data.frame")) {
  1451. print(ggplot(edgeR_res, aes(x = logCPM, y = logFC, color = FDR <= 0.05)) +
  1452. geom_point() + theme_bw() +
  1453. scale_color_manual(values = c("TRUE" = "red", "FALSE" = "black")))
  1454. } else {
  1455. for (nm in names(edgeR_res)) {
  1456. cat('### ', nm, '\n\n')
  1457. print(ggplot(edgeR_res[[nm]], aes(x = logCPM, y = logFC, color = FDR <= 0.05)) +
  1458. geom_point() + theme_bw() +
  1459. scale_color_manual(values = c("TRUE" = "red", "FALSE" = "black")) +
  1460. ggtitle(nm))
  1461. cat('\n\n')
  1462. }
  1463. }
  1464. ```
  1465. ## Explore DGE results {.tabset .tabset-pills}
  1466. We export the results into text files that can be opened using any text editor.
  1467. The volcanoes plot the uncorrected p-values, but the coloring (significance) is based on FDR-adjusted p-values.
  1468. Independently of the FDRs, genes with an absolute log2 fold-change greater than 0.5 are also reported (coloring and vertical lines will at the negative and positive values of the FC cut-off).
  1469. ```{r, results = 'asis', fig.width = 8, fig.height = 8}
  1470. ## Write results to text files and make MA plots
  1471. if (is(edgeR_res, "data.frame")) {
  1472. write.table(edgeR_res %>% dplyr::arrange(PValue) %>%
  1473. dplyr::select(-dplyr::any_of("tx_ids")),
  1474. file = "edgeR_dge_results.txt",
  1475. sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1476. } else {
  1477. for (nm in names(edgeR_res)) {
  1478. cat('### ', nm, '\n\n')
  1479. edgeR_res[[nm]]$simple_gene_name <- edgeR_res[[nm]]$gene_name
  1480. edgeR_res[[nm]]$simple_gene_name[is.na(edgeR_res[[nm]]$simple_gene_name)] <- edgeR_res[[nm]]$gene_id[is.na(edgeR_res[[nm]]$simple_gene_name)]
  1481. print(EnhancedVolcano(edgeR_res[[nm]],
  1482. lab = edgeR_res[[nm]]$simple_gene_name,
  1483. x = 'logFC',
  1484. y = 'PValue',
  1485. pCutoffCol = 'FDR',
  1486. title = 'differential gene expression',
  1487. subtitle = nm,
  1488. pCutoff = 0.05,
  1489. FCcutoff = 0.5,
  1490. drawConnectors = TRUE,
  1491. pointSize = 3.0,
  1492. labSize = 6.0))
  1493. fn <- paste0("edgeR_dge_results_", nm, "_28_days_only.txt")
  1494. write.table(edgeR_res[[nm]] %>% dplyr::arrange(PValue) %>%
  1495. dplyr::select(-dplyr::any_of("tx_ids")),
  1496. file = fn,
  1497. sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1498. cat(sprintf('\n\n<a href="%s">Download results</a>\n\n', fn))
  1499. ## (DT::datatable(edgeR_res[[nm]], rownames = FALSE,
  1500. ## extensions = 'Buttons',
  1501. ## filter = "none",
  1502. ## options = list(pageLength = 5, autowidth = TRUE,
  1503. ## dom = 'Blftip',
  1504. ## buttons = c('copy', 'csv', 'excel'))))
  1505. cat('\n\n')
  1506. ## write.table(edgeR_res[[nm]] %>% dplyr::arrange(PValue) %>%
  1507. ## dplyr::select(-dplyr::any_of("tx_ids")),
  1508. ## file = paste0("edgeR_dge_results_", nm, ".txt"),
  1509. ## sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1510. }
  1511. }
  1512. ```
  1513. <!-- # Output DGE results as list of `SingleCellExperiment` objects -->
  1514. <!-- Here, we store the analysis results with the original data. The results are -->
  1515. <!-- appended on the `rowData` of the original gene-level `SummarizedExperiment` -->
  1516. <!-- object `sg`. For genes that were filtered out, `NA` values are used in the -->
  1517. <!-- result columns. The updated `sg` could be fed to the R package `iSEE` to -->
  1518. <!-- perform more exploratory and visual analysis. -->
  1519. <!-- ```{r edgeR-se} -->
  1520. <!-- ## add rows (NA) for genes that are filtered out (if any) -->
  1521. <!-- edgeR_resA <- lapply(seq_along(edgeR_res), FUN = function(x) { -->
  1522. <!-- ## All genes -->
  1523. <!-- geneA <- rowData(sg)$gene_id -->
  1524. <!-- ## Genes that are not filtered out -->
  1525. <!-- resX <- edgeR_res[[x]] -->
  1526. <!-- resX <- resX %>% -->
  1527. <!-- dplyr::select(c("gene_id", "gene_name", "logFC", "logCPM", -->
  1528. <!-- "F", "FDR", "PValue", "mlog10PValue")) -->
  1529. <!-- rownames(resX) <- resX$gene_id -->
  1530. <!-- ## Genes that are filtered out -->
  1531. <!-- geneO <- setdiff(geneA, resX$gene_id) -->
  1532. <!-- ## results for all genes -->
  1533. <!-- if (length(geneO) > 0) { -->
  1534. <!-- ## create a data frame with values NA as the results of the genes that -->
  1535. <!-- ## are filtered out -->
  1536. <!-- matO <- matrix(NA, nrow = length(geneO), -->
  1537. <!-- ncol = ncol(resX), -->
  1538. <!-- dimnames = list(geneO, -->
  1539. <!-- colnames(resX))) -->
  1540. <!-- resO <- data.frame(matO) -->
  1541. <!-- resO$gene_id <- geneO -->
  1542. <!-- resO$gene_name <- rowData(sg)$gene_name[match(geneO, rowData(sg)$gene_id)] -->
  1543. <!-- ## Combine the result tables -->
  1544. <!-- resA <- resO %>% -->
  1545. <!-- dplyr::bind_rows(resX) %>% -->
  1546. <!-- dplyr::arrange(match(gene_id, geneA)) %>% -->
  1547. <!-- dplyr::mutate(contrast = names(edgeR_res)[[x]]) -->
  1548. <!-- } else { -->
  1549. <!-- resA <- resX %>% -->
  1550. <!-- dplyr::arrange(match(gene_id, geneA)) %>% -->
  1551. <!-- dplyr::mutate(contrast = names(edgeR_res)[[x]]) -->
  1552. <!-- } -->
  1553. <!-- ## Use gene column as rownames -->
  1554. <!-- rownames(resA) <- paste(resA$gene_id, resA$gene_name, sep = "__") -->
  1555. <!-- ## convert to DataFrame -->
  1556. <!-- resA <- S4Vectors::DataFrame(resA) -->
  1557. <!-- return(resA) -->
  1558. <!-- }) -->
  1559. <!-- names(edgeR_resA) <- names(edgeR_res) -->
  1560. <!-- ## Put the result tables in rowData -->
  1561. <!-- for (i in seq_along(edgeR_resA)) { -->
  1562. <!-- nam <- names(edgeR_resA)[i] -->
  1563. <!-- namI <- paste("edgeR:", nam, sep = "") -->
  1564. <!-- stopifnot(all(rownames(sg) == rownames(edgeR_resA[[i]]))) -->
  1565. <!-- rowData(sg)[[namI]] <- edgeR_resA[[i]] -->
  1566. <!-- } -->
  1567. <!-- ``` -->
  1568. <!-- The output is saved as a list. Compared to the input data `se`, the element `sg` -->
  1569. <!-- is updated and `st` stays the same. -->
  1570. <!-- ```{r edgeR-save-se} -->
  1571. <!-- analysis_se <- list(sg = sg, st = se$st) -->
  1572. <!-- saveRDS(analysis_se, file = "edgeR_dge.rds") -->
  1573. <!-- ``` -->
  1574. <!-- ```{r check-gene_names-column, eval = !is.null(genesets), include = FALSE} -->
  1575. <!-- if(!("gene_name" %in% colnames(rowData(sg)))) { -->
  1576. <!-- genesets <- NULL -->
  1577. <!-- } -->
  1578. <!-- ``` -->
  1579. ## Geneset analysis {.tabset .tabset-pills}
  1580. ```{r, eval = !is.null(genesets), include = !is.null(genesets)}
  1581. ## genesets <- 'H,C1,C2,C3,C5,C8'
  1582. genesets <- 'C2,C5,C8'
  1583. genesets <- strsplit(gsub(" ","",genesets), ",")[[1]]
  1584. organism <- 'human'
  1585. ## Retrieve gene sets and combine in a tibble
  1586. m_df <- bind_rows(lapply(genesets,
  1587. function(x) msigdbr(species = organism, category = x)))
  1588. minSize <- 3
  1589. maxSize <- 500
  1590. ## Get index for genes in each gene set in the DGEList
  1591. indexList <- limma::ids2indices(
  1592. gene.sets = lapply(split(m_df, f = m_df$gs_name), function(w) w$gene_symbol),
  1593. identifiers = dge$genes$gene_name,
  1594. remove.empty = TRUE
  1595. )
  1596. ## Filter out too small or too large gene sets
  1597. gsSizes <- vapply(indexList, length, 0)
  1598. indexList <- indexList[gsSizes >= minSize & gsSizes <= maxSize]
  1599. ```
  1600. ```{r, eval = !is.null(genesets), include = FALSE}
  1601. ## Check if the index list is empty after filtering
  1602. if (length(indexList) == 0){
  1603. genesets <- NULL
  1604. empty <- TRUE
  1605. } else {
  1606. empty <- FALSE
  1607. }
  1608. ```
  1609. ```{r, eval = !is.null(genesets), include = !is.null(genesets)}
  1610. geneSets <- lapply(indexList, function(i) dge$genes$gene_name[i])
  1611. saveRDS(list(cameraRes = camera_res,
  1612. geneSets = geneSets), file = "camera_gsa_28_days_only.rds")
  1613. ```
  1614. ```{r camera-perform-tests, cache = FALSE, eval = !is.null(genesets), include = !is.null(genesets)}
  1615. camera_res <- lapply(contrasts, function(cm) {
  1616. camera(dge, index = indexList, design = des, contrast = cm,
  1617. inter.gene.cor = NA)
  1618. })
  1619. ```
  1620. ```{r, results = 'asis', eval = !is.null(genesets), include = !is.null(genesets)}
  1621. ## Write results to text files
  1622. if (is(camera_res, "data.frame")) {
  1623. ## write.table(camera_res %>% tibble::rownames_to_column("GeneSet") %>%
  1624. ## dplyr::arrange(PValue),
  1625. ## file = "camera_dge_results.txt",
  1626. ## sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1627. } else {
  1628. for (nm in names(camera_res)) {
  1629. cat('### ', nm, '\n\n')
  1630. cat('Significantly (before multiple testing correction, pvalue 0.05) enriched/depleted genesets for this contrast:')
  1631. print(sum(camera_res[[nm]]$PValue < 0.05))
  1632. cat('\n\n')
  1633. cat('Significantly (FDR < 0.05) enriched/depleted genesets for this contrast:')
  1634. print(sum(camera_res[[nm]]$FDR < 0.05))
  1635. cat('\n\n')
  1636. fn <- paste0("camera_dge_results_", nm, "_28_days_only.txt")
  1637. write.table(camera_res[[nm]] %>%
  1638. tibble::rownames_to_column("GeneSet") %>%
  1639. dplyr::arrange(PValue),
  1640. file = fn,
  1641. sep = "\t", row.names = FALSE, col.names = TRUE, quote = FALSE)
  1642. cat(sprintf('\n\n<a href="%s">Download geneset analysis results</a>\n\n', fn))
  1643. cat('\n\n')
  1644. }
  1645. }
  1646. ```
  1647. # Consistency check
  1648. ```{r}
  1649. a0 <- readRDS('ambra_villani_only_sce.rds')
  1650. a1 <- readRDS('villani_plus_abud_sce.rds')
  1651. a2 <- readRDS('all_data_sce.rds')
  1652. set.seed(54)
  1653. genes <- sample(rownames(a0), 10, replace = FALSE)
  1654. id <- '20220504.B-TREM2-A1_2_R1'
  1655. stopifnot(all(assay(a0, 'logcpm')[genes,id] == assay(a1, 'logcpms')[genes,id]) )
  1656. stopifnot(all(assay(a0, 'logcpm')[genes,id] == assay(a2, 'logcpms')[genes,id]) )
  1657. stopifnot(all(assay(a0, 'counts')[genes,id] == assay(a1, 'counts')[genes,id]) )
  1658. stopifnot(all(assay(a0, 'counts')[genes,id] == assay(a2, 'counts')[genes,id]) )
  1659. ## stopifnot(all(assay(a0, 'cpms')[genes,id] == assay(a1, 'cpms')[genes,id]) )
  1660. ## stopifnot(all(assay(a0, 'cpms')[genes,id] == assay(a2, 'cpms')[genes,id]))
  1661. print('passed')
  1662. ```
  1663. # Plot custom logCPMs
  1664. # Timestamp
  1665. ```{r sessionInfo2, cache = FALSE}
  1666. date()
  1667. sessionInfo()
  1668. ## devtools::session_info()
  1669. ```

01_custom_analysis_postmeeting.Rmd, under GPL-3.0 · at the source

Overview

Authors: Ambra Villani1, Jana Wittmann1, Tamara Wyss1, Izaskun Mallona1,2, Irene Santisteban Ortiz3,4, Nathalie Tichy1, Corinna Maria Biermeier1, Monique Pena3,4, Ayush Aditya Pal1, Darren Gilmour1, Simon T Schafer3,4, Francesca Peri1
  1. Department of Molecular Life Sciences, University of Zurich, Zurich, Switzerland
  2. SIB, Swiss Institute of Bioinformatics, Zurich, Switzerland
  3. Department of Psychiatry and Psychotherapy, School of Medicine and Health, Technical University of Munich, Munich, Germany
  4. Center for Organoid Systems, Munich Institute for Biomedical Engineering, Technical University of Munich, Garching, Germany
Journal: Communications biology, volume 9, issue 1, article 785
Dates: received 15 August 2025; accepted 17 March 2026; published online 9 April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1038/s42003-026-09948-6 · PMID 41957412 · PMCID PMC13250125 · OpenAlex W4413855248
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), zebrafish (organism), cellular / molecular (subfield)
Methods: Statistics, Evoked potentials, fMRI & imaging
Keywords: Cell death and immune response, Apoptosis
MeSH: Microglia*, Neurons*, Zebrafish*, Animals, Apoptosis, Brain, Efferocytosis, Humans, Induced Pluripotent Stem Cells, Transplantation, Heterologous (* major topic)
Topic: Neuroinflammation and Neurodegeneration Mechanisms (Neurology, Neuroscience), according to OpenAlex
Funding: Swiss National Science Foundation (212794, 310030_204834, 310030, 310030_212794)
Citations: not cited yet (Europe PMC); 71 references in the paper
Research resources: RRID:SCR_007370

Abstract

Microglia engulf dying neurons through efferocytosis, a critical function in both development and disease. How microglia process the engulfed neuronal material—especially lipids—remains poorly understood, despite its central role in neurodegeneration. Thus, we developed HuZIBRA, a scalable in vivo xenotransplantation model in which human iPSC-derived microglia-like cells (iMGLs) are introduced into the developing zebrafish brain (zf-hiMG), a system characterized by high levels of neuronal cell death and amenable to precise genetic and pharmacological manipulation. We show that human microglia-like cells recognize and engulf apoptotic zebrafish neurons, indicating conserved efferocytic mechanisms. In these cells, engulfed neuronal material accumulates into a distinct, lipid-rich intracellular compartment, the gastrosome, which we also observed in iMGLs placed in a human brain-like environment. The size of the human gastrosome dynamically reflects neuronal cell death levels and is regulated by key genes, including TREM2 and SLC37A2. Pharmacological inhibition of the cholesterol transporter NPC1 induces gastrosome expansion and lipid accumulation, recapitulating pathological features of Niemann-Pick disease type C. Thus, HuZIBRA provides a powerful in vivo platform to uncover cell-autonomous adaptive responses of human microglia to apoptotic and metabolic stress, with the gastrosome emerging as a key integrator of neuronal debris processing and disease-relevant lipid metabolism.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 2 matches between paragraphs and lines of code.

Zenodo 18997850

License: GPL-3.0
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Data availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: ggplot2 (9 files), SingleCellExperiment (9 files), ComplexHeatmap (6 files), circlize (3 files), limma (3 files), tidyverse (3 files), edgeR (2 files), Snakemake (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers (HTTP 200)
  • 29 September 2026: the link answers (HTTP 200)
16 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 14 scripts, each with its path and the digest of its content;
  • 2 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data availability

Numerical data related to this manuscript are provided in Supplementary Data 1. The imaging data generated in this study are available from the corresponding author on reasonable request. Raw and processed RNA-seq data generated in this study are available at GEO GSE324751 (https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE324751). Reanalyzed data were downloaded from SRA accessions SRP092075 and SRP155574. Code to analyze the RNA-seq data is available at https://zenodo.org/records/18997850 under the GPLv3 terms.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 29 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 12 authors, 2 keywords, 10 MeSH terms, 1 funder, 70 references, 1 RRID.

Cite

This paper

Villani, A., Wittmann, J., Wyss, T., Mallona, I., Santisteban Ortiz, I., Tichy, N., Biermeier, C. M., Pena, M., Pal, A. A., Gilmour, D., Schafer, S. T., & Peri, F. (2026). A scalable human-zebrafish xenotransplantation model reveals gastrosome-mediated processing of dying neurons by human microglia. Communications biology, 9(1), 785. https://doi.org/10.1038/s42003-026-09948-6

BibTeX

@article{villani2026scalable,
author = {Villani, Ambra and Wittmann, Jana and Wyss, Tamara and Mallona, Izaskun and Santisteban Ortiz, Irene and Tichy, Nathalie and Biermeier, Corinna Maria and Pena, Monique and Pal, Ayush Aditya and Gilmour, Darren and Schafer, Simon T and Peri, Francesca},
title = {{A scalable human-zebrafish xenotransplantation model reveals gastrosome-mediated processing of dying neurons by human microglia}},
journal = {Communications biology},
year = {2026},
month = apr,
volume = {9},
number = {1},
pages = {785},
publisher = {Nature Publishing Group},
issn = {2399-3642},
doi = {10.1038/s42003-026-09948-6},
url = {https://doi.org/10.1038/s42003-026-09948-6},
pmid = {41957412},
pmcid = {PMC13250125}
}

RIS

TY - JOUR
AU - Villani, Ambra
AU - Wittmann, Jana
AU - Wyss, Tamara
AU - Mallona, Izaskun
AU - Santisteban Ortiz, Irene
AU - Tichy, Nathalie
AU - Biermeier, Corinna Maria
AU - Pena, Monique
AU - Pal, Ayush Aditya
AU - Gilmour, Darren
AU - Schafer, Simon T
AU - Peri, Francesca
TI - A scalable human-zebrafish xenotransplantation model reveals gastrosome-mediated processing of dying neurons by human microglia
T2 - Communications biology
J2 - Commun Biol
PY - 2026
DA - 2026/04/09
VL - 9
IS - 1
SP - 785
SN - 2399-3642
PB - Nature Publishing Group
DO - 10.1038/s42003-026-09948-6
UR - https://doi.org/10.1038/s42003-026-09948-6
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s42003-026-09948-6",
"type": "article-journal",
"title": "A scalable human-zebrafish xenotransplantation model reveals gastrosome-mediated processing of dying neurons by human microglia",
"container-title": "Communications biology",
"author": [
{
"family": "Villani",
"given": "Ambra"
},
{
"family": "Wittmann",
"given": "Jana"
},
{
"family": "Wyss",
"given": "Tamara"
},
{
"family": "Mallona",
"given": "Izaskun"
},
{
"family": "Santisteban Ortiz",
"given": "Irene"
},
{
"family": "Tichy",
"given": "Nathalie"
},
{
"family": "Biermeier",
"given": "Corinna Maria"
},
{
"family": "Pena",
"given": "Monique"
},
{
"family": "Pal",
"given": "Ayush Aditya"
},
{
"family": "Gilmour",
"given": "Darren"
},
{
"family": "Schafer",
"given": "Simon T"
},
{
"family": "Peri",
"given": "Francesca"
}
],
"container-title-short": "Commun Biol",
"volume": "9",
"issue": "1",
"page": "785",
"DOI": "10.1038/s42003-026-09948-6",
"PMID": "41957412",
"PMCID": "PMC13250125",
"ISSN": "2399-3642",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s42003-026-09948-6",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
9
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41593-026-02367-0 [code]
A reproducible three-dimensional model of human brain tissue to investigate physiological and disease-associated microglia phenotypes.
Journal: Nature neuroscience
In common: SingleCellExperiment, edgeR, limma, 4 other tools, cellular / molecular, 7 references
[2] doi:10.1002/imt2.70163 [code]
Spatial multi-omics unveils sphingolipid metabolic reprogramming within the retinal pathological niche.
Journal: iMeta
In common: SingleCellExperiment, edgeR, limma, 4 other tools, cellular / molecular, 3 references
[3] doi:10.1016/j.xcrm.2026.102766 [code]
A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.
Journal: Cell reports. Medicine
In common: SingleCellExperiment, edgeR, limma, 4 other tools, cellular / molecular, 1 reference
[4] doi:10.1016/j.neuron.2026.07.007 [code]
Human-specific SRGAP2 paralogs synchronize neotenic microglial maturation and synaptic development.
Journal: Neuron
In common: ggplot2, tidyverse, 7 references
[5] doi:10.1038/s41386-026-02406-1 [code]
Functional genomic profiling of schizophrenia-associated genes reveals key microglial regulators.
Journal: Neuropsychopharmacology : official publication of the American College of Neuropsychopharmacology
In common: limma, circlize, ComplexHeatmap, 2 other tools, cellular / molecular, 3 references
[6] doi:10.1038/s41467-026-76341-6 [code]
Neonatal inflammation disrupts a temporally restricted postnatal Numb-enriched microglial state in mice.
Journal: Nature communications
In common: SingleCellExperiment, circlize, ComplexHeatmap, 2 other tools, 3 references
[7] doi:10.1038/s41467-026-71790-5 [code]
Recurrent DNA break clusters drive replication-stress-induced copy number variants and genome diversification.
Journal: Nature communications
In common: Snakemake, edgeR, circlize, 3 other tools, cellular / molecular, 1 reference
[8] doi:10.1038/s41467-026-70989-w [code]
MIC-Drop-seq: scalable single-cell phenotyping of mutant vertebrate embryos.
Journal: Nature communications
In common: SingleCellExperiment, edgeR, circlize, 3 other tools, zebrafish, 1 reference
[9] doi:10.1111/adb.70179 [code]
Transcriptional Response to Chronic Long-Access Fentanyl Self-Administration in Rat Habenula and Amygdala.
Journal: Addiction biology
In common: SingleCellExperiment, edgeR, limma, 4 other tools, cellular / molecular
[10] doi:10.1016/j.isci.2026.115573 [code]
Female cortical cellular mosaicism underlies shared MeCP2 and PCB impacted gene pathways.
Journal: iScience
In common: SingleCellExperiment, edgeR, limma, 4 other tools, cellular / molecular

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.