OSCR

Zika virus infections of human stem cell-derived cerebral organoids reveal viral lineage-specific pathogenesis responses.

Code ↔ Paper

4 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 4 matches · 1 of them tie a paragraph to a whole file, not to given lines: a weak match, whose lines are not tinted
  1. [1] § MATERIALS AND METHODS › Bulk RNA isolation and sequencing ↔ BulkRNA-seq_umi/nf-core_rnaseq/nf-core_rnaseq_umi.sh, the whole file · a weak match · score 0.75 · nf core, plus virus, gencode, hg38, MIT, genomes
  2. [2] § RESULTS › ZIKV-induced growth defect severity correlates with cytoarchitectural disruption ↔ scRNA-seq/020222_Gehrke.Rmd, lines 450–458 · score 0.60 · intermediate progenitor cells, neuronal progenitor cells, astrocytes, radial, Neurons
  3. [3] § RESULTS › Bulk RNA-seq reveals differential stress responses among ZIKV lineage infections ↔ scRNA-seq/020222_Gehrke.Rmd, lines 1028–1080 · score 0.59 · Log2 FC, stress response, fold change, Volcano, adj, cutoffs
  4. [4] § RESULTS › Bulk RNA-seq reveals differential stress responses among ZIKV lineage infections ↔ scRNA-seq/020222_Gehrke.Rmd, lines 1028–1080 · score 0.51 · log2 FC, stress response, overlaps, adj, folded, UG

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 · 1,145 lines · 44 KB · MIT · 3 matches

  1. ---
  2. title: "scRNA-seq processing"
  3. author: "Charlie Whittaker and Yann Vanrobaeys"
  4. date: "2024-07-08"
  5. output:
  6. html_document:
  7. toc: true
  8. toc_depth: 3
  9. html_notebook: default
  10. ---
  11. ```{r setup, include=FALSE}
  12. knitr::opts_chunk$set(tidy=FALSE, cache=TRUE,
  13. dev="png", message=FALSE, error=FALSE, warning=TRUE)
  14. ```
  15. ```{r}
  16. library(Seurat)
  17. library(dplyr)
  18. library(ggplot2)
  19. library(openxlsx)
  20. library(SingleR)
  21. library(ggpubr)
  22. library(readxl)
  23. library(writexl)
  24. library(openxlsx)
  25. library(tidyverse)
  26. library(reprex)
  27. library(matrixStats)
  28. library(XML)
  29. library(ggrepel)
  30. library(rtracklayer)
  31. library(DESeq2)
  32. library(apeglm)
  33. library(ComplexHeatmap)
  34. library(edgeR)
  35. library(gprofiler2)
  36. library(cluster)
  37. library(stringr)
  38. library(fgsea)
  39. ```
  40. # Setup the Seurat Object
  41. ## Load the CellRanger outputs of individual sample
  42. ```{r}
  43. d9433_d7_mock.data <- Read10X(data.dir = "CellRanger_filtered_feature_bc_matrix_dir/d9433_d7_mock/filtered_feature_bc_matrix")
  44. d9433_d7_pr.data <- Read10X(data.dir = "CellRanger_filtered_feature_bc_matrix_dir/d9434_d7_pr/filtered_feature_bc_matrix")
  45. d9433_d7_mal.data <- Read10X(data.dir = "CellRanger_filtered_feature_bc_matrix_dir/d9435_d7_mal/filtered_feature_bc_matrix")
  46. d9433_d7_ug.data <- Read10X(data.dir = "CellRanger_filtered_feature_bc_matrix_dir/d9436_d7_ug/filtered_feature_bc_matrix")
  47. ```
  48. ## Initialize the Seurat object with the raw (non-normalized data).
  49. ```{r}
  50. d9433_d7_mock <- CreateSeuratObject(counts = d9433_d7_mock.data, project = "d9433_d7_mock", min.cells = 3, min.features = 200)
  51. d9434_d7_pr <- CreateSeuratObject(counts = d9433_d7_pr.data, project = "d9433_d7_pr", min.cells = 3, min.features = 200)
  52. d9435_d7_mal <- CreateSeuratObject(counts = d9433_d7_mal.data, project = "d9433_d7_mal", min.cells = 3, min.features = 200)
  53. d9436_d7_ug <- CreateSeuratObject(counts = d9433_d7_ug.data, project = "d9433_d7_ug", min.cells = 3, min.features = 200)
  54. d9433_d7_mock[["Condition"]] <- "Mock"
  55. d9434_d7_pr[["Condition"]] <- "Puerto Rico"
  56. d9435_d7_mal[["Condition"]] <- "Malaysia"
  57. d9436_d7_ug[["Condition"]] <- "Uganda"
  58. ```
  59. ## Merging data - All samples
  60. ```{r}
  61. seurat.merge <- merge(
  62. x = d9433_d7_mock,
  63. y = list(d9434_d7_pr, d9435_d7_mal, d9436_d7_ug),
  64. add.cell.ids = c("Mock", "PR", "MAL", "UG"),
  65. project = "020222Gehrke"
  66. )
  67. ```
  68. # Standard pre-processing worflow
  69. ## Process data
  70. ```{r}
  71. seurat.merge <- NormalizeData(seurat.merge, normalization.method = "LogNormalize", scale.factor = 10000)
  72. seurat.merge <- FindVariableFeatures(seurat.merge, selection.method = "vst", nfeatures = 2000)
  73. seurat.merge <- ScaleData(seurat.merge)
  74. seurat.merge <- RunPCA(seurat.merge, features = VariableFeatures(object = seurat.merge))
  75. ElbowPlot(seurat.merge, ndims = 50)
  76. seurat.merge <- RunUMAP(seurat.merge, reduction="pca",dims=1:30)
  77. seurat.merge <- FindNeighbors(seurat.merge, dims = 1:30, verbose = FALSE)
  78. seurat.merge <- FindClusters(seurat.merge, verbose = FALSE)
  79. ```
  80. ## Dim plot to explore the unintegrated data
  81. ```{r}
  82. Idents(seurat.merge) <- 'RNA_snn_res.0.8'
  83. seurat.merge$Condition <- factor(seurat.merge$Condition, levels = c("Mock", "Puerto Rico", "Malaysia", "Uganda"))
  84. DimPlot(seurat.merge,reduction="umap",split.by="Condition",label=TRUE,repel=FALSE) + NoLegend()
  85. ```
  86. ## QCs
  87. ```{r}
  88. seurat.merge[["percent.mt"]] <- PercentageFeatureSet(seurat.merge, pattern = "^MT-")
  89. seurat.merge$log10GenesPerUMI <- log10(seurat.merge$nFeature_RNA) / log10(seurat.merge$nCount_RNA)
  90. l2.n_feature <- log2([email hidden]$nFeature_RNA)
  91. l2.n_count <- log2([email hidden]$nCount_RNA)
  92. seurat.merge <- AddMetaData(seurat.merge, l2.n_feature, "l2.n_feature")
  93. seurat.merge <- AddMetaData(seurat.merge, l2.n_count, "l2.n_count")
  94. p <- table([email hidden]$RNA_snn_res.0.8,[email hidden]$Condition)
  95. p
  96. Idents(seurat.merge) <- 'RNA_snn_res.0.8'
  97. VlnPlot(seurat.merge, features=c("percent.mt"), split.by = "Condition", ncol=1, pt.size=0)+ NoLegend()
  98. VlnPlot(seurat.merge, features=c("l2.n_feature"), split.by = "Condition", ncol=1, pt.size=0)+ NoLegend()
  99. VlnPlot(seurat.merge, features=c("l2.n_count"), split.by = "Condition", ncol=1, pt.size=0)+ NoLegend()
  100. [email hidden] %>%
  101. ggplot(aes(color=orig.ident, x=nCount_RNA, fill= orig.ident)) +
  102. geom_density(alpha = 0.2) +
  103. scale_x_log10() +
  104. theme_classic() +
  105. ylab("Cell density") +
  106. geom_vline(xintercept = 500) +
  107. ggtitle("nCount")
  108. [email hidden] %>%
  109. ggplot(aes(color=orig.ident, x=nFeature_RNA, fill= orig.ident)) +
  110. geom_density(alpha = 0.2) +
  111. scale_x_log10() +
  112. theme_classic() +
  113. ylab("Cell density") +
  114. geom_vline(xintercept = 200) +
  115. ggtitle("nFeature")
  116. [email hidden] %>%
  117. ggplot(aes(x=nCount_RNA, y=nFeature_RNA, color=percent.mt)) +
  118. geom_point() +
  119. scale_color_gradient(low = "gray90", high = "black") +
  120. stat_smooth(method=lm) +
  121. scale_x_log10() +
  122. scale_y_log10() +
  123. theme_classic() +
  124. geom_vline(xintercept = 500) +
  125. geom_hline(yintercept = 250) +
  126. facet_wrap(~orig.ident)
  127. [email hidden] %>%
  128. ggplot(aes(x=log10GenesPerUMI, color = orig.ident, fill=orig.ident)) +
  129. geom_density(alpha = 0.2) +
  130. theme_classic() +
  131. geom_vline(xintercept = 0.8)
  132. ```
  133. ## Gene per UMI filtering of the merged object
  134. ```{r}
  135. seurat.merge.filt <- subset(seurat.merge, percent.mt <= 25 & log10GenesPerUMI > 0.75)
  136. VlnPlot(seurat.merge.filt, group.by = "orig.ident",features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
  137. [email hidden] %>%
  138. ggplot(aes(x=log10GenesPerUMI, color = orig.ident, fill=orig.ident)) +
  139. geom_density(alpha = 0.2) +
  140. theme_classic() +
  141. geom_vline(xintercept = 0.8)
  142. Idents(seurat.merge.filt) <- 'RNA_snn_res.0.8'
  143. DimPlot(seurat.merge.filt,reduction="umap",split.by="Condition",label=TRUE,repel=FALSE) + NoLegend()
  144. ```
  145. ## Gene level filtering of the merged object
  146. ```{r}
  147. seurat.merge.filt2 <- seurat.merge.filt
  148. # Output a logical vector for every gene on whether the more than zero counts per cell
  149. # Extract counts
  150. counts <- GetAssayData(object = seurat.merge.filt2, slot = "counts")
  151. # Output a logical vector for every gene on whether the more than zero counts per cell
  152. nonzero <- counts > 0
  153. # Sums all TRUE values and returns TRUE if more than 10 TRUE values per gene
  154. keep_genes <- Matrix::rowSums(nonzero) >= 10
  155. # Only keeping those genes expressed in more than 10 cells
  156. filtered_counts <- counts[keep_genes, ]
  157. # Reassign to filtered Seurat object
  158. seurat.merge.filt2 <- CreateSeuratObject(filtered_counts, meta.data = [email hidden])
  159. ```
  160. ## Reprocess of the filtered merged object
  161. ```{r}
  162. seurat.merge.filt2 <- NormalizeData(seurat.merge.filt2, normalization.method = "LogNormalize", scale.factor = 10000)
  163. seurat.merge.filt2 <- FindVariableFeatures(seurat.merge.filt2, selection.method = "vst", nfeatures = 2000)
  164. seurat.merge.filt2 <- ScaleData(seurat.merge.filt2)
  165. seurat.merge.filt2 <- RunPCA(seurat.merge.filt2, features = VariableFeatures(object = seurat.merge.filt2))
  166. ElbowPlot(seurat.merge.filt2, ndims = 50)
  167. seurat.merge.filt2 <- RunUMAP(seurat.merge.filt2, reduction="pca",dims=1:30)
  168. seurat.merge.filt2 <- FindNeighbors(seurat.merge.filt2, dims = 1:30, verbose = FALSE)
  169. seurat.merge.filt2 <- FindClusters(seurat.merge.filt2, verbose = FALSE)
  170. Idents(seurat.merge.filt2) <- 'RNA_snn_res.0.8'
  171. DimPlot(seurat.merge.filt2,reduction="umap",split.by="Condition",label=TRUE,repel=FALSE) + NoLegend()
  172. ```
  173. # SCT normalization, MT regression and integration
  174. ```{r}
  175. split_seurat <- SplitObject(seurat.merge.filt2, split.by = "orig.ident")
  176. for (i in 1:length(split_seurat)) {
  177. split_seurat[[i]] <- NormalizeData(split_seurat[[i]], verbose = TRUE)
  178. split_seurat[[i]] <- SCTransform(split_seurat[[i]], vars.to.regress = c("percent.mt"))
  179. }
  180. # Select the most variable features to use for integration
  181. integ_features <- SelectIntegrationFeatures(object.list = split_seurat,
  182. nfeatures = 3000)
  183. # Prepare the SCT list object for integration
  184. split_seurat <- PrepSCTIntegration(object.list = split_seurat,
  185. anchor.features = integ_features)
  186. # Find best buddies - can take a while to run
  187. integ_anchors <- FindIntegrationAnchors(object.list = split_seurat,
  188. normalization.method = "SCT",
  189. anchor.features = integ_features)
  190. # Integrate across conditions
  191. seurat.integrated <- IntegrateData(anchorset = integ_anchors,
  192. normalization.method = "SCT")
  193. ```
  194. ## PCA view of integrated data
  195. ```{r}
  196. # Run PCA
  197. seurat.integrated <- RunPCA(object = seurat.integrated)
  198. # Plot PCA
  199. PCAPlot(seurat.integrated,split.by = "Condition")
  200. # Elbow Plot
  201. ElbowPlot(seurat.integrated, ndims = 50)
  202. ```
  203. # UMAP of integrated data
  204. ```{r}
  205. seurat.integrated <- RunUMAP(seurat.integrated, dims = 1:30, reduction = "pca")
  206. DimPlot(seurat.integrated, split.by = "Condition")
  207. ```
  208. ## Integrated Clustering
  209. ```{r}
  210. # Determine the K-nearest neighbor graph
  211. seurat.integrated <- FindNeighbors(object = seurat.integrated, dims = 1:30)
  212. # Determine the clusters for various resolutions
  213. seurat.integrated <- FindClusters(object = seurat.integrated, resolution = c(0.4, 0.8, 1.2))
  214. ```
  215. ## Dimplots of different resolutions
  216. ```{r fig.width=11, fig.height=4}
  217. Idents(seurat.integrated) <- 'integrated_snn_res.0.4'
  218. DimPlot(seurat.integrated, split.by = "Condition",label=TRUE,repel=FALSE) + NoLegend() + ggtitle("0.4 res")
  219. Idents(seurat.integrated) <- 'integrated_snn_res.0.8'
  220. DimPlot(seurat.integrated, split.by = "Condition",label=TRUE,repel=FALSE) + NoLegend() + ggtitle("0.8 res")
  221. Idents(seurat.integrated) <- 'integrated_snn_res.1.2'
  222. DimPlot(seurat.integrated, split.by = "Condition",label=TRUE,repel=FALSE) + NoLegend() + ggtitle("1.4 res")
  223. ```
  224. ## Proportion tables of integrated data
  225. ```{r}
  226. p <- table([email hidden]$integrated_snn_res.0.4,[email hidden]$orig.ident)
  227. p
  228. (round(prop.table(p,2),3)*100)
  229. ```
  230. # Investigate quality control features of the integrated clusters
  231. ```{r fig.width=5, fig.height=4}
  232. Idents(seurat.integrated,) <- 'integrated_snn_res.0.4'
  233. vln_plot_mt <- VlnPlot(seurat.integrated, features=c("percent.mt"), ncol=1, pt.size=0)+ NoLegend()
  234. vln_plot_feature <- VlnPlot(seurat.integrated, features=c("l2.n_feature"), ncol=1, pt.size=0)+ NoLegend()
  235. vln_plot_count <- VlnPlot(seurat.integrated, features=c("l2.n_count"), ncol=1, pt.size=0)+ NoLegend()
  236. UMAP_plot <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  237. ggarrange(UMAP_plot, vln_plot_mt, vln_plot_feature, vln_plot_count, ncol = 2, nrow = 2)
  238. ```
  239. # Cell type assignment of clusters
  240. ## Identiify cell type biomarkers
  241. ```{r}
  242. DefaultAssay(seurat.integrated)<-"RNA"
  243. Idents(object = seurat.integrated) <- 'integrated_snn_res.0.4'
  244. seurat.integrated.clusterMarkers <- FindAllMarkers(seurat.integrated, only.pos=TRUE, min.pct=0.25)
  245. write.xlsx(seurat.integrated.clusterMarkers, file="seurat.integrated.clusterMarkers.xlsx", rowNames=TRUE, overwrite = TRUE)
  246. ```
  247. ## Biomarkers gene expression pattern
  248. ### Biomarker TTR
  249. ```{r fig.width=11, fig.height=4}
  250. vln_plot_TTR <- VlnPlot(seurat.integrated, features = "TTR", pt.size=0) + NoLegend()
  251. UMAP_plot <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  252. feature_plot_TTR <- FeaturePlot(seurat.integrated, features = c("TTR"), min.cutoff = "q10", max.cutoff = "q90")
  253. ggarrange(vln_plot_TTR, UMAP_plot, feature_plot_TTR, ncol = 3, nrow = 1)
  254. ```
  255. ### Biomarker PCP4
  256. ```{r fig.width=11, fig.height=4}
  257. vln_plot_PCP4 <- VlnPlot(seurat.integrated, features = "PCP4", pt.size=0) + NoLegend()
  258. UMAP_plot_PCP4 <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  259. feature_plot_PCP4 <- FeaturePlot(seurat.integrated, features = c("PCP4"), min.cutoff = "q10", max.cutoff = "q90")
  260. ggarrange(vln_plot_PCP4, UMAP_plot_PCP4, feature_plot_PCP4, ncol = 3, nrow = 1)
  261. ```
  262. ### Biomarker STMN2
  263. ```{r fig.width=11, fig.height=4}
  264. vln_plot_STMN2 <- VlnPlot(seurat.integrated, features = "STMN2", pt.size=0) + NoLegend()
  265. UMAP_plot_STMN2 <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  266. feature_plot_STMN2 <- FeaturePlot(seurat.integrated, features = c("STMN2"), min.cutoff = "q10", max.cutoff = "q90")
  267. ggarrange(vln_plot_EOMES, UMAP_plot_EOMES, feature_plot_EOMES, ncol = 3, nrow = 1)
  268. ```
  269. ### Biomarker MAP2
  270. ```{r fig.width=11, fig.height=4}
  271. vln_plot_MAP2 <- VlnPlot(seurat.integrated, features = "MAP2", pt.size=0) + NoLegend()
  272. UMAP_plot <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  273. feature_plot_MAP2 <- FeaturePlot(seurat.integrated, features = c("MAP2"), min.cutoff = "q10", max.cutoff = "q90")
  274. ggarrange(vln_plot_MAP2, UMAP_plot, feature_plot_MAP2, ncol = 3, nrow = 1)
  275. ```
  276. ### Biomarker TOP2A
  277. ```{r fig.width=11, fig.height=4}
  278. vln_plot_TOP2A <- VlnPlot(seurat.integrated, features = "TOP2A", pt.size=0) + NoLegend()
  279. UMAP_plot <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  280. feature_plot_TOP2A <- FeaturePlot(seurat.integrated, features = c("TOP2A"), min.cutoff = "q10", max.cutoff = "q90")
  281. ggarrange(vln_plot_TOP2A, UMAP_plot, feature_plot_TOP2A, ncol = 3, nrow = 1)
  282. ```
  283. ### Biomarker CCNB2
  284. ```{r fig.width=11, fig.height=4}
  285. vln_plot_CCNB2 <- VlnPlot(seurat.integrated, features = "CCNB2", pt.size=0) + NoLegend()
  286. UMAP_plot_CCNB2 <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  287. feature_plot_CCNB2 <- FeaturePlot(seurat.integrated, features = c("CCNB2"), min.cutoff = "q10", max.cutoff = "q90")
  288. ggarrange(vln_plot_TBR2, UMAP_plot_TBR2, feature_plot_TBR2, ncol = 3, nrow = 1)
  289. ```
  290. ### Biomarker NEFL
  291. ```{r fig.width=11, fig.height=4}
  292. vln_plot_NEFL <- VlnPlot(seurat.integrated, features = "NEFL", pt.size=0) + NoLegend()
  293. UMAP_plot_NEFL <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  294. feature_plot_NEFL <- FeaturePlot(seurat.integrated, features = c("NEFL"), min.cutoff = "q10", max.cutoff = "q90")
  295. ggarrange(vln_plot_NEFL, UMAP_plot_NEFL, feature_plot_NEFL, ncol = 3, nrow = 1)
  296. ```
  297. ### Biomarker MEIS2
  298. ```{r fig.width=11, fig.height=4}
  299. vln_plot_MEIS2 <- VlnPlot(seurat.integrated, features = "MEIS2", pt.size=0) + NoLegend()
  300. UMAP_plot_MEIS2 <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  301. feature_plot_MEIS2 <- FeaturePlot(seurat.integrated, features = c("MEIS2"), min.cutoff = "q10", max.cutoff = "q90")
  302. ggarrange(vln_plot_MEIS2, UMAP_plot_MEIS2, feature_plot_MEIS2, ncol = 3, nrow = 1)
  303. ```
  304. ### Biomarker SOX2
  305. ```{r fig.width=11, fig.height=4}
  306. vln_plot_SOX2 <- VlnPlot(seurat.integrated, features = "SOX2", pt.size=0) + NoLegend()
  307. UMAP_plot_SOX2 <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  308. feature_plot_SOX2 <- FeaturePlot(seurat.integrated, features = c("SOX2"), min.cutoff = "q10", max.cutoff = "q90")
  309. ggarrange(vln_plot_SOX2, UMAP_plot_SOX2, feature_plot_SOX2, ncol = 3, nrow = 1)
  310. ```
  311. ### Biomarker PTN
  312. ```{r fig.width=11, fig.height=4}
  313. vln_plot_PTN <- VlnPlot(seurat.integrated, features = "PTN", pt.size=0) + NoLegend()
  314. UMAP_plot_PTN <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  315. feature_plot_PTN <- FeaturePlot(seurat.integrated, features = c("PTN"), min.cutoff = "q10", max.cutoff = "q90")
  316. ggarrange(vln_plot_PTN, UMAP_plot_PTN, feature_plot_PTN, ncol = 3, nrow = 1)
  317. ```
  318. ### Biomarker S100A10
  319. ```{r fig.width=11, fig.height=4}
  320. vln_plot_S100A10 <- VlnPlot(seurat.integrated, features = "S100A10", pt.size=0) + NoLegend()
  321. UMAP_plot_S100A10 <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  322. feature_plot_S100A10 <- FeaturePlot(seurat.integrated, features = c("S100A10"), min.cutoff = "q10", max.cutoff = "q90")
  323. ggarrange(vln_plot_S100A10, UMAP_plot_S100A10, feature_plot_S100A10, ncol = 3, nrow = 1)
  324. ```
  325. ### Biomarker COL1A1
  326. ```{r fig.width=11, fig.height=4}
  327. vln_plot_COL1A1 <- VlnPlot(seurat.integrated, features = "COL1A1", pt.size=0) + NoLegend()
  328. UMAP_plot_COL1A1 <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  329. feature_plot_COL1A1 <- FeaturePlot(seurat.integrated, features = c("COL1A1"), min.cutoff = "q10", max.cutoff = "q90")
  330. ggarrange(vln_plot_COL1A1, UMAP_plot_COL1A1, feature_plot_COL1A1, ncol = 3, nrow = 1)
  331. ```
  332. ### Biomarker COL1A2
  333. ```{r fig.width=11, fig.height=4}
  334. vln_plot_COL1A2 <- VlnPlot(seurat.integrated, features = "COL1A2", pt.size=0) + NoLegend()
  335. UMAP_plot_COL1A2 <- DimPlot(seurat.integrated, label=TRUE,repel=FALSE) + NoLegend()
  336. feature_plot_COL1A2 <- FeaturePlot(seurat.integrated, features = c("COL1A2"), min.cutoff = "q10", max.cutoff = "q90")
  337. ggarrange(vln_plot_COL1A2, UMAP_plot_COL1A2, feature_plot_COL1A2, ncol = 3, nrow = 1)
  338. ```
  339. ## Rename cluster names
  340. ```{r fig.width=4.5, fig.height=4.5}
  341. Idents(object = seurat.integrated) <- 'integrated_snn_res.0.4'
  342. new.cluster.ids <- c("Neuronal progenitor cells", "Neuronal progenitor cells", "Intermediate progenitor cells", "Neurons", "Cell debris", "Neuronal progenitor cells", "Midbrain-Hindbrain", "Radial glial cells", "Ventral progenitors", "Ventral progenitors", "Choroid plexus", "Astrocytes", "Neuronal progenitor cells")
  343. names(new.cluster.ids) <- levels(seurat.integrated)
  344. seurat.integrated <- RenameIdents(seurat.integrated, new.cluster.ids)
  345. DimPlot(seurat.integrated, reduction = "umap", label = TRUE, pt.size = 0.5, label.size = 5, repel = TRUE) + NoLegend()
  346. ```
  347. ## Bubble plot with biomarkers
  348. ```{r fig.width=7, fig.height=6}
  349. markers.to.plot <- c("SOX2", "TOP2A", "MAP2", "NEFL", "TLE4", "EPB41L4A-AS1", "TTR", "S100A10")
  350. DotPlot(seurat.integrated, features = markers.to.plot, cols = c("black", "brown", "yellow", "green"), dot.scale = 8, split.by = "Condition") +
  351. RotatedAxis()
  352. ```
  353. # Viral expression
  354. ## Between samples and conditions
  355. ```{r}
  356. seurat.integrated$Condition <- factor(seurat.integrated$Condition, levels = c("Mock", "Malaysia", "Puerto Rico", "Uganda"))
  357. # Create individual violin plots
  358. vln_plot_1 <- VlnPlot(seurat.integrated, features = "vir-ZVMAL", group.by = "Condition", pt.size=0.3, y.max=8) + NoLegend()
  359. vln_plot_2 <- VlnPlot(seurat.integrated, features = "vir-ZVPR", group.by = "Condition", pt.size=0.3, y.max=8) + NoLegend()
  360. vln_plot_3 <- VlnPlot(seurat.integrated, features = "vir-ZVUG", group.by = "Condition", pt.size=0.3, y.max=8) + NoLegend()
  361. # Arrange the plots together
  362. ggarrange(vln_plot_1, vln_plot_2, vln_plot_3, ncol = 3, nrow = 1)
  363. ```
  364. ## Per celltype
  365. ```{r fig.width=6, fig.height=12}
  366. # Subset the Seurat object to exclude the "Cell debris" cluster
  367. seurat_no_debris <- subset(seurat.integrated, idents = "Cell debris", invert = TRUE)
  368. # Create individual violin plots
  369. vln_plot_1 <- VlnPlot(seurat_no_debris, features = "vir-ZVMAL", split.by = "Condition", pt.size=0, y.max=8)
  370. vln_plot_2 <- VlnPlot(seurat_no_debris, features = "vir-ZVPR", split.by = "Condition", pt.size=0, y.max=8)
  371. vln_plot_3 <- VlnPlot(seurat_no_debris, features = "vir-ZVUG", split.by = "Condition", pt.size=0, y.max=8)
  372. # Arrange the plots together
  373. ggarrange(vln_plot_2, vln_plot_1, vln_plot_3, ncol = 1, nrow = 3)
  374. ```
  375. ## QC per celltype per infection
  376. ```{r fig.width=10, fig.height=15}
  377. # Create individual violin plots
  378. vln_plot_mt <- VlnPlot(seurat_no_debris, features=c("percent.mt"), split.by = "Condition", ncol=1, pt.size=0)
  379. vln_plot_feature <- VlnPlot(seurat_no_debris, features=c("l2.n_feature"), split.by = "Condition", ncol=1, pt.size=0)
  380. vln_plot_count <- VlnPlot(seurat_no_debris, features=c("l2.n_count"), split.by = "Condition", ncol=1, pt.size=0)
  381. # Arrange the plots together
  382. combined_QCplot <- ggarrange(
  383. vln_plot_mt + theme(plot.margin = margin(5.5, 5.5, 5.5, 60, "pt")),
  384. vln_plot_feature + theme(plot.margin = margin(5.5, 5.5, 5.5, 60, "pt")),
  385. vln_plot_count + theme(plot.margin = margin(5.5, 5.5, 5.5, 60, "pt")),
  386. ncol = 1, nrow = 3
  387. )
  388. ggsave("combined_QCplot.pdf", plot = combined_QCplot, width = 10, height = 15)
  389. ```
  390. ## Number of cells per celltype per condition
  391. ```{r}
  392. # Create the table
  393. cell_counts <- table(Idents(seurat_no_debris), seurat_no_debris$Condition)
  394. # Convert the table to a data frame for exporting
  395. cell_counts_df <- as.data.frame.matrix(cell_counts)
  396. # Export to Excel
  397. write.xlsx(cell_counts_df, file = "cell_counts_clusters_conditions.xlsx", rowNames = TRUE)
  398. ```
  399. ## Number of infected and non infected cells
  400. ```{r}
  401. DefaultAssay(seurat_no_debris)<-"RNA"
  402. # Specify the genes of interest
  403. genes_of_interest <- c("vir-ZVPR", "vir-ZVMAL", "vir-ZVUG")
  404. # Extract expression data for the genes of interest
  405. expression_data <- FetchData(seurat_no_debris, vars = genes_of_interest)
  406. # Identify cells expressing each gene
  407. expressing_cells <- expression_data > 0
  408. # Identify cells that do not express any of the genes
  409. non_expressing_cells <- rowSums(expressing_cells) == 0
  410. # Get cell types from the active ident
  411. cell_types <- [email hidden]
  412. # Get conditions from the metadata
  413. conditions <- seurat_no_debris$Condition
  414. # Create a dataframe with cell types, conditions, and expression status
  415. expression_per_celltype_condition <- data.frame(cell_type = cell_types,
  416. condition = conditions,
  417. expressing_cells,
  418. none_expressed = non_expressing_cells)
  419. # Count the number of cells expressing each gene and not expressing any gene per cell type and condition
  420. counts_per_celltype_condition <- aggregate(. ~ cell_type + condition,
  421. data = expression_per_celltype_condition,
  422. FUN = sum)
  423. # View the results
  424. print(counts_per_celltype_condition)
  425. # Specify the output file name
  426. output_file <- "CellType_Condition_ViralExpression.xlsx"
  427. # Export the result to an Excel file
  428. write.xlsx(counts_per_celltype_condition, file = output_file, rowNames = FALSE)
  429. ```
  430. # Bubble plot interferon genes
  431. ```{r fig.width=7, fig.height=6}
  432. interferons.to.plot.bulk <- c("IFIT2", "ISG15", "IFIT3", "STAT1", "IFIT5", "IFIT1", "IFI27", "HLA-B", "IFI6", "EIF2AK2")
  433. # Create the DotPlot
  434. DotPlot(seurat.integrated, features = interferons.to.plot.bulk,
  435. cols = c("black", "brown", "yellow", "green"),
  436. dot.scale = 8, split.by = "Condition", scale.by = "size") +
  437. RotatedAxis()
  438. interferonDotPlot <- DotPlot(seurat.integrated, features = interferons.to.plot.bulk,
  439. cols = c("black", "brown", "yellow", "green"),
  440. dot.scale = 8, split.by = "Condition", scale.by = "size") +
  441. RotatedAxis()
  442. # Save the plot as a PDF
  443. ggsave(filename = "interferonDotPlot.pdf", plot = interferonDotPlot, device = "pdf", width = 10, height = 12)
  444. ```
  445. # Differentially Expressed Genes per cluster virus vs mock
  446. ```{r}
  447. seurat.integrated$Condition <- factor(seurat.integrated$Condition, levels = c("Mock", "Malaysia", "Puerto Rico", "Uganda"))
  448. seurat.integrated$ClusterNames <- [email hidden]
  449. seurat.integrated$cluster.condition <- paste(seurat.integrated$ClusterNames, seurat.integrated$Condition, sep = "_")
  450. Idents(seurat.integrated) <- "cluster.condition"
  451. table(seurat.integrated$cluster.condition)
  452. # Create a data frame from the table output
  453. df <- as.data.frame(table(seurat.integrated$cluster.condition))
  454. # Split the cluster.condition into separate columns for cluster and condition
  455. df <- tidyr::separate(df, Var1, into = c("Cluster", "Condition"), sep = "_")
  456. # Create the bar plot
  457. ggplot(df, aes(x = Cluster, y = Freq, fill = Condition)) +
  458. geom_bar(stat = "identity", position = "dodge") +
  459. labs(title = "Number of cells per cluster per condition",
  460. x = "Cluster",
  461. y = "Count",
  462. fill = "Condition") +
  463. theme_minimal()+
  464. theme(axis.text.x = element_text(angle = 45, hjust = 1))
  465. ```
  466. ```{r}
  467. # Initialize a list to store the results
  468. de_results_PR <- list()
  469. de_results_Mal <- list()
  470. de_results_Ug <- list()
  471. de_results_MalPR <- list()
  472. de_results_UgPR <- list()
  473. de_results_MalUg <- list()
  474. # Define the clusters and conditions
  475. clusters <- levels(seurat.integrated$ClusterNames)
  476. PRconditions <- c("Puerto Rico", "Mock")
  477. Malconditions <- c("Malaysia", "Mock")
  478. Ugconditions <- c("Uganda", "Mock")
  479. MalPRconditions <- c("Malaysia", "Puerto Rico")
  480. UgPRconditions <- c("Uganda", "Puerto Rico")
  481. MalUgconditions <- c("Malaysia", "Uganda")
  482. # Loop through each cluster and perform differential expression analysis for PR vs mock
  483. for (cluster in clusters) {
  484. ident.1 <- paste(cluster, PRconditions[1], sep = "_")
  485. ident.2 <- paste(cluster, PRconditions[2], sep = "_")
  486. if (ident.1 %in% Idents(seurat.integrated) & ident.2 %in% Idents(seurat.integrated)) {
  487. de_results_PR[[paste("Cluster", cluster)]] <- FindMarkers(seurat.integrated, ident.1 = ident.1, ident.2 = ident.2, verbose = FALSE, logfc.threshold = 0)
  488. } else {
  489. message(paste("Cluster", cluster, "does not have both conditions."))
  490. }
  491. }
  492. # Loop through each cluster and perform differential expression analysis for Mal vs mock
  493. for (cluster in clusters) {
  494. ident.1 <- paste(cluster, Malconditions[1], sep = "_")
  495. ident.2 <- paste(cluster, Malconditions[2], sep = "_")
  496. if (ident.1 %in% Idents(seurat.integrated) & ident.2 %in% Idents(seurat.integrated)) {
  497. de_results_Mal[[paste("Cluster", cluster)]] <- FindMarkers(seurat.integrated, ident.1 = ident.1, ident.2 = ident.2, verbose = FALSE, logfc.threshold = 0)
  498. } else {
  499. message(paste("Cluster", cluster, "does not have both conditions."))
  500. }
  501. }
  502. # Loop through each cluster and perform differential expression analysis for Ug vs mock
  503. for (cluster in clusters) {
  504. ident.1 <- paste(cluster, Ugconditions[1], sep = "_")
  505. ident.2 <- paste(cluster, Ugconditions[2], sep = "_")
  506. if (ident.1 %in% Idents(seurat.integrated) & ident.2 %in% Idents(seurat.integrated)) {
  507. de_results_Ug[[paste("Cluster", cluster)]] <- FindMarkers(seurat.integrated, ident.1 = ident.1, ident.2 = ident.2, verbose = FALSE, logfc.threshold = 0)
  508. } else {
  509. message(paste("Cluster", cluster, "does not have both conditions."))
  510. }
  511. }
  512. # Loop through each cluster and perform differential expression analysis for Mal vs PR
  513. for (cluster in clusters) {
  514. ident.1 <- paste(cluster, MalPRconditions[1], sep = "_")
  515. ident.2 <- paste(cluster, MalPRconditions[2], sep = "_")
  516. if (ident.1 %in% Idents(seurat.integrated) & ident.2 %in% Idents(seurat.integrated)) {
  517. de_results_MalPR[[paste("Cluster", cluster)]] <- FindMarkers(seurat.integrated, ident.1 = ident.1, ident.2 = ident.2, verbose = FALSE, logfc.threshold = 0)
  518. } else {
  519. message(paste("Cluster", cluster, "does not have both conditions."))
  520. }
  521. }
  522. # Loop through each cluster and perform differential expression analysis for Ug vs PR
  523. for (cluster in clusters) {
  524. ident.1 <- paste(cluster, UgPRconditions[1], sep = "_")
  525. ident.2 <- paste(cluster, UgPRconditions[2], sep = "_")
  526. if (ident.1 %in% Idents(seurat.integrated) & ident.2 %in% Idents(seurat.integrated)) {
  527. de_results_UgPR[[paste("Cluster", cluster)]] <- FindMarkers(seurat.integrated, ident.1 = ident.1, ident.2 = ident.2, verbose = FALSE, logfc.threshold = 0)
  528. } else {
  529. message(paste("Cluster", cluster, "does not have both conditions."))
  530. }
  531. }
  532. # Loop through each cluster and perform differential expression analysis for Mal vs Ug
  533. for (cluster in clusters) {
  534. ident.1 <- paste(cluster, MalUgconditions[1], sep = "_")
  535. ident.2 <- paste(cluster, MalUgconditions[2], sep = "_")
  536. if (ident.1 %in% Idents(seurat.integrated) & ident.2 %in% Idents(seurat.integrated)) {
  537. de_results_MalUg[[paste("Cluster", cluster)]] <- FindMarkers(seurat.integrated, ident.1 = ident.1, ident.2 = ident.2, verbose = FALSE, logfc.threshold = 0)
  538. } else {
  539. message(paste("Cluster", cluster, "does not have both conditions."))
  540. }
  541. }
  542. ```
  543. # GSEA - Functional Class Sorting
  544. Preparing rank files
  545. Extract protein coding genes from assembled data files, adjust the data and write rnk file in gsea directory
  546. ## Rank files for GSEA
  547. Ranked gene lists (rnk files) were generated from differential expression results using avg_log2FC values. The process involved iterating through each cluster of differential expression results for the conditions Mal, PR, and Ug, extracting the avg_log2FC values, and renaming the columns to reflect the specific condition and cluster (e.g., Ug_v_mock.Neuronal_progenitor_cells.rnk, PR_v_mock.Neurons.rnk, etc.). These avg_log2FC values were stored in individual data frames for each celltype, which were subsequently merged into a single data frame for each condition.
  548. For each merged data frame, the gene name and corresponding avg_log2FC values were selected and saved as rnk files, ensuring only complete cases were included. These rnk files were then used for Gene Set Enrichment Analysis (GSEA) utilizing the prerank_4.3.2_v2023.1.sh script to identify enriched gene sets within the collection Gene Ontology Biological Process (c5bp).
  549. ```{r}
  550. # Initialize an empty list to store avg_log2FC dataframes
  551. avg_log2FC_list_Mal <- list()
  552. avg_log2FC_list_PR <- list()
  553. avg_log2FC_list_Ug <- list()
  554. avg_log2FC_list_MalPR <- list()
  555. avg_log2FC_list_UgPR <- list()
  556. avg_log2FC_list_MalUg <- list()
  557. # Loop through the differential expression results and extract avg_log2FC
  558. for (cluster in names(de_results_Mal)) {
  559. cluster_name <- sub("Cluster ", "", cluster) # Remove "Cluster " prefix to get the cluster name
  560. column_name <- paste0("mal_v_mock.", cluster_name) # Create the new column name
  561. avg_log2FC_df <- data.frame(gene_name = rownames(de_results_Mal[[cluster]]),
  562. avg_log2FC = de_results_Mal[[cluster]]$avg_log2FC)
  563. colnames(avg_log2FC_df)[2] <- column_name # Rename the avg_log2FC column to the new name
  564. avg_log2FC_list_Mal[[cluster_name]] <- avg_log2FC_df # Use the cluster name without "Cluster " prefix
  565. }
  566. for (cluster in names(de_results_PR)) {
  567. cluster_name <- sub("Cluster ", "", cluster) # Remove "Cluster " prefix to get the cluster name
  568. column_name <- paste0("PR_v_mock.", cluster_name) # Create the new column name
  569. avg_log2FC_df <- data.frame(gene_name = rownames(de_results_PR[[cluster]]),
  570. avg_log2FC = de_results_PR[[cluster]]$avg_log2FC)
  571. colnames(avg_log2FC_df)[2] <- column_name # Rename the avg_log2FC column to the new name
  572. avg_log2FC_list_PR[[cluster_name]] <- avg_log2FC_df # Use the cluster name without "Cluster " prefix
  573. }
  574. for (cluster in names(de_results_Ug)) {
  575. cluster_name <- sub("Cluster ", "", cluster) # Remove "Cluster " prefix to get the cluster name
  576. column_name <- paste0("Ug_v_mock.", cluster_name) # Create the new column name
  577. avg_log2FC_df <- data.frame(gene_name = rownames(de_results_Ug[[cluster]]),
  578. avg_log2FC = de_results_Ug[[cluster]]$avg_log2FC)
  579. colnames(avg_log2FC_df)[2] <- column_name # Rename the avg_log2FC column to the new name
  580. avg_log2FC_list_Ug[[cluster_name]] <- avg_log2FC_df # Use the cluster name without "Cluster " prefix
  581. }
  582. for (cluster in names(de_results_MalPR)) {
  583. cluster_name <- sub("Cluster ", "", cluster) # Remove "Cluster " prefix to get the cluster name
  584. column_name <- paste0("Mal_v_PR.", cluster_name) # Create the new column name
  585. avg_log2FC_df <- data.frame(gene_name = rownames(de_results_MalPR[[cluster]]),
  586. avg_log2FC = de_results_MalPR[[cluster]]$avg_log2FC)
  587. colnames(avg_log2FC_df)[2] <- column_name # Rename the avg_log2FC column to the new name
  588. avg_log2FC_list_MalPR[[cluster_name]] <- avg_log2FC_df # Use the cluster name without "Cluster " prefix
  589. }
  590. for (cluster in names(de_results_UgPR)) {
  591. cluster_name <- sub("Cluster ", "", cluster) # Remove "Cluster " prefix to get the cluster name
  592. column_name <- paste0("Ug_v_PR.", cluster_name) # Create the new column name
  593. avg_log2FC_df <- data.frame(gene_name = rownames(de_results_UgPR[[cluster]]),
  594. avg_log2FC = de_results_UgPR[[cluster]]$avg_log2FC)
  595. colnames(avg_log2FC_df)[2] <- column_name # Rename the avg_log2FC column to the new name
  596. avg_log2FC_list_UgPR[[cluster_name]] <- avg_log2FC_df # Use the cluster name without "Cluster " prefix
  597. }
  598. for (cluster in names(de_results_MalUg)) {
  599. cluster_name <- sub("Cluster ", "", cluster) # Remove "Cluster " prefix to get the cluster name
  600. column_name <- paste0("Mal_v_Ug.", cluster_name) # Create the new column name
  601. avg_log2FC_df <- data.frame(gene_name = rownames(de_results_MalUg[[cluster]]),
  602. avg_log2FC = de_results_MalUg[[cluster]]$avg_log2FC)
  603. colnames(avg_log2FC_df)[2] <- column_name # Rename the avg_log2FC column to the new name
  604. avg_log2FC_list_MalUg[[cluster_name]] <- avg_log2FC_df # Use the cluster name without "Cluster " prefix
  605. }
  606. # Merge all dataframes in the list into one dataframe
  607. merged_df_Mal <- Reduce(function(x, y) merge(x, y, by = "gene_name", all = TRUE), avg_log2FC_list_Mal)
  608. merged_df_PR <- Reduce(function(x, y) merge(x, y, by = "gene_name", all = TRUE), avg_log2FC_list_PR)
  609. merged_df_Ug <- Reduce(function(x, y) merge(x, y, by = "gene_name", all = TRUE), avg_log2FC_list_Ug)
  610. merged_df_MalPR <- Reduce(function(x, y) merge(x, y, by = "gene_name", all = TRUE), avg_log2FC_list_MalPR)
  611. merged_df_UgPR <- Reduce(function(x, y) merge(x, y, by = "gene_name", all = TRUE), avg_log2FC_list_UgPR)
  612. merged_df_MalUg <- Reduce(function(x, y) merge(x, y, by = "gene_name", all = TRUE), avg_log2FC_list_MalUg)
  613. # View the final merged dataframe
  614. head(merged_df_Mal)
  615. head(merged_df_PR)
  616. head(merged_df_Ug)
  617. names <- colnames(merged_df_Mal)
  618. for (i in names[-1]){
  619. d<-merged_df_Mal %>% dplyr::select('gene_name',all_of(i))
  620. names(d)[1] <- '#Gene'
  621. d<-d[complete.cases(d), ]
  622. write.table(d, sep='\t',file=paste0(i,".rnk"),col.names=TRUE, quote=FALSE, row.names=FALSE)
  623. }
  624. names <- colnames(merged_df_PR)
  625. for (i in names[-1]){
  626. d<-merged_df_PR %>% dplyr::select('gene_name',all_of(i))
  627. names(d)[1] <- '#Gene'
  628. d<-d[complete.cases(d), ]
  629. write.table(d, sep='\t',file=paste0(i,".rnk"),col.names=TRUE, quote=FALSE, row.names=FALSE)
  630. }
  631. names <- colnames(merged_df_Ug)
  632. for (i in names[-1]){
  633. d<-merged_df_Ug %>% dplyr::select('gene_name',all_of(i))
  634. names(d)[1] <- '#Gene'
  635. d<-d[complete.cases(d), ]
  636. write.table(d, sep='\t',file=paste0(i,".rnk"),col.names=TRUE, quote=FALSE, row.names=FALSE)
  637. }
  638. names <- colnames(merged_df_MalPR)
  639. for (i in names[-1]){
  640. d<-merged_df_MalPR %>% dplyr::select('gene_name',all_of(i))
  641. names(d)[1] <- '#Gene'
  642. d<-d[complete.cases(d), ]
  643. write.table(d, sep='\t',file=paste0(i,".rnk"),col.names=TRUE, quote=FALSE, row.names=FALSE)
  644. }
  645. names <- colnames(merged_df_UgPR)
  646. for (i in names[-1]){
  647. d<-merged_df_UgPR %>% dplyr::select('gene_name',all_of(i))
  648. names(d)[1] <- '#Gene'
  649. d<-d[complete.cases(d), ]
  650. write.table(d, sep='\t',file=paste0(i,".rnk"),col.names=TRUE, quote=FALSE, row.names=FALSE)
  651. }
  652. names <- colnames(merged_df_MalUg)
  653. for (i in names[-1]){
  654. d<-merged_df_MalUg %>% dplyr::select('gene_name',all_of(i))
  655. names(d)[1] <- '#Gene'
  656. d<-d[complete.cases(d), ]
  657. write.table(d, sep='\t',file=paste0(i,".rnk"),col.names=TRUE, quote=FALSE, row.names=FALSE)
  658. }
  659. ```
  660. ## Process luria tsv GSEA results to produce a single output file using submit_prerank_4.3.2_v2023.2_caw.sh bash script
  661. ## Identify files that need to be imported - the basedir will change according to run
  662. After running the javaGSEA version 4.3.2 bash script with msigDb version 2023.2 (Subramanian et al. 2005), the resulting data files were processed to create a comprehensive and analyzable dataset. The initial step involved identifying and importing the relevant GSEA result files. These files were located in the specified base directory and its subdirectories, matching a specific naming pattern (e.g., "^gsea_.*\.tsv$").
  663. ```{r}
  664. base_dir <- "gsea_celltypes/aug01/"
  665. # Get a list of directories in the base directory
  666. sub_dirs <- list.dirs(base_dir, recursive = FALSE)
  667. # Initialize an empty list to store file paths
  668. file_list <- list()
  669. # Loop through each sub-directory
  670. for (sub_dir in sub_dirs) {
  671. # Get a list of files in the sub-directory that match the pattern
  672. files <- list.files(path = sub_dir, pattern = "^gsea_.*\\.tsv$", full.names = TRUE)
  673. # Add the files to the file list
  674. file_list <- c(file_list, files)
  675. }
  676. ```
  677. ## Import the GSEA data into a list of lists
  678. ```{r}
  679. gsea_results.list <- lapply(file_list, read.delim)
  680. ```
  681. ## Extract relevant results names from output filenames and rename results dataframes
  682. To facilitate the analysis, the filenames were processed to extract meaningful names, indicating the specific comparison and whether the results were positive or negative. These names were used to label the corresponding data frames in the list of GSEA results.
  683. ```{r}
  684. # Define a function to process each file path
  685. process_filepath <- function(filepath) {
  686. # Remove folder info from the front of the file path
  687. short_filename <- gsub("gsea_celltypes/aug01//", "", filepath)
  688. # Replace ".rnk." with "_"
  689. short_filename <- gsub("\\.rnk\\.", "_", short_filename)
  690. # Add "_pos" if "pos" is in the remainder or "_neg" if "neg" in the remainder
  691. if (grepl("pos", short_filename)) {
  692. short_filename <- paste0(short_filename, "_pos")
  693. } else if (grepl("neg", short_filename)) {
  694. short_filename <- paste0(short_filename, "_neg")
  695. }
  696. short_filename <- gsub("\\.GseaP.*_pos$", "_pos", short_filename)
  697. short_filename <- gsub("\\.GseaP.*_neg$", "_neg", short_filename)
  698. return(short_filename)
  699. }
  700. # Apply the function to each file path in file_list
  701. short_filenames <- lapply(file_list, process_filepath)
  702. # Rename the list of gsea results
  703. names(gsea_results.list) <- short_filenames
  704. ```
  705. ## Extract relevant columns from the dataframes
  706. Next, each data frame in the list was refined to include only the relevant columns: "NAME" (gene set name), "SIZE" (number of genes in the set), "NES" (normalized enrichment score), and "FDR.q.val" (false discovery rate q-value).
  707. ```{r}
  708. # Define the columns to select
  709. columns_to_select <- c("NAME", "SIZE", "NES", "FDR.q.val")
  710. # Loop through the list of dataframes
  711. gsea_results.list <- lapply(gsea_results.list, function(df) {
  712. # Select the desired columns using dplyr::select
  713. df_selected <- dplyr::select(df, all_of(columns_to_select))
  714. # Return the modified dataframe
  715. return(df_selected)
  716. })
  717. ```
  718. ## Add a comparison column containing the name of each results list
  719. Additionally, a new column was added to each data frame to indicate the specific comparison it represents, simplifying downstream analysis.
  720. ```{r}
  721. # Loop through the list of dataframes
  722. for (comparison_name in names(gsea_results.list)) {
  723. new_name <- gsub("_pos$|_neg$", "", comparison_name)
  724. # Access the dataframe by its name
  725. df <- gsea_results.list[[comparison_name]]
  726. # Add a new column containing the comparison name
  727. df <- df %>% mutate(Comparison = new_name)
  728. # Update the dataframe in the list
  729. gsea_results.list[[comparison_name]] <- df
  730. }
  731. ```
  732. ```{r}
  733. gsea_results.list <- lapply(gsea_results.list, function(x) {
  734. x$NES <- as.numeric(x$NES)
  735. return(x)
  736. })
  737. ```
  738. ## Assemble the data into a single output file and export the results
  739. The GSEA results were then combined into a single data frame. This combined data frame included an additional column "Collection," derived from the comparison names, which identified the gene set collections (e.g., c2cp, h, c5bp).
  740. ```{r}
  741. combined_df <- bind_rows(gsea_results.list, .id = "ComparisonFull")
  742. #Use mutate and str_extract to create the new 'Collection' column
  743. combined_df <- combined_df %>%
  744. mutate(Collection = str_extract(Comparison, "[^_]+$")) %>%
  745. mutate(Comparison = str_remove(Comparison, "_c2cp|_c5bp|_h"))
  746. ```
  747. ## Pivoting dataframe
  748. To ensure unique entries and appropriate aggregation, the maximum "SIZE" value for each gene set name was determined. The data was then summarized to avoid duplicates, averaging the NES and FDR.q.val values for each gene set and comparison. This summarized data was pivoted to create a wide-format table, where NES and FDR.q.val values for different comparisons were represented as separate columns. The final data frame was enhanced by including the maximum "SIZE" values and sorting by the minimum FDR.q.val across all comparisons.
  749. ```{r}
  750. # Get the maximum SIZE value for each NAME
  751. df_size_max <- combined_df %>%
  752. group_by(NAME) %>%
  753. summarize(SIZE = max(SIZE, na.rm = TRUE), .groups = 'drop')
  754. # Ensure no duplicate entries for the same NAME and Comparison
  755. df_summary <- df %>%
  756. group_by(NAME, Comparison, Collection) %>%
  757. summarize(NES = mean(NES, na.rm = TRUE), FDR.q.val = mean(FDR.q.val, na.rm = TRUE), .groups = 'drop')
  758. # Pivot the dataframe to get NES and FDR.q.val with correct column names
  759. pivoted_df <- df_summary %>%
  760. pivot_wider(names_from = Comparison,
  761. values_from = c(NES, FDR.q.val),
  762. names_sep = ".") %>%
  763. rename_with(~ sub("^(.+?)\\.NES$", "\\1.NES", .), starts_with("NES")) %>%
  764. rename_with(~ sub("^(.+?)\\.FDR.q.val$", "\\1.FDR.q.val", .), starts_with("FDR.q.val"))
  765. # Combine the SIZE column with the pivoted dataframe
  766. final_df <- pivoted_df %>%
  767. left_join(df_size_max, by = "NAME") %>%
  768. select(NAME, SIZE, Collection, everything()) # Ensure Collection is included and in the right order
  769. # Identify FDR.q.val columns
  770. fdr_columns <- grep("^FDR.q.val", names(final_df), value = TRUE)
  771. # Create MinFDR column with the minimum value of all FDR.q.val columns
  772. final_df <- final_df %>%
  773. rowwise() %>%
  774. mutate(MinFDR = min(c_across(all_of(fdr_columns)), na.rm = TRUE)) %>%
  775. ungroup()
  776. # Sort final_df by MinFDR with smallest values at the top
  777. final_df <- final_df %>%
  778. arrange(MinFDR)
  779. head(final_df)
  780. write.xlsx(final_df, "GSEA_virus_v_mock_celltypes_c2cp_h_c5bp.xlsx")
  781. ```
  782. # Volcano plot
  783. ```{r fig.width=7, fig.height=7}
  784. library(EnhancedVolcano)
  785. # Extract the DEG table for "Cluster Neuronal progenitor cells"
  786. deg_table <- de_results_Ug[["Cluster Neuronal progenitor cells"]]
  787. # Define the genes to label
  788. genes_to_label <- c("CANX", "PDIA4", "HSP90B1", "SELENOS", "HERPUD1", "HSPA5", "SEC61B", "XBP1", "GRINA", "SEC63", "MIF", "CLU")
  789. # Filter for significant genes in the list to label
  790. genes_to_label_significant <- intersect(genes_to_label, rownames(deg_table)[deg_table$p_val_adj < 0.05])
  791. # Define custom colors
  792. keyvals <- rep('grey', nrow(deg_table))
  793. names(keyvals) <- rownames(deg_table)
  794. keyvals[deg_table$avg_log2FC > 0.2 & deg_table$p_val_adj < 0.05] <- 'red'
  795. keyvals[deg_table$avg_log2FC < -0.2 & deg_table$p_val_adj < 0.05] <- 'blue'
  796. # Create a volcano plot with custom colors
  797. EnhancedVolcano(
  798. deg_table,
  799. lab = rownames(deg_table),
  800. x = 'avg_log2FC',
  801. y = 'p_val_adj',
  802. xlim = c(-0.75, 0.75),
  803. selectLab = genes_to_label_significant,
  804. xlab = bquote(~Log[2]~ 'fold change'),
  805. ylab = bquote(~-Log[10]~ 'p-value adjusted'),
  806. title = "Uganda VS Mock Neuronal progenitor cells",
  807. subtitle = bquote(italic(ER_STRESS_RESPONSE)),
  808. pCutoff = 0.05,
  809. FCcutoff = 0.20,
  810. pointSize = 2.0,
  811. labSize = 4.0,
  812. colCustom = keyvals,
  813. legendPosition = 'right',
  814. legendLabSize = 10,
  815. legendIconSize = 3.0,
  816. drawConnectors = TRUE,
  817. widthConnectors = 0.5,
  818. colConnectors = 'black',
  819. gridlines.major = FALSE,
  820. gridlines.minor = FALSE,
  821. border = 'full',
  822. borderWidth = 1.0,
  823. borderColour = 'black',
  824. max.overlaps = Inf
  825. )+
  826. theme(legend.position="none")
  827. ```
  828. # Heatmap of specific gene sets from GSEA
  829. ```{r}
  830. library(ComplexHeatmap)
  831. library(circlize)
  832. # Convert tbl_df to data.frame
  833. df <- as.data.frame(GSEA_LeeGeneSets_forheatmap)
  834. # Set the first column as row names
  835. rownames(df) <- df[[1]]
  836. # Remove the first column from the dataframe
  837. df <- df[ , -1]
  838. # Filter rows where MinFDR is under 0.05
  839. df_filtered <- df[df$MinFDR < 0.05, ]
  840. df_filtered <- subset(df, select = -c(MinFDR))
  841. df_filtered[is.na(df_filtered)] <- 0
  842. # Split dataframe into groups of 3 columns
  843. column_groups <- split(names(df), ceiling(seq_along(names(df)) / 3))
  844. # Convert the groups into a list of matrices
  845. list_of_matrices <- lapply(column_groups, function(cols) {
  846. mat <- as.matrix(df[, cols])
  847. rownames(mat) <- rownames(df)
  848. mat
  849. })
  850. # Define a function to create heatmaps
  851. create_heatmap <- function(mat) {
  852. Heatmap(mat, name = "NES", cluster_rows = FALSE, cluster_columns = FALSE, show_row_names = TRUE, row_names_side = c("left"), row_names_gp = gpar(fontsize = 4), column_names_gp = gpar(fontsize = 4))
  853. }
  854. # Create heatmaps for each group
  855. heatmaps <- lapply(list_of_matrices, create_heatmap)
  856. # Combine heatmaps side by side using the + operator
  857. combined_heatmap <- heatmaps[[1]]
  858. for (i in 2:length(heatmaps)) {
  859. combined_heatmap <- combined_heatmap + heatmaps[[i]]
  860. }
  861. # Draw the combined heatmap
  862. draw(combined_heatmap, merge_legends = TRUE)
  863. # # Save as PDF
  864. # pdf("combined_heatmap.pdf", width = 10, height = 6) # Adjust width and height as needed
  865. # draw(combined_heatmap, merge_legends = TRUE)
  866. # dev.off()
  867. ```
  868. <<<<<<< HEAD
  869. # write session info
  870. Capturing information about the R session helps document your work
  871. ```{r}
  872. sessionInfo()
  873. writeLines(capture.output(sessionInfo()), "220222Geh_sessionInfo.txt")
  874. ```

020222_Gehrke.Rmd at commit c6e52b0, under MIT · at the source

Overview

Authors: Alfred T. Harding1, Yichen Zhang1, Jane-Jane Chen1, Jenna M. Antonucci1, Alexsia Richards2, Valerie Leger1, Charles A. Whittaker3, Yann S. Vanrobaeys3, Divyansh Agarwal4, Tenzin Lungjangwa2, Rudolf Jaenisch1,2, Lee Gehrke1,5
  1. Massachusetts Institute of Technology, Cambridge, Massachusetts, USA
  2. Whitehead Institute for Biomedical Research, Cambridge, Massachusetts, USA
  3. Bioinformatics & Computing Core Facility of the Swanson Biotechnology Center, Koch Institute for Integrative Cancer Research, Massachusetts Institute of Technology, Cambridge, Massachusetts, USA
  4. Massachusetts General Hospital, Boston, Massachusetts, USA
  5. Harvard Medical School, Boston, Massachusetts, USA
Journal: mBio, volume 17, issue 7, pages e00863-26
Dates: received 10 April 2026; accepted 26 May 2026; published online 15 June 2026; in print July 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1128/mbio.00863-26 · PMID 42294680 · PMCID PMC13343971 · OpenAlex W7164856986
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), other condition (population)
Methods: Statistics, Connectivity
Keywords: virus, organoid, pathogenesis, Zika, flavivirus, stress response, tolerance
MeSH: Brain*, Organoids*, Zika Virus*, Zika Virus Infection*, Apoptosis, Host-Pathogen Interactions, Humans, Neural Stem Cells, Oxidative Stress, Reactive Oxygen Species (* major topic)
Topic: Mosquito-borne diseases and control (Public Health, Environmental and Occupational Health, Medicine), according to OpenAlex
Citations: not cited yet (Europe PMC); 87 references in the paper

Abstract

Zika virus (ZIKV) has multiple lineages and strains that cause a range of disease severity, underscoring the need to elucidate differential neuropathogenesis mechanisms. Here, we performed systematic, side-by-side comparisons of African, Asian, and American ZIKV lineage infections using cerebral organoids derived from human embryonic stem cells, a relevant human model experimental system. African lineage ZIKV strains, as well as the ancestral Asian Malaysia strain, persistently infected neural progenitor cells, causing apoptosis and severe disruption of ventricular cytoarchitecture. In contrast, contemporary Asian and American lineage viruses were cleared from ventricles, coinciding with low apoptosis and reduced neuropathology. Single-cell RNA sequencing demonstrated upregulated cell-type-specific antiviral signaling during American lineage infections, coinciding with viral clearance from ventricular progenitor cells. Conversely, pathogenic African lineage infections were associated with apoptosis, reduced STAT2 and IFIT1 protein levels, and enhanced activation of stress pathways. African lineage and ancestral Malaysian strain infections induced mitochondrial oxidative stress. Scavenging the reactive oxygen species improved ventricular cytoarchitecture and progenitor survival, but without reducing viral titers. Together, these findings suggest that lineage- and strain-specific host stress responses, rather than viral burden alone, contribute to ZIKV-induced neurodevelopmental damage. An implication of this study is that host-directed therapeutic strategies, used to improve host tolerance to viral infection, may benefit clinical outcomes.

IMPORTANCE: This study provides a systematic comparison of Zika virus (ZIKV) lineage infections in a relevant human model system to correlate mechanisms of host cellular responses with neuropathogenesis. By analyzing African, Asian, and American ZIKV infections side by side in cerebral organoids derived from human embryonic stem cells, we link persistent infection of neural progenitor cells to elevated cellular stress responses and structural disruption of organoid ventricles. Structural disruption was reduced by adding a hydroxyl radical scavenger, but without lowering viral titer. These data are important in strongly suggesting that host responses to viral infection can be of equal or greater importance than viral burden in determining pathogenesis. This study provides mechanistic insight into how closely related viral lineage infections result in divergent outcomes in the developing brain. The data have added importance for suggesting the potential value of host-directed therapeutics to improve ZIKV tolerance without addressing viral titer.

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 4 matches between paragraphs and lines of code.

KochInstitute-Bioinformatics/Gehrke_d7_infection

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: c6e52b0ceece280e5e84e9dc544ee3d855cb5537, 18 February 2026
Languages: R (9), Shell (6)
Size: 238 files, 15 scripts
Software Heritage: not archived
Found in: “DATA AVAILABILITY”
Holds: README, license file, 2 notebooks
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: DESeq2 (3 files), tidyverse (3 files), ComplexHeatmap (2 files), edgeR (2 files), circlize (1 file), ggplot2 (1 file), ggpubr (1 file), Nextflow (1 file), Seurat (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
17 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;
  • 15 scripts, each with its path and the digest of its content;
  • 4 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

The GitHub repository for the code used for bulk and single-cell RNAseq analyses can be found at https://github.com/KochInstitute-Bioinformatics/Gehrke_d7_infection.git. Bulk and single-cell RNA-Seq data are available from the Gene Expression Omnibus under accession number GSE29779 (https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE297797).

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, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 12 authors, 7 keywords, 10 MeSH terms, 2 funders, 83 references.

Cite

This paper

Harding, A. T., Zhang, Y., Chen, J.-J., Antonucci, J. M., Richards, A., Leger, V., Whittaker, C. A., Vanrobaeys, Y. S., Agarwal, D., Lungjangwa, T., Jaenisch, R., & Gehrke, L. (2026). Zika virus infections of human stem cell-derived cerebral organoids reveal viral lineage-specific pathogenesis responses. mBio, 17(7), e00863-26. https://doi.org/10.1128/mbio.00863-26

BibTeX

@article{harding2026zika,
author = {Harding, Alfred T. and Zhang, Yichen and Chen, Jane-Jane and Antonucci, Jenna M. and Richards, Alexsia and Leger, Valerie and Whittaker, Charles A. and Vanrobaeys, Yann S. and Agarwal, Divyansh and Lungjangwa, Tenzin and Jaenisch, Rudolf and Gehrke, Lee},
title = {{Zika virus infections of human stem cell-derived cerebral organoids reveal viral lineage-specific pathogenesis responses}},
journal = {mBio},
year = {2026},
month = jun,
volume = {17},
number = {7},
pages = {e00863--26},
publisher = {American Society for Microbiology (ASM)},
issn = {2150-7511},
doi = {10.1128/mbio.00863-26},
url = {https://doi.org/10.1128/mbio.00863-26},
pmid = {42294680},
pmcid = {PMC13343971}
}

RIS

TY - JOUR
AU - Harding, Alfred T.
AU - Zhang, Yichen
AU - Chen, Jane-Jane
AU - Antonucci, Jenna M.
AU - Richards, Alexsia
AU - Leger, Valerie
AU - Whittaker, Charles A.
AU - Vanrobaeys, Yann S.
AU - Agarwal, Divyansh
AU - Lungjangwa, Tenzin
AU - Jaenisch, Rudolf
AU - Gehrke, Lee
TI - Zika virus infections of human stem cell-derived cerebral organoids reveal viral lineage-specific pathogenesis responses
T2 - mBio
J2 - mBio
PY - 2026
DA - 2026/06/15
VL - 17
IS - 7
SP - e00863
EP - 26
SN - 2150-7511
PB - American Society for Microbiology (ASM)
DO - 10.1128/mbio.00863-26
UR - https://doi.org/10.1128/mbio.00863-26
LA - en
ER -

CSL-JSON

{
"id": "10.1128/mbio.00863-26",
"type": "article-journal",
"title": "Zika virus infections of human stem cell-derived cerebral organoids reveal viral lineage-specific pathogenesis responses",
"container-title": "mBio",
"author": [
{
"family": "Harding",
"given": "Alfred T."
},
{
"family": "Zhang",
"given": "Yichen"
},
{
"family": "Chen",
"given": "Jane-Jane"
},
{
"family": "Antonucci",
"given": "Jenna M."
},
{
"family": "Richards",
"given": "Alexsia"
},
{
"family": "Leger",
"given": "Valerie"
},
{
"family": "Whittaker",
"given": "Charles A."
},
{
"family": "Vanrobaeys",
"given": "Yann S."
},
{
"family": "Agarwal",
"given": "Divyansh"
},
{
"family": "Lungjangwa",
"given": "Tenzin"
},
{
"family": "Jaenisch",
"given": "Rudolf"
},
{
"family": "Gehrke",
"given": "Lee"
}
],
"container-title-short": "mBio",
"volume": "17",
"issue": "7",
"page": "e00863-26",
"DOI": "10.1128/mbio.00863-26",
"PMID": "42294680",
"PMCID": "PMC13343971",
"ISSN": "2150-7511",
"publisher": "American Society for Microbiology (ASM)",
"URL": "https://doi.org/10.1128/mbio.00863-26",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
15
]
]
}
}

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: edgeR, DESeq2, circlize, 5 other tools, 4 references
[2] doi:10.1038/s41593-026-02300-5 [code]
Integrated single-cell and spatial transcriptomic profiling in ALS uncovers peripheral-to-central immune infiltration and reprogramming.
Journal: Nature neuroscience
In common: edgeR, DESeq2, circlize, 5 other tools, other condition
[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: edgeR, DESeq2, circlize, 5 other tools, other condition
[4] doi:10.1016/j.isci.2026.115657 [code]
Integration of machine learning to develop a disulfidptosis model for predicting glioma prognosis, immunotherapy response, and drug.
Journal: iScience
In common: edgeR, DESeq2, circlize, 5 other tools, other condition
[5] 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: Nextflow, edgeR, circlize, 4 other tools, other condition
[6] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: edgeR, DESeq2, circlize, 5 other tools
[7] doi:10.1093/braincomms/fcag239 [code]
Extracellular matrix remodelling in degenerative cervical myelopathy.
Journal: Brain communications
In common: edgeR, DESeq2, circlize, 5 other tools
[8] doi:10.1038/s41586-026-10512-9 [code]
Astrocyte glucocorticoid receptor signalling restricts neuronal plasticity.
Journal: Nature
In common: edgeR, DESeq2, circlize, 5 other tools
[9] doi:10.1016/j.isci.2026.115573 [code]
Female cortical cellular mosaicism underlies shared MeCP2 and PCB impacted gene pathways.
Journal: iScience
In common: edgeR, DESeq2, circlize, 5 other tools
[10] doi:10.1002/imt2.70163 [code]
Spatial multi-omics unveils sphingolipid metabolic reprogramming within the retinal pathological niche.
Journal: iMeta
In common: edgeR, DESeq2, circlize, 5 other tools

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.