OSCR

Proteomic profiling of brain organoids and extracellular vesicles identifies early Alzheimer's disease biomarkers and drug response heterogeneity.

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. [1] § RESULTS › 5‐HT organoids and EVs represent distinct sources of biological information that exhibit internally consistent expression profiles ↔ Code/Bioinfo_Part2_AD vs NCI_EO vs CL.R, lines 725–745 · score 0.77 · ATP1A3, L1CAM, PDCD6IP, NCI EO, NCAM1, CD63
  2. [2] § RESULTS › 5‐HT organoids and EVs represent distinct sources of biological information that exhibit internally consistent expression profiles ↔ Code/Bioinfo_Part2_AD vs NCI_EO vs CL.R, lines 725–745 · score 0.59 · HTR7, MAOA, MAOB, NES, TUBB3, GAP43
  3. [3] § METHODS › Bioinformatics analyses ↔ Code/Bioinfo_Part2_AD vs NCI_EO vs CL.R, lines 1–60 · score 0.55 · experimental batches, downstream, Bioinformatics, PCA, volcano, heatmap
  4. [4] § METHODS › Bioinformatics analyses ↔ Code/Bioinfo_Part1_EV vs Org.R, lines 1–54 · score 0.52 · median normalization, downstream, Bioinformatics, volcano, filtering, heatmap

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

  1. # -------------------------------------------------------------------
  2. # Extracellular Vesicle & Organoid Proteomics Pipeline (R)
  3. # -------------------------------------------------------------------
  4. # This script ingests raw quantitative proteomics data, merges them with
  5. # sample‑level metadata, cleans and normalises the expression matrix,
  6. # performs batch‑effect correction, exploratory PCA, differential
  7. # expression testing (disease‑ and treatment‑based contrasts), and a
  8. # suite of downstream visualisations (boxplots, volcano plots, violin
  9. # plots, PCA overlays and annotated heatmaps).
  10. # -------------------------------------------------------------------
  11. # ----------------------------
  12. # SECTION 0 – SET‑UP
  13. # ----------------------------
  14. setwd("./Data_used_in_bioinfo") # point to folder containing raw xlsx files
  15. EV <- list() # container for extracellular‑vesicle data
  16. Org <- list() # container for organoid data
  17. # ----------------------------
  18. # SECTION 1 – LIBRARIES
  19. # ----------------------------
  20. library(dplyr)
  21. library(readxl)
  22. library(stringr)
  23. library(tibble)
  24. library(HarmonizR)
  25. library(ggplot2)
  26. library(reshape2)
  27. library(ggrepel)
  28. library(ggfortify)
  29. library(pheatmap)
  30. library(purrr)
  31. # ----------------------------
  32. # SECTION 2 – CLINICAL METADATA
  33. # ----------------------------
  34. # Read sample sheets for the two experimental batches
  35. batch1.clinical <- read_excel("20240501_cz050_samples list for tymora_updated.xlsx", sheet = "batch 1 (cz031)")
  36. batch2.clinical <- read_excel("20240501_cz050_samples list for tymora_updated.xlsx", sheet = "batch 2 (cz039)")
  37. # Split into EV / organoid and bind batches together
  38. EV[["clinical"]] <- rbind(batch1.clinical[batch1.clinical$`sample type` == "EVs",],
  39. batch2.clinical[batch2.clinical$`sample type` == "EVs",])
  40. Org[["clinical"]] <- rbind(batch1.clinical[batch1.clinical$`sample type` == "organoids",],
  41. batch2.clinical[batch2.clinical$`sample type` == "organoids",])
  42. # ----------------------------
  43. # SECTION 3 – RAW QUANTITATIVE MATRICES
  44. # ----------------------------
  45. EV[["raw"]] <- read_excel("JHMI-Vasso-batch1_and_2-media-EV-quant.xlsx", sheet = "Proteins")
  46. Org[["raw"]] <- read_excel("032424-JHMI-Vasso-organoid-batch_1_2-quant.xlsx", sheet = "Proteins")
  47. # ----------------------------
  48. # SECTION 4 – SAMPLE‑LEVEL METADATA & LABELS
  49. # ----------------------------
  50. sample.clinical <- read_excel("sample_metadata.xlsx")
  51. sample.clinical$CellLineID <- tolower(sample.clinical$CellLineID)
  52. EV[["clinical"]]$`original cell line name` <- tolower(EV[["clinical"]]$`original cell line name`)
  53. Org[["clinical"]]$`original cell line name` <- tolower(Org[["clinical"]]$`original cell line name`)
  54. # Function: build unique sample labels incorporating disease, matrix
  55. # (EV/organoid), numeric ID and treatment status
  56. create_label <- function(clinical.data) {
  57. clinical.data %>%
  58. mutate(
  59. label = case_when(
  60. diagnosis == "n/a" ~ "n/a",
  61. diagnosis == "AD" ~ paste0("AD.", ifelse(`sample type` == "organoids", "Org.", "EV."), id, ifelse(treatment == "+EO", ".eo", ".cl")),
  62. diagnosis == "AD+NPS" ~ paste0("ADNPS.", ifelse(`sample type` == "organoids", "Org.", "EV."), id, ifelse(treatment == "+EO", ".eo", ".cl")),
  63. diagnosis == "healthy"~ paste0("NCI.", ifelse(`sample type` == "organoids", "Org.", "EV."), id, ifelse(treatment == "+EO", ".eo", ".cl"))
  64. )
  65. )
  66. }
  67. EV[["clinical"]] <- create_label(EV[["clinical"]])
  68. Org[["clinical"]] <- create_label(Org[["clinical"]])
  69. # Merge extra clinical attributes (sex, age, etc.)
  70. colnames(sample.clinical)[2] <- "original cell line name"
  71. EV[["clinical"]] <- merge(EV[["clinical"]], sample.clinical[, c(2, 4:7)], by = "original cell line name", all.x = TRUE)
  72. Org[["clinical"]] <- merge(Org[["clinical"]], sample.clinical[, c(2, 4:7)], by = "original cell line name", all.x = TRUE)
  73. # ----------------------------
  74. # SECTION 5 – COLUMN RENAMING & BASIC CLEAN‑UP
  75. # ----------------------------
  76. EV[["clean"]] <- EV[["raw"]][, c(1, 2, 13:ncol(EV[["raw"]]))]
  77. Org[["clean"]] <- Org[["raw"]][, c(1, 2, 13:ncol(Org[["raw"]]))]
  78. # replace ‘Abundances (Normalised): <sample #>’ headers with
  79. # sample labels constructed above, appending ".1", ".2"… where the
  80. # same label appears multiple times (e.g. technical replicates)
  81. replace_colnames <- function(preclean.data, clinical.data) {
  82. sample_numbers <- str_extract(colnames(preclean.data), "(?<=Abundances \\(Normalized\\): )\\d+")
  83. name_map <- clinical.data %>%
  84. filter(`sample #` %in% as.numeric(sample_numbers)) %>%
  85. select(`sample #`, label) %>%
  86. deframe()
  87. new_colnames <- colnames(preclean.data)
  88. for (i in seq_along(sample_numbers)) {
  89. sample_num <- sample_numbers[i]
  90. if (!is.na(sample_num) && sample_num %in% names(name_map)) {
  91. new_colnames[i] <- name_map[[sample_num]]
  92. }
  93. }
  94. name_counts <- ave(seq_along(new_colnames), new_colnames, FUN = seq)
  95. ifelse(duplicated(new_colnames) | duplicated(new_colnames, fromLast = TRUE),
  96. paste0(new_colnames, ".", name_counts),
  97. new_colnames)
  98. }
  99. colnames(EV[["clean"]]) <- replace_colnames(EV[["clean"]], EV[["clinical"]])
  100. colnames(Org[["clean"]]) <- replace_colnames(Org[["clean"]], Org[["clinical"]])
  101. colnames(EV[["clean"]])[2] <- "Gene"
  102. colnames(Org[["clean"]])[2] <- "Gene"
  103. # ----------------------------
  104. # SECTION 6 – BATCH‑EFFECT CORRECTION (HarmonizR)
  105. # ----------------------------
  106. batch_effect_adjust <- function(clean.data) {
  107. merge_df <- clean.data[, 4:ncol(clean.data)]
  108. SampleNum <- as.numeric(str_extract(colnames(merge_df), "(?<=\\.(EV|Org)\\.)\\d+"))
  109. batchType <- ifelse(SampleNum %in% c(1, 2, 3), 1, 2)
  110. Description <- data.frame(ID = colnames(merge_df), sample = seq_len(ncol(merge_df)), batch = batchType)
  111. merge_df <- cbind(clean.data[, 1], merge_df)
  112. write.table(merge_df, "test.tsv", row.names = FALSE, sep = "\t")
  113. write.csv(Description, "Description.csv", row.names = FALSE)
  114. temp <- HarmonizR::harmonizR("test.tsv", "Description.csv")
  115. temp$Accession <- rownames(temp)
  116. merge(clean.data[, 1:3], temp, by = "Accession")
  117. }
  118. EV[["adjust"]] <- batch_effect_adjust(EV[["clean"]])
  119. Org[["adjust"]] <- batch_effect_adjust(Org[["clean"]])
  120. file.remove("./test.tsv", "./Description.csv", "./cured_data.tsv")
  121. # ----------------------------
  122. # SECTION 7 – EXPLORATORY PCA (PRE/POST BATCH CORRECTION)
  123. # ----------------------------
  124. # Function draws PCA coloured by batch assignment and saves PDF files.
  125. batch_pca_plot <- function(my.data, my.title = "PCA plot") {
  126. numeric_data <- my.data[, sapply(my.data, is.numeric), drop = FALSE]
  127. numeric_data <- na.omit(numeric_data)
  128. sample_names <- colnames(numeric_data)
  129. splitted <- strsplit(sample_names, "\\.")
  130. batch_vec <- sapply(splitted, function(x) ifelse(length(x) < 3, "batch2", {num <- suppressWarnings(as.integer(x[3])); if (!is.na(num) && num %in% c(1, 2, 3)) "batch1" else "batch2"}))
  131. pca_res <- prcomp(t(numeric_data), center = TRUE, scale. = TRUE)
  132. df_pca <- as.data.frame(pca_res$x)
  133. var_expl <- (pca_res$sdev^2 / sum(pca_res$sdev^2)) * 100
  134. ggplot(data.frame(PC1 = df_pca$PC1, PC2 = df_pca$PC2, Group = factor(batch_vec)),
  135. aes(PC1, PC2, colour = Group)) +
  136. geom_point(size = 2) +
  137. labs(x = paste0("PC1: ", sprintf("%.2f", var_expl[1]), "%"),
  138. y = paste0("PC2: ", sprintf("%.2f", var_expl[2]), "%"),
  139. title = my.title) +
  140. theme_minimal() +
  141. theme(legend.position = "right", plot.title = element_text(hjust = 0.5)) +
  142. scale_colour_manual(values = c(batch1 = "#66c2a6", batch2 = "#8ea0cb"))
  143. }
  144. # Generate PCA plots & save
  145. batch_pca_plot(EV[["clean"]],my.title = "PCA - EV - Before BEC")
  146. ggsave("Figure1D_PCA_EV_BeforeBEC.pdf",width = 6, height = 6)
  147. batch_pca_plot(EV[["adjust"]],my.title = "PCA - EV - After BEC")
  148. ggsave("Figure1D_PCA_EV_AfterBEC.pdf",width = 6, height = 6)
  149. batch_pca_plot(Org[["clean"]],my.title = "PCA - Org - Before BEC")
  150. ggsave("Figure1B_PCA_Org_BeforeBEC.pdf",width = 6, height = 6)
  151. batch_pca_plot(Org[["adjust"]],my.title = "PCA - Org - After BEC")
  152. ggsave("Figure1B_PCA_Org_AfterBEC.pdf",width = 6, height = 6)
  153. # ----------------------------
  154. # SECTION 8 – MISSING‑VALUE FILTERING
  155. # ----------------------------
  156. # Retain proteins with ≥ `na_threshold` proportion of non‑zero values in
  157. # any diagnostic × treatment subgroup.
  158. filter_na_rows_improve <- function(my.nooutlier.data, na_threshold = 0.8) {
  159. ad.cl <- grep("(AD|ADNPS)\\.[A-Za-z]+\\.\\d+\\.cl", colnames(my.nooutlier.data), value = TRUE)
  160. nci.cl <- grep("NCI\\.[A-Za-z]+\\.\\d+\\.cl", colnames(my.nooutlier.data), value = TRUE)
  161. ad.eo <- grep("(AD|ADNPS)\\.[A-Za-z]+\\.\\d+\\.eo", colnames(my.nooutlier.data), value = TRUE)
  162. nci.eo <- grep("NCI\\.[A-Za-z]+\\.\\d+\\.eo", colnames(my.nooutlier.data), value = TRUE)
  163. frac_ok <- function(cols) apply(my.nooutlier.data[, cols, drop = FALSE], 1, function(x) mean(x != 0 & !is.na(x)))
  164. keep_rows <- frac_ok(ad.cl) > na_threshold | frac_ok(nci.cl) > na_threshold | frac_ok(ad.eo) > na_threshold | frac_ok(nci.eo) > na_threshold
  165. my.nooutlier.data[keep_rows, , drop = FALSE]
  166. }
  167. EV[["filtermiss"]] <- filter_na_rows_improve(EV[["adjust"]])
  168. Org[["filtermiss"]] <- filter_na_rows_improve(Org[["adjust"]])
  169. # ----------------------------
  170. # SECTION 9 – LOG2 TRANSFORM & MEDIAN NORMALISATION
  171. # ----------------------------
  172. norm_transform <- function(my.filtermiss.data) {
  173. meta_cols <- my.filtermiss.data[, 1:3]
  174. quant <- my.filtermiss.data[, -c(1:3)]
  175. log2_data <- log2(quant + 1)
  176. norm_data <- sweep(log2_data, 2, apply(log2_data, 2, median, na.rm = TRUE), FUN = "-")
  177. cbind(meta_cols, norm_data)
  178. }
  179. EV[["norm"]] <- norm_transform(EV[["filtermiss"]])
  180. Org[["norm"]] <- norm_transform(Org[["filtermiss"]])
  181. # ----------------------------
  182. # SECTION 10 – DIFFERENTIAL EXPRESSION (CUSTOM t‑TEST WRAPPER)
  183. # ----------------------------
  184. perform_t_test <- function(my.data,
  185. diag = FALSE,
  186. treat = FALSE,
  187. paired = FALSE,
  188. reverse_fc = FALSE,
  189. filter_diag = NULL,
  190. filter_treat = NULL) {
  191. if (!is.data.frame(my.data) && !is.matrix(my.data)) {
  192. stop("Input data must be a data frame or matrix.")
  193. }
  194. my.data <- as.data.frame(my.data)
  195. if (diag && treat) {
  196. stop("Please choose either diag or treat (but not both) for t-test in this function.")
  197. }
  198. expr_data <- my.data[, sapply(my.data, is.numeric), drop = FALSE]
  199. if (nrow(expr_data) == 0 || ncol(expr_data) == 0) {
  200. stop("No numeric expression columns found.")
  201. }
  202. sample_names <- colnames(expr_data)
  203. splitted <- strsplit(sample_names, "\\.")
  204. diag_vec <- sapply(splitted, function(x) x[1])
  205. treat_vec <- sapply(splitted, function(x) if (length(x) >= 4) x[4] else NA)
  206. keep <- rep(TRUE, length(sample_names))
  207. if (!is.null(filter_diag) && length(filter_diag) > 0) {
  208. keep <- keep & (diag_vec %in% filter_diag)
  209. }
  210. if (!is.null(filter_treat) && length(filter_treat) > 0) {
  211. keep <- keep & (treat_vec %in% filter_treat)
  212. }
  213. if (!any(keep)) {
  214. stop("No samples left after filtering!")
  215. }
  216. expr_data <- expr_data[, keep, drop = FALSE]
  217. diag_vec <- diag_vec[keep]
  218. treat_vec <- treat_vec[keep]
  219. sample_names <- sample_names[keep]
  220. splitted <- splitted[keep]
  221. if (diag) {
  222. group_labels <- ifelse(diag_vec %in% c("AD", "ADNPS"), "Dis", "NPI")
  223. } else if (treat) {
  224. group_labels <- ifelse(treat_vec == "eo", "Treatment", "NoTreatment")
  225. } else {
  226. stop("You must set either diag=TRUE or treat=TRUE for this t-test.")
  227. }
  228. if (length(unique(group_labels)) < 2) {
  229. stop("Only one group found after filtering; cannot perform t-test.")
  230. }
  231. results <- data.frame(
  232. Gene = my.data$Gene,
  233. Accession = my.data$Accession,
  234. Modifications = my.data$Modifications,
  235. p_value = NA_real_,
  236. log2FC = NA_real_
  237. )
  238. if (paired && diag) {
  239. warning("Paired t-test typically makes sense for treat grouping, but diag=TRUE was set. Will attempt it anyway.")
  240. }
  241. if (paired) {
  242. individual_id <- sapply(splitted, function(x) {
  243. if (length(x) < 3) {
  244. paste(x, collapse=".")
  245. } else {
  246. paste(x[1:3], collapse=".")
  247. }
  248. })
  249. g1_name <- unique(group_labels)[1]
  250. g2_name <- unique(group_labels)[2]
  251. for (i in seq_len(nrow(expr_data))) {
  252. row_values <- as.numeric(expr_data[i, ])
  253. pairVals_g1 <- c()
  254. pairVals_g2 <- c()
  255. all_individuals <- unique(individual_id)
  256. for (indiv in all_individuals) {
  257. idx_g1 <- which(individual_id == indiv & group_labels == g1_name)
  258. idx_g2 <- which(individual_id == indiv & group_labels == g2_name)
  259. if (length(idx_g1) == 1 && length(idx_g2) == 1) {
  260. val1 <- row_values[idx_g1]
  261. val2 <- row_values[idx_g2]
  262. if (!is.na(val1) && !is.na(val2)) {
  263. pairVals_g1 <- c(pairVals_g1, val1)
  264. pairVals_g2 <- c(pairVals_g2, val2)
  265. }
  266. }
  267. }
  268. if (length(pairVals_g1) > 1 && length(pairVals_g2) > 1) {
  269. t_test_res <- tryCatch(
  270. t.test(pairVals_g1, pairVals_g2, paired = TRUE, var.equal = FALSE),
  271. error = function(e) {
  272. warning(paste("Paired t.test failed in row", i, ":", e$message))
  273. return(NULL)
  274. }
  275. )
  276. if (!is.null(t_test_res)) {
  277. p_val <- t_test_res$p.value
  278. log2fc_val <- mean(pairVals_g1) - mean(pairVals_g2)
  279. if (reverse_fc) {
  280. log2fc_val <- -log2fc_val
  281. }
  282. results$p_value[i] <- p_val
  283. results$log2FC[i] <- log2fc_val
  284. }
  285. } else {
  286. results$p_value[i] <- NA
  287. results$log2FC[i] <- NA
  288. }
  289. }
  290. } else {
  291. uni_group_labels <- unique(group_labels)
  292. uni_group_labels <- uni_group_labels[order(uni_group_labels)]
  293. g1_name <- uni_group_labels[1]
  294. g2_name <- uni_group_labels[2]
  295. for (i in seq_len(nrow(expr_data))) {
  296. row_values <- as.numeric(expr_data[i, ])
  297. group1_values <- row_values[group_labels == g1_name]
  298. group2_values <- row_values[group_labels == g2_name]
  299. if (sum(!is.na(group1_values)) > 1 && sum(!is.na(group2_values)) > 1) {
  300. t_test_res <- tryCatch(
  301. t.test(group1_values, group2_values, paired = FALSE, var.equal = FALSE),
  302. error = function(e) {
  303. warning(paste("Unpaired t.test failed in row", i, ":", e$message))
  304. return(NULL)
  305. }
  306. )
  307. if (!is.null(t_test_res)) {
  308. p_val <- t_test_res$p.value
  309. log2fc_val <- mean(group1_values, na.rm=TRUE) - mean(group2_values, na.rm=TRUE)
  310. if (reverse_fc) {
  311. log2fc_val <- -log2fc_val
  312. }
  313. results$p_value[i] <- p_val
  314. results$log2FC[i] <- log2fc_val
  315. }
  316. } else {
  317. results$p_value[i] <- NA
  318. results$log2FC[i] <- NA
  319. }
  320. }
  321. }
  322. results$padj <- p.adjust(results$p_value, method = "BH")
  323. final <- cbind(
  324. results[, c("Gene", "Accession", "Modifications",
  325. "p_value", "padj", "log2FC")],
  326. expr_data
  327. )
  328. return(final)
  329. }
  330. generate_DE_list <- function(norm.data){
  331. DEG <- list()
  332. DEG[["ADvsNCI"]] <- perform_t_test(norm.data, diag = T)
  333. DEG[["ADvsNCI_|_onlyEO"]] <- perform_t_test(norm.data, diag = T, filter_treat = "eo")
  334. DEG[["ADvsNCI_|_onlyCL"]] <- perform_t_test(norm.data, diag = T, filter_treat = "cl")
  335. DEG[["EOvsCL"]] <- perform_t_test(norm.data, treat = T, paired = T, reverse_fc = T)
  336. DEG[["EOvsCL_|_onlyAD"]] <- perform_t_test(norm.data, treat = T, paired = T, reverse_fc = T, filter_diag = c("AD","ADNPS"))
  337. DEG[["EOvsCL_|_onlyNCI"]] <- perform_t_test(norm.data, treat = T, paired = T, reverse_fc = T, filter_diag = "NCI")
  338. return(DEG)
  339. }
  340. EV.DEG <- generate_DE_list(EV[["norm"]])
  341. Org.DEG <- generate_DE_list(Org[["norm"]])
  342. EV.DEGp005 <- EV.DEG
  343. for (i in 1:6) {
  344. EV.DEGp005[[i]] <- EV.DEGp005[[i]][which(EV.DEGp005[[i]]$padj < 0.05
  345. & abs(EV.DEGp005[[i]]$log2FC)>1),]
  346. }
  347. Org.DEGp005 <- Org.DEG
  348. for (i in 1:6) {
  349. Org.DEGp005[[i]] <- Org.DEGp005[[i]][which(Org.DEGp005[[i]]$padj < 0.05
  350. & abs(Org.DEGp005[[i]]$log2FC)>1),]
  351. }
  352. # ----------------------------
  353. # SECTION 11 – AUXILIARY ANALYSES (MARKER/TREATMENT OVERLAP)
  354. # ----------------------------
  355. check_treat_vs_marker <- function(DEG_list){
  356. temp_AD_marker <- DEG_list[["ADvsNCI_|_onlyCL"]] %>%
  357. filter(padj < 0.05, abs(log2FC) > log2(1.5)) %>%
  358. select(1:6)
  359. temp_treat_effct <- DEG_list[["EOvsCL_|_onlyAD"]] %>%
  360. filter(padj < 0.05, abs(log2FC) > log2(1.5)) %>%
  361. select(1:6)
  362. colnames(temp_AD_marker)[4:6] <- c("AD.p_value", "AD.padj", "AD.log2FC")
  363. colnames(temp_treat_effct)[4:6] <- c("EO.p_value", "EO.padj", "EO.log2FC")
  364. treat_vs_marker <- merge(temp_AD_marker, temp_treat_effct[,c(1,4:6)], by='Gene',all=F)
  365. treat_vs_marker$trend <- treat_vs_marker$AD.log2FC * treat_vs_marker$EO.log2FC
  366. treat_vs_marker <- treat_vs_marker[which(treat_vs_marker$trend<0),]
  367. return(treat_vs_marker)
  368. }
  369. EV.DEG[["treat_vs_marker"]] <- check_treat_vs_marker(EV.DEG)
  370. Org.DEG[["treat_vs_marker"]] <- check_treat_vs_marker(Org.DEG)
  371. # ----------------------------
  372. # SECTION 12 – RESPONDER VS UNRESPONDER
  373. # ----------------------------
  374. unresponder <- c("AD.17","AD.18","ADNPS.19","ADNPS.20")
  375. perform_t_test_unresponder <- function(my.data = EV[["norm"]], keep_treat ="cl") {
  376. expr_data <- my.data[, sapply(my.data, is.numeric), drop = FALSE]
  377. sample_names <- colnames(expr_data)
  378. splitted <- strsplit(sample_names, "\\.")
  379. diag_vec <- sapply(splitted, function(x) x[1])
  380. treat_vec <- sapply(splitted, function(x) x[4])
  381. sample_id <- sapply(splitted, function(x) x[3])
  382. sample_name <- paste0(diag_vec,".",sample_id)
  383. keep <- rep(TRUE, length(sample_names))
  384. keep <- keep & (treat_vec %in% keep_treat)
  385. expr_data <- expr_data[, keep, drop = FALSE]
  386. diag_vec <- diag_vec[keep]
  387. treat_vec <- treat_vec[keep]
  388. sample_names <- sample_names[keep]
  389. splitted <- splitted[keep]
  390. group_labels <- ifelse(sample_name %in% unresponder, "Unres", "Res")
  391. results <- data.frame(
  392. Gene = my.data$Gene,
  393. Accession = my.data$Accession,
  394. Modifications = my.data$Modifications,
  395. p_value = NA_real_,
  396. log2FC = NA_real_
  397. )
  398. uni_group_labels <- unique(group_labels)
  399. uni_group_labels <- uni_group_labels[order(uni_group_labels)]
  400. g1_name <- uni_group_labels[1]
  401. g2_name <- uni_group_labels[2]
  402. for (i in seq_len(nrow(expr_data))) {
  403. row_values <- as.numeric(expr_data[i, ])
  404. group1_values <- row_values[group_labels == g1_name]
  405. group2_values <- row_values[group_labels == g2_name]
  406. if (sum(!is.na(group1_values)) > 1 && sum(!is.na(group2_values)) > 1) {
  407. t_test_res <- tryCatch(
  408. t.test(group1_values, group2_values, paired = FALSE, var.equal = FALSE),
  409. error = function(e) {
  410. warning(paste("Unpaired t.test failed in row", i, ":", e$message))
  411. return(NULL)
  412. }
  413. )
  414. if (!is.null(t_test_res)) {
  415. p_val <- t_test_res$p.value
  416. log2fc_val <- mean(group2_values, na.rm=TRUE) - mean(group1_values, na.rm=TRUE)
  417. results$p_value[i] <- p_val
  418. results$log2FC[i] <- log2fc_val
  419. }
  420. } else {
  421. results$p_value[i] <- NA
  422. results$log2FC[i] <- NA
  423. }
  424. }
  425. results$padj <- p.adjust(results$p_value, method = "BH")
  426. final <- cbind(
  427. results[, c("Gene", "Accession", "Modifications",
  428. "p_value", "padj", "log2FC")],
  429. expr_data
  430. )
  431. return(final)
  432. }
  433. unrespond <- list()
  434. unrespond[["EV_DEG"]] <- perform_t_test_unresponder(EV[["norm"]])
  435. unrespond[["EV_DEGp005"]] <- unrespond[["EV_DEG"]][which(unrespond[["EV_DEG"]]$padj<0.05 & abs(unrespond[["EV_DEG"]]$log2FC)>1),]
  436. unrespond[["Org_DEG"]] <- perform_t_test_unresponder(Org[["norm"]])
  437. unrespond[["Org_DEGp005"]] <- unrespond[["Org_DEG"]][which(unrespond[["Org_DEG"]]$padj<0.05 & abs(unrespond[["Org_DEG"]]$log2FC)>1),]
  438. # ----------------------------
  439. # SECTION 13 – VISUALISATIONS
  440. # ----------------------------
  441. # 13A) Batch & QC boxplots -------------------------------------------
  442. batch_check_boxplot <- function(mydata = EV[["norm"]],
  443. extract_pattern = "(?<=\\.EV\\.)\\d+"){
  444. long_data <- reshape2::melt(mydata, id.vars = c("Gene", "Accession", "Modifications"),
  445. variable.name = "Sample", value.name = "Expression")
  446. long_data <- long_data %>%
  447. mutate(
  448. SampleNum = as.numeric(str_extract(Sample, extract_pattern)),
  449. Group = case_when(
  450. str_detect(Sample, "^NCI") ~ "NCI",
  451. TRUE ~ "AD_ADNPS"
  452. ),
  453. Ending = ifelse(str_ends(Sample, "\\.cl"), "cl", "eo"),
  454. Is123 = ifelse(SampleNum %in% c(1, 2, 3), TRUE, FALSE),
  455. SortKey = paste0(Is123, "_", Ending, "_", Group, "_", sprintf("%02d", SampleNum)),
  456. ColorGroup = ifelse(Is123, "#66c2a6", "#8ea0cb")
  457. ) %>%
  458. arrange(desc(Is123), Ending, Group, SampleNum) %>%
  459. mutate(Sample = factor(Sample, levels = unique(Sample)))
  460. ggplot(long_data, aes(x = Sample, y = Expression, fill = ColorGroup)) +
  461. geom_boxplot(outlier.size = 0.05, na.rm = TRUE) +
  462. scale_fill_manual(values = c("#66c2a6" = "#66c2a6", "#8ea0cb" = "#8ea0cb")) +
  463. theme_minimal() +
  464. labs(x = "Sample", y = "Expression Level") +
  465. theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "none")
  466. }
  467. # Save boxplots
  468. batch_check_boxplot(mydata = EV[["norm"]],
  469. extract_pattern = "(?<=\\.EV\\.)\\d+")
  470. ggsave("Figure1E_boxplot_EV_Expression_Distribution.pdf",width=14, height = 5)
  471. batch_check_boxplot(mydata = Org[["norm"]],
  472. extract_pattern = "(?<=\\.Org\\.)\\d+")
  473. ggsave("Figure1C_boxplot_Org_Expression_Distribution.pdf",width=14, height = 5)
  474. # 13B) Volcano plots --------------------------------------------------
  475. volcano_plot <- function(deg_data = EV.DEG[["ADvsNCI_|_onlyCL"]], titlename, p_thre = 0.05, log2fc_thre = 1){
  476. temp <- deg_data[,1:6]
  477. temp$select <- ifelse(temp$padj<p_thre & abs(temp$log2FC)>log2fc_thre,temp$Gene,NA)
  478. temp$group <- ifelse(temp$log2FC > 1 & temp$padj < 0.05,
  479. "Upregulated",
  480. ifelse(temp$log2FC < -1 & temp$padj < 0.05,
  481. "Downregulated",
  482. "No Difference"))
  483. temp <- temp[!is.na(temp$padj),]
  484. ggplot(
  485. temp, aes(x = log2FC, y = -log10(padj), color = factor(group))
  486. ) +
  487. geom_point(size=1.5) +
  488. geom_label_repel(
  489. color = "white",
  490. arrow = arrow(length = unit(0.03, "npc"), type = "closed", ends = "first"),
  491. point.padding = NA,
  492. box.padding = 0.1,
  493. aes(label = select, fill = group),
  494. size = 2,
  495. fontface = "bold",
  496. max.overlaps=30
  497. ) +
  498. scale_fill_manual(
  499. values = c("Upregulated" = "tomato", "Downregulated" = "skyblue", "No Difference" = "grey"),labels=NULL
  500. ) +
  501. scale_color_manual(
  502. values = c("Upregulated" = "tomato", "Downregulated" = "skyblue", "No Difference" = "grey")
  503. ) +
  504. theme_bw() +
  505. labs(x="log2(fold change)", y="-log10 (padj)", title = titlename) +
  506. theme(
  507. legend.position = "right",
  508. panel.grid.major = element_blank(), # This will remove the major grid lines
  509. panel.grid.minor = element_blank(), # This will remove the minor grid lines
  510. legend.title = element_blank() # This will remove the legend titles if needed
  511. ) +
  512. geom_hline(yintercept = -log10(0.05),linetype=2,cex=0.5,color = "grey")+ #添加辅助线
  513. geom_vline(xintercept = c(-1,1),linetype=2,cex=0.5,color = "grey")
  514. }
  515. # Save volcano plots
  516. volcano_plot(EV.DEG[["ADvsNCI_|_onlyCL"]], titlename = "Untreated EVs - AD vs NCI")
  517. ggsave("Figure3D_volcano_Untreated EVs_AD vs NCI.pdf",width = 6, height = 6)
  518. volcano_plot(Org.DEG[["ADvsNCI_|_onlyCL"]], titlename = "Untreated Organoids - AD vs NCI")
  519. ggsave("Figure3A_volcano_Untreated Orgs_AD vs NCI.pdf",width = 6, height = 6)
  520. volcano_plot(EV.DEG[["EOvsCL"]], titlename = "All EVs - EO vs CL",p_thre=0.001,log2fc_thre =2.5)
  521. ggsave("Figure5A_volcano_All EVs_EO vs CL.pdf",width = 6, height = 6)
  522. volcano_plot(Org.DEG[["EOvsCL"]], titlename = "All Organoids - EO vs CL",p_thre=0.01,log2fc_thre =1.5)
  523. ggsave("Figure4A_volcano_All Orgs_EO vs CL.pdf",width = 6, height = 6)
  524. volcano_plot(EV.DEG[["EOvsCL_|_onlyAD"]],titlename = "AD EVs - EO vs CL",p_thre=10e-6,log2fc_thre =2.5)
  525. ggsave("Figure5_volcano_AD EVs_EO vs CL.pdf",width = 6, height = 6)
  526. volcano_plot(Org.DEG[["EOvsCL_|_onlyAD"]],titlename = "AD Organoids - EO vs CL",p_thre=10e-6,log2fc_thre =2.5)
  527. ggsave("Figure4_volcano_AD Orgs_EO vs CL.pdf",width = 6, height = 6)
  528. volcano_plot(EV.DEG[["EOvsCL_|_onlyNCI"]], titlename = "NCI EVs - EO vs CL")
  529. ggsave("Figure5_volcano_NCI EVs_EO vs CL.pdf",width = 6, height = 6)
  530. volcano_plot(Org.DEG[["EOvsCL_|_onlyNCI"]], titlename = "NCI Organoids - EO vs CL")
  531. ggsave("Figure4_volcano_NCI Orgs_EO vs CL.pdf",width = 6, height = 6)
  532. # 13C) Violin plots ---------------------------------------------------
  533. Violin_Protein_Expression_Facet <- function(genes,
  534. data,
  535. DEGdata,
  536. mytitle,
  537. log2 = TRUE) {
  538. # Get the data
  539. df_sub <- data[data$Gene %in% genes, ]
  540. long_data <- reshape2::melt(
  541. df_sub,
  542. id.vars = c("Gene", "Accession", "Modifications"),
  543. variable.name = "Sample",
  544. value.name = "Expression"
  545. )
  546. splitted <- strsplit(as.character(long_data$Sample), "\\.")
  547. diag_vec <- sapply(splitted, `[`, 1)
  548. diag_vec <- ifelse(diag_vec == "NCI", "NCI", "AD")
  549. treat_vec <- sapply(splitted, `[`, 4)
  550. long_data$Group <- paste0(diag_vec, ".", treat_vec)
  551. if(log2){long_data$Expression <- log2(long_data$Expression)}
  552. long_data$Group <- factor(long_data$Group, levels = sort(unique(long_data$Group)))
  553. long_data$SubjectID <- sapply(splitted, function(x) {
  554. if (length(x) < 3) paste(x, collapse = ".")
  555. else paste(x[1:3], collapse = ".")
  556. })
  557. # Get the p value
  558. comparisons_tbl <- tibble::tribble(
  559. ~pair_id, ~group1, ~group2, ~tbl_name,
  560. "1vs2", "AD.cl", "AD.eo", "EOvsCL_|_onlyAD",
  561. "3vs4", "NCI.cl", "NCI.eo", "EOvsCL_|_onlyNCI",
  562. "1vs3", "AD.cl", "NCI.cl", "ADvsNCI_|_onlyCL",
  563. "2vs4", "AD.eo", "NCI.eo", "ADvsNCI_|_onlyEO"
  564. )
  565. library(dplyr)
  566. library(purrr)
  567. pval_df <- purrr::map_dfr(seq_len(nrow(comparisons_tbl)), function(i) {
  568. comp <- comparisons_tbl[i, ]
  569. deg_tbl <- DEGdata[[ comp$tbl_name ]]
  570. deg_tbl %>%
  571. filter(Gene %in% long_data$Gene) %>%
  572. transmute(
  573. Gene = Gene,
  574. pair = comp$pair,
  575. group1 = comp$group1,
  576. group2 = comp$group2,
  577. p = coalesce(padj, 1) # NA -> 1
  578. )
  579. })
  580. # calculate y.position
  581. expr_range <- long_data %>%
  582. group_by(Gene) %>%
  583. summarise(
  584. min_expr = min(Expression, na.rm = TRUE),
  585. max_expr = max(Expression, na.rm = TRUE),
  586. .groups = "drop"
  587. )
  588. # combine p and y.position
  589. pval_df <- pval_df %>%
  590. left_join(expr_range, by = "Gene") %>%
  591. group_by(Gene) %>%
  592. mutate(
  593. offset_level = case_when(
  594. row_number() <= 2 ~ 1,
  595. row_number() == 3 ~ 2,
  596. TRUE ~ 3
  597. ),
  598. y.position = max_expr + (max_expr - min_expr) / 6 * offset_level,
  599. p.signif = cut(
  600. p,
  601. breaks = c(-Inf, 0.001, 0.01, 0.05, Inf),
  602. labels = c("***", "**", "*", "ns")
  603. )
  604. ) %>%
  605. ungroup() %>%
  606. select(-min_expr, -max_expr, -offset_level)
  607. df_lines <- long_data %>%
  608. filter(Group %in% c("AD.cl","AD.eo","NCI.cl","NCI.eo"))
  609. p <- ggplot(long_data,
  610. aes(x = Group, y = Expression, fill = Group)) +
  611. gghalves::geom_half_violin(position = position_nudge(x = -0.1)) +
  612. geom_boxplot(outlier.size = 0.1, width = 0.15) +
  613. geom_point(data = df_lines,
  614. aes(x = Group, y = Expression),
  615. position = position_nudge(x = 0.15),
  616. shape = 21,
  617. alpha = 0.7,
  618. inherit.aes = FALSE) +
  619. geom_line(data = df_lines,
  620. aes(x = Group, y = Expression, group = SubjectID),
  621. position = position_nudge(x = 0.15),
  622. color = "gray70",
  623. alpha = 0.6,
  624. inherit.aes = FALSE) +
  625. ggpubr::stat_pvalue_manual( data = pval_df,
  626. label = "p.signif",
  627. xmin = "group1",
  628. xmax = "group2",
  629. y.position = "y.position",
  630. bracket.size = 0.3,
  631. tip.length = 0.01,
  632. inherit.aes = FALSE ) +
  633. facet_wrap(~ Gene, nrow = 2, ncol = ceiling(length(genes)/2), scales="free_y") +
  634. theme_bw() +
  635. theme(legend.position = "none") +
  636. scale_fill_manual(values = c(
  637. "NCI.cl" = "skyblue",
  638. "NCI.eo" = "#E0F7FF",
  639. "AD.cl" = "#FF9999",
  640. "AD.eo" = "#FFF0F0"
  641. )) +
  642. scale_y_continuous(expand = expansion(mult = c(0.1, 0.1))) +
  643. labs(title = mytitle, x=NULL, y="Protein Expression Level (log2)")
  644. return(p)
  645. }
  646. EV_gene <- c("NCAM1","ATP1A3","CD63","CD81","CD9","L1CAM","PDCD6IP","TSG101")
  647. Violin_Protein_Expression_Facet(EV_gene, EV[["filtermiss"]], EV.DEG, "EVs Protein Expression Across Markers")
  648. ggsave("Figure2C_violin_EVs_markers_before_norm.pdf",width = 12, height = 7)
  649. Violin_Protein_Expression_Facet(EV_gene, EV[["norm"]], EV.DEG, "EVs Protein Expression Across Markers", log2 = F)
  650. ggsave("Figure2C_violin_EVs_markers_after_norm.pdf",width = 12, height = 7)
  651. Org_gene <- c("MAP2","TUBB3","MAPT","SYN1","GAP43","TPH1","NES","MAOA","HTR7","MAOB")
  652. Violin_Protein_Expression_Facet(Org_gene, Org[["filtermiss"]], Org.DEG, "Organoids Protein Expression Across Markers")
  653. ggsave("Figure2A_violin_Orgs_markers_before_norm.pdf",width = 12, height = 7)
  654. Violin_Protein_Expression_Facet(Org_gene, Org[["norm"]], Org.DEG, "Organoids Protein Expression Across Markers", log2 = F)
  655. ggsave("Figure2A_violin_Orgs_markers_after_norm.pdf",width = 12, height = 7)
  656. EV_relief <- EV.DEG[["treat_vs_marker"]]$Gene
  657. Violin_Protein_Expression_Facet(EV_relief[1:12], EV[["filtermiss"]], EV.DEG, "EVs Protein Expression Across DEPs")
  658. #ggsave("Interest_figure_EVs_relief_before_norm1.pdf",width = 12, height = 7)
  659. Violin_Protein_Expression_Facet(EV_relief[13:24], EV[["filtermiss"]], EV.DEG, "EVs Protein Expression Across DEPs")
  660. #ggsave("Interest_figure_EVs_relief_before_norm2.pdf",width = 12, height = 7)
  661. Org_relief <- Org.DEG[["treat_vs_marker"]]$Gene
  662. Violin_Protein_Expression_Facet(Org_relief[1:13], Org[["filtermiss"]], Org.DEG, "Orgs Protein Expression Across DEPs")
  663. #ggsave("Interest_figure_Orgs_reliefs_before_norm.pdf",width = 14, height = 7)
  664. # 12D) PCA plots based on the marker
  665. EO_pca_plot <- function(my.data, my.title = "PCA plot"
  666. ) {
  667. numeric_data <- my.data[, sapply(my.data, is.numeric), drop = FALSE]
  668. numeric_data <- na.omit(numeric_data)
  669. sample_names <- colnames(numeric_data)
  670. splitted <- strsplit(sample_names, "\\.")
  671. eo_vec <- sapply(splitted, function(x){x[4]})
  672. pca_res <- prcomp(t(numeric_data), center = TRUE, scale. = TRUE)
  673. pca_df <- as.data.frame(pca_res$x)
  674. group <- eo_vec
  675. group_factor <- factor(group)
  676. pc1 <- pca_df[,"PC1"]
  677. pc2 <- pca_df[,"PC2"]
  678. var_explained <- (pca_res$sdev^2 / sum(pca_res$sdev^2)) * 100
  679. df_pca <- data.frame(
  680. PC1 = pc1,
  681. PC2 = pc2,
  682. Group = group_factor
  683. )
  684. pc1_label <- paste0("PC1: ", sprintf("%.2f", var_explained[1]), "%")
  685. pc2_label <- paste0("PC2: ", sprintf("%.2f", var_explained[2]), "%")
  686. ggplot(df_pca, aes(x = PC1, y = PC2, color = Group)) +
  687. geom_point(size = 2) +
  688. labs(
  689. x = pc1_label,
  690. y = pc2_label,
  691. title = my.title
  692. ) +
  693. theme_minimal() +
  694. theme(
  695. legend.position = "right",
  696. plot.title = element_text(hjust = 0.5)
  697. ) +
  698. scale_color_manual(values = c("cl" = "#FCCDE5", "eo"="#B3DE69"))
  699. }
  700. temp <- Org[["norm"]][which(Org[["norm"]]$Gene %in% Org_gene),]
  701. EO_pca_plot(temp, "Org - Based on Ten Markers")
  702. ggsave("Figure2B_PCA_Org_with 10 Markers.pdf",width = 6, height = 6)
  703. temp <- EV[["norm"]][which(EV[["norm"]]$Gene %in% EV_gene),]
  704. EO_pca_plot(temp, "EV - Based on Eight Markers")
  705. ggsave("Figure2D_PCA_EV_with 8 Markers.pdf",width = 6, height = 6)
  706. # 13D) Heatmaps -------------------------------------------------------
  707. heatmap_plot <- function(my.data, show_protein_label = T, mytitle = "") {
  708. numeric_data <- my.data[, sapply(my.data, is.numeric), drop = FALSE]
  709. rownames(numeric_data) <- my.data$Gene
  710. sample_names <- colnames(numeric_data)
  711. splitted <- strsplit(sample_names, "\\.")
  712. diag_vec <- sapply(splitted, function(x) x[1])
  713. treat_vec <- sapply(splitted, function(x) {
  714. if (length(x) >= 4) x[4] else NA
  715. })
  716. diag_vec2 <- ifelse(diag_vec=="NCI","NCI","AD")
  717. sample_id <- sapply(splitted, function(x) x[3])
  718. sample_label <- paste0(diag_vec2,"-",sample_id)
  719. label_df <- data.frame(SampleName = sample_label)
  720. label_with_cli <- label_df %>%
  721. left_join(sample.clinical %>% select(SampleName, Age, Sex), by = "SampleName")
  722. sample_age <- label_with_cli$Age
  723. sample_Sex <- label_with_cli$Sex
  724. batch_vec <- sapply(splitted, function(x) {
  725. if (length(x) < 3) {
  726. "batch2"
  727. } else {
  728. num_part <- suppressWarnings(as.integer(x[3]))
  729. if (!is.na(num_part) && num_part %in% c(1, 2, 3)) "batch1" else "batch2"
  730. }
  731. })
  732. annotation_df <- data.frame(row.names = sample_names)
  733. annotation_df[["Diag"]] <- factor(diag_vec)
  734. annotation_df[["Treat"]] <- factor(treat_vec)
  735. annotation_df[["Batch"]] <- factor(batch_vec)
  736. annotation_df[["Age"]] <- sample_age
  737. annotation_df[["Sex"]] <- factor(sample_Sex)
  738. annotation_colors <- list(Diag = c("AD"= "#FF9999","ADNPS" = "#8B0000","NCI"= "skyblue"),
  739. Treat = c("cl" = "#FCCDE5", "eo"="#B3DE69"),
  740. Batch = c("batch1" = "#66c2a6", "batch2" = "#8ea0cb"),
  741. Sex = c("M"="#FB5012", "F"="#9590FF"))
  742. pheatmap(
  743. numeric_data,
  744. scale = "row",
  745. show_rownames = show_protein_label,
  746. show_colnames = TRUE,
  747. cluster_rows = TRUE,
  748. cluster_cols = T,
  749. annotation_col = if (ncol(annotation_df) > 0) annotation_df else NULL,
  750. annotation_colors = annotation_colors,
  751. main = mytitle,
  752. na_col = "grey"
  753. )
  754. }
  755. temp_data <- Org.DEGp005[["ADvsNCI_|_onlyCL"]]
  756. dev.off()
  757. pdf("Figure3B_heatmap_Untreated Organoids_AD vs NCI.pdf",width = 9, height = 7)
  758. heatmap_plot(temp_data[,c(1:3,7:ncol(temp_data))], mytitle="Untreated Organoids - AD vs NCI")
  759. dev.off()
  760. temp_data <- EV.DEGp005[["ADvsNCI_|_onlyCL"]]
  761. pdf("Figure3E_heatmap_Untreated EVs_AD vs NCI.pdf",width = 9, height = 7)
  762. heatmap_plot(temp_data[,c(1:3,7:ncol(temp_data))], mytitle="Untreated EVs - AD vs NCI")
  763. dev.off()
  764. temp_data <- Org.DEGp005[["EOvsCL"]]
  765. pdf("Figure4B_heatmap_All Organoids_EO vs CL.pdf",width = 14, height = 7)
  766. heatmap_plot(temp_data[,c(1:3,7:ncol(temp_data))], show_protein_label = F, mytitle="All Organoids - EO vs CL")
  767. dev.off()
  768. temp_data <- EV.DEGp005[["EOvsCL"]]
  769. pdf("Figure5B_heatmap_All EVs_EO vs CL.pdf",width = 14, height = 7)
  770. heatmap_plot(temp_data[,c(1:3,7:ncol(temp_data))], show_protein_label = F, mytitle="All EVs - EO vs CL")
  771. dev.off()
  772. # 13E) Annotated PCA for responder analysis ---------------------------
  773. labeled_diag_treat_pca_plot <- function(my.data, label_sample = "",my.title = "PCA plot"
  774. ) {
  775. numeric_data <- my.data[, sapply(my.data, is.numeric), drop = FALSE]
  776. numeric_data <- na.omit(numeric_data)
  777. sample_names <- colnames(numeric_data)
  778. splitted <- strsplit(sample_names, "\\.")
  779. diag_vec <- sapply(splitted, function(x) x[1])
  780. treat_vec <- sapply(splitted, function(x) if (length(x) >= 4) x[4] else NA)
  781. batch_vec <- sapply(splitted, function(x) {
  782. if (length(x) < 3) {
  783. return("batch2")
  784. }
  785. num_part <- suppressWarnings(as.integer(x[3]))
  786. if (!is.na(num_part) && num_part %in% c(1, 2, 3)) {
  787. "batch1"
  788. } else {
  789. "batch2"
  790. }
  791. })
  792. pca_res <- prcomp(t(numeric_data), center = TRUE, scale. = TRUE)
  793. pca_df <- as.data.frame(pca_res$x)
  794. group <- paste(diag_vec, treat_vec, sep = ".")
  795. group_factor <- factor(group)
  796. pc1 <- pca_df[,"PC1"]
  797. pc2 <- pca_df[,"PC2"]
  798. var_explained <- (pca_res$sdev^2 / sum(pca_res$sdev^2)) * 100
  799. df_pca <- data.frame(
  800. PC1 = pc1,
  801. PC2 = pc2,
  802. Group = group_factor,
  803. SampleName = sample_names
  804. )
  805. df_pca$SampleName <- ifelse(df_pca$SampleName %in% label_sample, df_pca$SampleName, NA)
  806. df_pca$SampleName <- sapply(df_pca$SampleName, function(x) {
  807. if (is.na(x)) return(NA)
  808. parts <- unlist(strsplit(x, "\\."))
  809. if (length(parts) >= 3) {
  810. group <- parts[1]
  811. id <- parts[3]
  812. subid <- if (length(parts) >= 5) parts[5] else NULL
  813. if (!is.null(subid)) {
  814. return(paste0(group, id, ".", subid))
  815. } else {
  816. return(paste0(group, id))
  817. }
  818. } else {
  819. return(x)
  820. }
  821. })
  822. pc1_label <- paste0("PC1: ", sprintf("%.2f", var_explained[1]), "%")
  823. pc2_label <- paste0("PC2: ", sprintf("%.2f", var_explained[2]), "%")
  824. ggplot(df_pca, aes(x = PC1, y = PC2)) +
  825. geom_point(aes(fill = Group), shape = 21, size = 3, stroke = 0.6, color = "gray30") +
  826. geom_text_repel(aes(label = SampleName), size = 3, max.overlaps = 100, color = "black") +
  827. labs(
  828. x = pc1_label,
  829. y = pc2_label,
  830. title = my.title
  831. ) +
  832. theme_minimal() +
  833. theme(
  834. legend.position = "right",
  835. plot.title = element_text(hjust = 0.5)
  836. ) +
  837. scale_fill_manual(values = c(
  838. "NCI.cl" = "skyblue",
  839. "NCI.eo" = "#E0F7FF",
  840. "AD.cl" = "#FF9999",
  841. "AD.eo" = "#FFF0F0",
  842. "ADNPS.cl" = "#8B0000",
  843. "ADNPS.eo" = "#F5CCCC"
  844. ))
  845. }
  846. mylabel_sample = c("ADNPS.Org.20.eo", "ADNPS.Org.19.eo", "AD.Org.18.eo", "AD.Org.17.eo",
  847. "NCI.Org.3.eo.2", "NCI.Org.3.eo.1", "NCI.Org.3.eo.3","NCI.Org.2.eo.1")
  848. temp_data <- Org.DEGp005[["EOvsCL"]]
  849. labeled_diag_treat_pca_plot(temp_data[,c(1:3,7:ncol(temp_data))], label_sample = mylabel_sample,
  850. my.title = "All Organoids − EO vs CL")
  851. ggsave("Figure4C_PCA_Org_EO_unresponsor.pdf",width = 8, height = 6)
  852. mylabel_sample = c("ADNPS.EV.21.eo", "ADNPS.EV.20.eo", "ADNPS.EV.19.eo", "AD.EV.18.eo", "AD.EV.17.eo")
  853. temp_data <- EV.DEGp005[["EOvsCL"]]
  854. labeled_diag_treat_pca_plot(temp_data[,c(1:3,7:ncol(temp_data))], label_sample = mylabel_sample,
  855. my.title = "All EVs − EO vs CL")
  856. ggsave("Figure5C_PCA_EV_EO_unresponsor.pdf",width = 8, height = 6)
  857. # 13F) Violin Plot for responder analysis ---------------------------
  858. Violin_Protein_Expression_Facet_unresponder <- function(genes,
  859. data,
  860. DEGdata,
  861. mytitle,
  862. log2 = TRUE) {
  863. # Get the data
  864. df_sub <- data[data$Gene %in% genes, ]
  865. long_data <- reshape2::melt(
  866. df_sub,
  867. id.vars = c("Gene", "Accession", "Modifications"),
  868. variable.name = "Sample",
  869. value.name = "Expression"
  870. )
  871. splitted <- strsplit(as.character(long_data$Sample), "\\.")
  872. diag_vec <- sapply(splitted, `[`, 1)
  873. diag_vec <- ifelse(diag_vec == "NCI", "NCI", "AD")
  874. treat_vec <- sapply(splitted, `[`, 4)
  875. long_data$Group <- paste0(diag_vec, ".", treat_vec)
  876. if(log2){long_data$Expression <- log2(long_data$Expression)}
  877. long_data$Group <- factor(long_data$Group, levels = sort(unique(long_data$Group)))
  878. long_data$SubjectID <- sapply(splitted, function(x) {paste0(x[1],".",x[3])})
  879. # Get the p value
  880. comparisons_tbl <- tibble::tribble(
  881. ~pair_id, ~group1, ~group2, ~tbl_name,
  882. "1vs2", "AD.cl", "AD.eo", "EOvsCL_|_onlyAD",
  883. "3vs4", "NCI.cl", "NCI.eo", "EOvsCL_|_onlyNCI",
  884. "1vs3", "AD.cl", "NCI.cl", "ADvsNCI_|_onlyCL",
  885. "2vs4", "AD.eo", "NCI.eo", "ADvsNCI_|_onlyEO"
  886. )
  887. pval_df <- purrr::map_dfr(seq_len(nrow(comparisons_tbl)), function(i) {
  888. comp <- comparisons_tbl[i, ]
  889. deg_tbl <- DEGdata[[ comp$tbl_name ]]
  890. deg_tbl %>%
  891. filter(Gene %in% long_data$Gene) %>%
  892. transmute(
  893. Gene = Gene,
  894. group1 = comp$group1,
  895. group2 = comp$group2,
  896. p = coalesce(padj, 1) # NA -> 1
  897. )
  898. })
  899. # calculate y.position
  900. expr_range <- long_data %>%
  901. group_by(Gene) %>%
  902. summarise(
  903. min_expr = min(Expression, na.rm = TRUE),
  904. max_expr = max(Expression, na.rm = TRUE),
  905. .groups = "drop"
  906. )
  907. # combine p and y.position
  908. pval_df <- pval_df %>%
  909. left_join(expr_range, by = "Gene") %>%
  910. group_by(Gene) %>%
  911. mutate(
  912. offset_level = case_when(
  913. row_number() <= 2 ~ 1,
  914. row_number() == 3 ~ 2,
  915. TRUE ~ 3
  916. ),
  917. y.position = max_expr + (max_expr - min_expr) / 6 * offset_level,
  918. p.signif = cut(
  919. p,
  920. breaks = c(-Inf, 0.001, 0.01, 0.05, Inf),
  921. labels = c("***", "**", "*", "ns")
  922. )
  923. ) %>%
  924. ungroup() %>%
  925. select(-min_expr, -max_expr, -offset_level)
  926. df_lines <- long_data %>%
  927. filter(Group %in% c("AD.cl","AD.eo","NCI.cl","NCI.eo"))
  928. highlight_df <- df_lines %>%
  929. filter(SubjectID %in% unresponder)
  930. p <- ggplot(long_data,
  931. aes(x = Group, y = Expression, fill = Group)) +
  932. gghalves::geom_half_violin(position = position_nudge(x = -0.1)) +
  933. geom_boxplot(outlier.size = 0.1, width = 0.15) +
  934. geom_point(data = df_lines,
  935. aes(x = Group, y = Expression),
  936. position = position_nudge(x = 0.15),
  937. shape = 21,
  938. alpha = 0.7,
  939. inherit.aes = FALSE) +
  940. geom_line(data = df_lines,
  941. aes(x = Group, y = Expression, group = SubjectID),
  942. position = position_nudge(x = 0.15),
  943. color = "gray70",
  944. alpha = 0.6,
  945. inherit.aes = FALSE) +
  946. ggpubr::stat_pvalue_manual( data = pval_df,
  947. label = "p.signif",
  948. xmin = "group1",
  949. xmax = "group2",
  950. y.position = "y.position",
  951. bracket.size = 0.3,
  952. tip.length = 0.01,
  953. inherit.aes = FALSE ) +
  954. geom_point(data = highlight_df,
  955. aes(x = Group, y = Expression),
  956. position = position_nudge(x = 0.15),
  957. shape = 21,
  958. fill = "darkred",
  959. color = "darkred",
  960. size = 2,
  961. inherit.aes = FALSE) +
  962. geom_line(
  963. data = highlight_df,
  964. aes(x = Group, y = Expression, group = SubjectID),
  965. position = position_nudge(x = 0.15),
  966. color = "darkred",
  967. size = 0.7,
  968. alpha = 0.8,
  969. inherit.aes = FALSE
  970. ) +
  971. facet_wrap(~ Gene, nrow = 2, ncol = ceiling(length(genes)/2), scales="free_y") +
  972. theme_bw() +
  973. theme(legend.position = "none") +
  974. scale_fill_manual(values = c(
  975. "NCI.cl" = "skyblue",
  976. "NCI.eo" = "#E0F7FF",
  977. "AD.cl" = "#FF9999",
  978. "AD.eo" = "#FFF0F0"
  979. )) +
  980. scale_y_continuous(expand = expansion(mult = c(0.1, 0.1))) +
  981. labs(title = mytitle, x=NULL, y="Protein Expression Level (log2)")
  982. return(p)
  983. }
  984. EV_relief <- EV.DEG[["treat_vs_marker"]]$Gene
  985. Violin_Protein_Expression_Facet_unresponder(EV_relief[1:12], EV[["filtermiss"]], EV.DEG, "EVs Protein Expression Across DEPs")
  986. ggsave("FigureX_violin_EVs_Unresponder_relief_before_norm1.pdf",width = 12, height = 7)
  987. Violin_Protein_Expression_Facet_unresponder(EV_relief[13:24], EV[["filtermiss"]], EV.DEG, "EVs Protein Expression Across DEPs")
  988. ggsave("FigureX_violin_EVs_Unresponder_relief_before_norm2.pdf",width = 12, height = 7)
  989. Org_relief <- Org.DEG[["treat_vs_marker"]]$Gene
  990. Violin_Protein_Expression_Facet_unresponder(Org_relief[1:13], Org[["filtermiss"]], Org.DEG, "Orgs Protein Expression Across DEPs")
  991. ggsave("FigureX_violin_Orgs_Unresponder_relief_before_norm.pdf",width = 14, height = 7)
  992. # 13G) Volcano Plot for responder analysis ---------------------------
  993. volcano_plot_unresponder <- function(deg_data = unrespond[["EV_DEG"]], titlename, p_thre = 0.05, log2fc_thre = 1){
  994. temp <- deg_data[,1:6]
  995. temp$select <- ifelse(temp$padj<p_thre & abs(temp$log2FC)>log2fc_thre,temp$Gene,NA)
  996. temp$group <- ifelse(temp$log2FC > 1 & temp$padj < 0.05,
  997. "Upregulated",
  998. ifelse(temp$log2FC < -1 & temp$padj < 0.05,
  999. "Downregulated",
  1000. "No Difference"))
  1001. temp <- temp[!is.na(temp$padj),]
  1002. ggplot(
  1003. temp, aes(x = log2FC, y = -log10(padj), color = factor(group))
  1004. ) +
  1005. geom_point(size=1.5) +
  1006. geom_label_repel(
  1007. color = "white",
  1008. arrow = arrow(length = unit(0.03, "npc"), type = "closed", ends = "first"),
  1009. point.padding = NA,
  1010. box.padding = 0.1,
  1011. aes(label = select, fill = group),
  1012. size = 2,
  1013. fontface = "bold",
  1014. max.overlaps=30
  1015. ) +
  1016. scale_fill_manual(
  1017. values = c("Upregulated" = "tomato", "Downregulated" = "skyblue", "No Difference" = "grey"),labels=NULL
  1018. ) +
  1019. scale_color_manual(
  1020. values = c("Upregulated" = "tomato", "Downregulated" = "skyblue", "No Difference" = "grey")
  1021. ) +
  1022. theme_bw() +
  1023. labs(x="log2(fold change)", y="-log10 (padj)", title = titlename) +
  1024. theme(
  1025. legend.position = "right",
  1026. panel.grid.major = element_blank(),
  1027. panel.grid.minor = element_blank(),
  1028. legend.title = element_blank()
  1029. ) +
  1030. geom_hline(yintercept = -log10(0.05),linetype=2,cex=0.5,color = "grey")+
  1031. geom_vline(xintercept = c(-1,1),linetype=2,cex=0.5,color = "grey")
  1032. }
  1033. volcano_plot_unresponder(unrespond[["EV_DEG"]], titlename = "EO Treated AD EV - Unresponder vs Responder")
  1034. ggsave("FigureX_Volcano_EV_Unresponder_DEanalysis.pdf", width=6, height = 6)
  1035. volcano_plot_unresponder(unrespond[["Org_DEG"]], titlename = "EO Treatd AD Organoid - Unresponder vs Responder")
  1036. ggsave("FigureX_Volcano_Org_Unresponder_DEanalysis.pdf", width=6, height = 6)
  1037. # 13H) Heatmap for responder analysis ---------------------------
  1038. dev.off()
  1039. pdf("FigureX_heatmap_EV_Unresponder vs Responder.pdf",width = 9, height = 7)
  1040. temp_data <- unrespond[["EV_DEGp005"]]
  1041. heatmap_plot(temp_data[,c(1:3,7:ncol(temp_data))], mytitle="Untreated EVs - Unresponder vs Responder", show_protein_label = F)
  1042. dev.off()
  1043. pdf("FigureX_heatmap_Org_Unresponder vs Responder.pdf",width = 9, height = 7)
  1044. temp_data <- unrespond[["Org_DEGp005"]]
  1045. heatmap_plot(temp_data[,c(1:3,7:ncol(temp_data))], mytitle="Untreated Organoids - Unresponder vs Responder", show_protein_label = F)
  1046. dev.off()

Bioinfo_Part2_AD vs NCI_EO vs CL.R at commit 7b45936, under MIT · at the source

Overview

Authors: Rachel J. Boyd1,2,3, Daiyun Dong1,2, Ram Sagar1,2, Anton Iliuk4, Waqar Ahmed1,2, Xenia Androni1,2, Anton P. Porsteinsson5, Paul B. Rosenberg2,6, Constantine G. Lyketsos2,6, Kenneth W. Witwer2,7,8, Vasiliki Mahairaki1,2
  1. Department of Genetic Medicine Johns Hopkins University School of Medicine Baltimore Maryland USA
  2. The Richman Family Precision Medicine Center of Excellence in Alzheimer's Disease Johns Hopkins University School of Medicine Baltimore Maryland USA
  3. Division of Geriatric Medicine and Gerontology, Johns Hopkins School of Medicine Baltimore Maryland USA
  4. Tymora Analytical Operations West Lafayette Indiana USA
  5. University of Rochester School of Medicine and Dentistry Rochester New York USA
  6. Department of Psychiatry and Behavioral Sciences Johns Hopkins University School of Medicine and Johns Hopkins Bayview Medical Center Baltimore Maryland USA
  7. Department of Molecular and Comparative Pathobiology Johns Hopkins University School of Medicine Baltimore Maryland USA
  8. Department of Neurology Johns Hopkins University School of Medicine Baltimore Maryland USA
Journal: Alzheimer's & dementia : the journal of the Alzheimer's Association, volume 22, issue 4, article e71273
Dates: received 26 November 2025; accepted 22 January 2026; published online 8 April 2026; in print April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1002/alz.71273 · PMID 41949026 · PMCID PMC13058922 · OpenAlex W7152064877
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: genetics / omics (modality), human (organism), Alzheimer's / dementia (population)
Methods: Statistics, Smoothing, state filtering, decompositions
Keywords: Alzheimer disease, clinical heterogeneity, escitalopram oxalate, extracellular vesicles, extracellular vesicle proteomics, induced pluripotent stem cells, proteomic profiling, serotonergic hindbrain organoids
MeSH: Alzheimer Disease*, Brain*, Extracellular Vesicles*, Organoids*, Proteomics*, Biomarkers, Humans, Induced Pluripotent Stem Cells, Selective Serotonin Reuptake Inhibitors, Treatment Effect Heterogeneity (* major topic)
Topic: Extracellular vesicles in disease (Molecular Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: NSF (1RF1AG083801); National Institute on Aging (AGR01054771, AGR01050515, AGR01046543, AGR01071522); Richman Family Precision Medicine Center of Excellence in Alzheimer’s Disease
Citations: cited by 1 paper (Europe PMC); 75 references in the paper

Abstract

INTRODUCTION: Alzheimer's disease (AD) exhibits high genetic and clinical heterogeneity that limits therapeutic success. Patient‐derived brain organoids and their extracellular vesicles (EVs) provide physiologically relevant models to study disease mechanisms and individualized drug responses.

METHODS: We generated the largest brain organoid cohort to date, derived from 30 independent induced pluripotent stem cell (iPSC) lines from AD and control individuals. Comparative proteomic profiling was performed on both organoids and their secreted EVs to capture molecular diversity and treatment effects.

RESULTS: Organoids and EVs consistently recapitulated neuronal proteomic signatures and revealed early alterations in AD‐related pathways, including synaptic and neurotransmitter dysfunction. Distinct proteomic responses mirrored individual variability in selective serotonin reuptake inhibitor sensitivity.

DISCUSSION: Integrating organoid and EV data provides a systems‐level view of AD pathophysiology and treatment response, positioning this dual‐platform model as a cost‐effective tool for precision medicine and drug discovery.

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.

daiyundong/Machairaki_2025_EV_Organoid_Proteomics

License: MIT
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 7b459365406d5ed0f3df7a65f95fae6187e7ebd7, 29 April 2026
Languages: R (2)
Size: 4 files, 2 scripts
Software Heritage: not archived
Found in: “DATA AVAILABILITY STATEMENT”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: ggplot2 (2 files), pheatmap (2 files), tidyverse (2 files), ggpubr (1 file), reshape2 (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
4 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;
  • 2 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

No dataset and no data link were found in the paper.

Data availability statement

The R scripts used for the analyses and visualizations is available at: https://github.com/daiyundong/Machairaki_2025_EV_Organoid_Proteomics

The website based on Shiny that allows readers to explore our data is available at: https://jtbuuy‐daiyun‐dong.shinyapps.io/organoid_proteomics_batch_correction_shiny/ (https://jtbuuy-daiyun-dong.shinyapps.io/organoid_proteomics_batch_correction_shiny/)

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

Recorded: type, language, journal, volume, issue, pages, dates, 11 authors, 8 keywords, 10 MeSH terms, 3 funders, 74 references.

Cite

This paper

Boyd, R. J., Dong, D., Sagar, R., Iliuk, A., Ahmed, W., Androni, X., Porsteinsson, A. P., Rosenberg, P. B., Lyketsos, C. G., Witwer, K. W., & Mahairaki, V. (2026). Proteomic profiling of brain organoids and extracellular vesicles identifies early Alzheimer's disease biomarkers and drug response heterogeneity. Alzheimer's & dementia : the journal of the Alzheimer's Association, 22(4), e71273. https://doi.org/10.1002/alz.71273

BibTeX

@article{boyd2026proteomic,
author = {Boyd, Rachel J. and Dong, Daiyun and Sagar, Ram and Iliuk, Anton and Ahmed, Waqar and Androni, Xenia and Porsteinsson, Anton P. and Rosenberg, Paul B. and Lyketsos, Constantine G. and Witwer, Kenneth W. and Mahairaki, Vasiliki},
title = {{Proteomic profiling of brain organoids and extracellular vesicles identifies early Alzheimer's disease biomarkers and drug response heterogeneity}},
journal = {Alzheimer's \& dementia : the journal of the Alzheimer's Association},
year = {2026},
month = apr,
volume = {22},
number = {4},
pages = {e71273},
publisher = {Wiley},
issn = {1552-5260},
doi = {10.1002/alz.71273},
url = {https://doi.org/10.1002/alz.71273},
pmid = {41949026},
pmcid = {PMC13058922}
}

RIS

TY - JOUR
AU - Boyd, Rachel J.
AU - Dong, Daiyun
AU - Sagar, Ram
AU - Iliuk, Anton
AU - Ahmed, Waqar
AU - Androni, Xenia
AU - Porsteinsson, Anton P.
AU - Rosenberg, Paul B.
AU - Lyketsos, Constantine G.
AU - Witwer, Kenneth W.
AU - Mahairaki, Vasiliki
TI - Proteomic profiling of brain organoids and extracellular vesicles identifies early Alzheimer's disease biomarkers and drug response heterogeneity
T2 - Alzheimer's & dementia : the journal of the Alzheimer's Association
J2 - Alzheimers Dement
PY - 2026
DA - 2026/04/01
VL - 22
IS - 4
SP - e71273
SN - 1552-5260
PB - Wiley
DO - 10.1002/alz.71273
UR - https://doi.org/10.1002/alz.71273
LA - en
ER -

CSL-JSON

{
"id": "10.1002/alz.71273",
"type": "article-journal",
"title": "Proteomic profiling of brain organoids and extracellular vesicles identifies early Alzheimer's disease biomarkers and drug response heterogeneity",
"container-title": "Alzheimer's & dementia : the journal of the Alzheimer's Association",
"author": [
{
"family": "Boyd",
"given": "Rachel J."
},
{
"family": "Dong",
"given": "Daiyun"
},
{
"family": "Sagar",
"given": "Ram"
},
{
"family": "Iliuk",
"given": "Anton"
},
{
"family": "Ahmed",
"given": "Waqar"
},
{
"family": "Androni",
"given": "Xenia"
},
{
"family": "Porsteinsson",
"given": "Anton P."
},
{
"family": "Rosenberg",
"given": "Paul B."
},
{
"family": "Lyketsos",
"given": "Constantine G."
},
{
"family": "Witwer",
"given": "Kenneth W."
},
{
"family": "Mahairaki",
"given": "Vasiliki"
}
],
"container-title-short": "Alzheimers Dement",
"volume": "22",
"issue": "4",
"page": "e71273",
"DOI": "10.1002/alz.71273",
"PMID": "41949026",
"PMCID": "PMC13058922",
"ISSN": "1552-5260",
"publisher": "Wiley",
"URL": "https://doi.org/10.1002/alz.71273",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
1
]
]
}
}

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.1002/adhm.202504889 [code]
Mapping the Cerebral Organoid Landscape: A Systematic Review of Preclinical 3D Models in Neuroscience.
Journal: Advanced healthcare materials
In common: 7 references
[2] doi:10.1172/jci.insight.207270 [code]
Progressive hypothalamic neuroinflammation in ovariectomized mice parallels aging-related transcriptomic changes in the female human hypothalamus.
Journal: JCI insight
In common: pheatmap, reshape2, ggpubr, 2 other tools, genetics / omics, 1 reference
[3] 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: pheatmap, reshape2, ggpubr, 2 other tools, Alzheimer's / dementia, 1 reference
[4] doi:10.1016/j.celrep.2026.117235 [code]
Integration of aged brain multi-omics reveals cross-system mechanisms underlying Alzheimer's disease heterogeneity.
Journal: Cell reports
In common: reshape2, ggpubr, ggplot2, 1 other tool, Alzheimer's / dementia, genetics / omics, 2 references
[5] doi:10.1038/s41593-026-02316-x [code]
Single-cell multi-omic atlas and morphogen screening informs midbrain and hindbrain organoid engineering.
Journal: Nature neuroscience
In common: reshape2, ggplot2, tidyverse, genetics / omics, 3 references
[6] doi:10.1093/bioinformatics/btag592 [code]
Network-based stratification of allele-specific expression reveals patient subgroups in Huntington's disease.
Journal: Bioinformatics (Oxford, England)
In common: pheatmap, reshape2, ggpubr, 2 other tools, genetics / omics, 1 reference
[7] doi:10.1073/pnas.2609132123 [code]
A human lysosomal storage disorder toolkit for decoding proteome landscapes in cortical-like and dopaminergic-like induced neurons.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: pheatmap, reshape2, ggpubr, 2 other tools, genetics / omics, 1 reference
[8] doi:10.1038/s41467-026-69944-6 [code]
Multi-modal dissection of cell-type specific TDP-43 pathology in the motor cortex.
Journal: Nature communications
In common: pheatmap, reshape2, ggpubr, 2 other tools, genetics / omics, 1 reference
[9] doi:10.1038/s41514-026-00443-0 [code]
Network-based discovery of regulatory drivers of cognitive decline in alzheimer's disease.
Journal: npj aging
In common: reshape2, ggpubr, ggplot2, 1 other tool, Alzheimer's / dementia, 2 references
[10] doi:10.3389/fbinf.2026.1816121 [code]
Transcriptome-informed metabolic modeling reveals astrocyte-specific vulnerabilities in mild cognitive impairment and Alzheimer's disease progression.
Journal: Frontiers in bioinformatics
In common: pheatmap, reshape2, ggplot2, 1 other tool, Alzheimer's / dementia, genetics / omics, 1 reference

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.