OSCR

A human iPSC-derived sensory neuron platform for high-throughput discovery of neuroprotectants against chemotherapy-induced peripheral neuropathy.

Code ↔ Paper

9 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 9 matches
  1. [1] § Results › Molecular profiling of paclitaxel-induced neurotoxicity ↔ rnaseq/plot_dge.Rmd, lines 49–77 · score 0.88 · TUBA1A, TUBB2B, TUBB4B, ACAT2, ACSS2, ASS1
  2. [2] § Results › Molecular profiling of paclitaxel-induced neurotoxicity ↔ gene_set_analysis_plotting.Rmd, lines 393–516 · score 0.82 · fatty acid metabolism, gap junctions, Rho GTPase, Golgi, trafficking, vesicle
  3. [3] § Results › Molecular profiling of paclitaxel-induced neurotoxicity ↔ gene_set_analysis_plotting.Rmd, lines 393–516 · score 0.64 · extracellular matrix, gene expression, JNK Inh, interactions, metabolism, Lipid
  4. [4] § STAR★Methods › Method details › Gene set enrichment analysis from RNA sequencing data ↔ gene_set_analysis_plotting.Rmd, lines 3148–3207 · score 0.57 · KEGG medicus, log2 fold, Hallmark, Reactome, enrichment, gene
  5. [5] § STAR★Methods › Method details › siRNA transfection and qPCR validation ↔ marker_expression.Rmd, lines 74–134 · score 0.56 · MAP4K6, MAP4K4, sensory neurons, TNIK
  6. [6] § Results › Molecular profiling of paclitaxel-induced neurotoxicity ↔ gene_set_analysis_plotting.Rmd, lines 759–883 · score 0.54 · fatty acid, cholesterol, apoptosis, metabolism, phospholipids, activation
  7. [7] § Results › Molecular profiling of paclitaxel-induced neurotoxicity ↔ gene_set_analysis_plotting.Rmd, lines 759–883 · score 0.54 · junction assembly, modification, GTPase, regulation, phosphoprotein, mediated
  8. [8] § STAR★Methods › Method details › Identification of differentially expressed genes ↔ rnaseq/deseq.Rmd, lines 868–939 · score 0.51 · sequencing batch, DESeq2, Wald, Salmon, gene
  9. [9] § Results › High-throughput screen of kinase inhibitors identified 19 neuroprotective compounds and three STE20 kinases as drivers of axon degeneration ↔ marker_expression.Rmd, lines 74–134 · score 0.51 · MAP4K6, MAP4K4, sensory neuron, TNIK

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 · 3,506 lines · 86 KB · no license · 5 matches

  1. ---
  2. title: "Peripheral neuropathy gene set analysis plotting"
  3. author: "Clemens Hug"
  4. output: html_document
  5. ---
  6. ```{r setup, include=FALSE}
  7. knitr::opts_chunk$set(echo = TRUE)
  8. library(here)
  9. library(tidyverse)
  10. library(data.table)
  11. library(synExtra)
  12. library(qs)
  13. library(powerjoin)
  14. library(plotly)
  15. library(ggrepel)
  16. synapser::synLogin()
  17. syn <- synDownloader(normalizePath("~/data"), .cache = TRUE)
  18. ```
  19. ## Reading GSE results
  20. ```{r}
  21. topgo_res_pert <- syn("syn64461453") %>%
  22. qread()
  23. fgsea_res_pert <- syn("syn64461445") %>%
  24. qread()
  25. fgsea_res_rescue <- syn("syn64461444") %>%
  26. qread()
  27. de_long_selected <- syn("syn70748503") %>%
  28. read_csv()
  29. fgsea_overrep <- syn("syn64582959") %>%
  30. qread()
  31. fgsea_overrep_single_treatments <- syn("syn71710650") %>%
  32. qread()
  33. fgsea_overrep_both_doses <- syn("syn71703313") %>%
  34. read_csv()
  35. fgsea_overrep_clusters <- syn("syn71761437") %>%
  36. qread()
  37. fgsea_intersection_overrep <- syn("syn64586672") %>%
  38. qread()
  39. indra_res <- syn("syn64908970") %>%
  40. read_csv()
  41. rag_ion_channel_genes <- syn("syn64745787") %>%
  42. readxl::read_excel() %>%
  43. pivot_longer(
  44. everything(),
  45. names_to = "gene_set",
  46. values_to = "gene_symbol_mouse"
  47. ) %>%
  48. drop_na()
  49. ptx_rescue_list <- syn("syn64608137") %>%
  50. read_csv()
  51. ptx_rescue_types <- syn("syn64608138") %>%
  52. read_csv()
  53. ```
  54. ```{r}
  55. database_abbreviations <- c(
  56. "REACTOME" = "R",
  57. "HALLMARK" = "H",
  58. "PID" = "P",
  59. "KEGG_MEDICUS" = "K",
  60. "GOBP" = "BP",
  61. "GOMF" = "MF",
  62. "GOCC" = "CC"
  63. )
  64. abbreviate_prefix <- function(strings, abbr_map = database_abbreviations) {
  65. # Create regex pattern from abbreviation names
  66. pattern <- paste0("^(", paste(names(abbr_map), collapse = "|"), ")_")
  67. # Replace matching prefixes with abbreviations
  68. str_replace(strings, pattern, function(x) {
  69. prefix <- str_remove(x, "_$")
  70. paste0(abbr_map[prefix], "_")
  71. })
  72. }
  73. recode_batch <- \(x) recode(x, batch_1 = "(3uM)", batch_2 = "(1uM)", batch_1_riki = "(3uM, Riki)")
  74. ```
  75. ## Plot Reactome PTX only and comparison with combination treatment
  76. ```{r}
  77. fgsea_res_single_treatments <- fgsea_res_pert %>%
  78. filter(
  79. metric == "signed_padj"
  80. ) %>%
  81. mutate(
  82. contrast_batch = paste(contrast, recode_batch(batch)),
  83. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  84. pathway = abbreviate_prefix(pathway)
  85. ) %>%
  86. filter(
  87. padj < 0.01
  88. ) %>%
  89. mutate(
  90. sign = if_else(NES > 0, "pos", "neg")
  91. )
  92. ps <- fgsea_res_single_treatments %>%
  93. group_nest(
  94. batch, contrast_batch, contrast
  95. ) %>%
  96. mutate(
  97. p = map2(
  98. contrast_batch, data,
  99. \(x, y) {
  100. y %>%
  101. mutate(
  102. pathway = fct_reorder(pathway, NES)
  103. ) %>%
  104. ggplot(
  105. aes(
  106. x = NES,
  107. y = pathway,
  108. fill = sign
  109. )
  110. ) +
  111. geom_col() +
  112. geom_text(
  113. aes(
  114. x = if_else(sign == "pos", -.05, .05),
  115. hjust = if_else(sign == "pos", 1, 0),
  116. label = pathway
  117. ),
  118. size = 2
  119. ) +
  120. scale_fill_discrete(guide = "none", direction = -1) +
  121. theme(
  122. axis.text.y = element_blank(),
  123. panel.grid.major.y = element_blank(),
  124. ) +
  125. labs(
  126. x = "Normalized enrichment score",
  127. y = NULL,
  128. title = paste0("Pathways enriched in ", x, " treatment"),
  129. subtitle = "FDR < 0.01"
  130. ) + {
  131. if (!any(y$sign == "neg"))
  132. lims(x = c(-max(abs(y$NES)), NA))
  133. }
  134. }
  135. )
  136. )
  137. dir.create(
  138. here("plots", "gene_set_analysis"),
  139. showWarnings = FALSE
  140. )
  141. pwalk(
  142. ps,
  143. \(contrast_batch, p, ...) {
  144. ggsave(
  145. here("plots", "gene_set_analysis", paste0("fgsea_single_perturbation_", contrast_batch, "_bars.pdf")),
  146. p, width = 8, height = 8
  147. )
  148. }
  149. )
  150. ps <- fgsea_res_single_treatments %>%
  151. filter(!str_starts(database, coll("GO"))) %>%
  152. group_nest(
  153. batch, contrast_batch, contrast
  154. ) %>%
  155. mutate(
  156. p = map2(
  157. contrast_batch, data,
  158. \(x, y) {
  159. y %>%
  160. mutate(
  161. pathway = fct_reorder(pathway, NES)
  162. ) %>%
  163. ggplot(
  164. aes(
  165. x = NES,
  166. y = pathway,
  167. fill = sign
  168. )
  169. ) +
  170. geom_col() +
  171. geom_text(
  172. aes(
  173. x = if_else(sign == "pos", -.05, .05),
  174. hjust = if_else(sign == "pos", 1, 0),
  175. label = pathway
  176. ),
  177. size = 2
  178. ) +
  179. scale_fill_discrete(guide = "none", direction = -1) +
  180. theme(
  181. axis.text.y = element_blank(),
  182. panel.grid.major.y = element_blank(),
  183. ) +
  184. labs(
  185. x = "Normalized enrichment score",
  186. y = NULL,
  187. title = paste0("Pathways enriched in ", x, " treatment"),
  188. subtitle = "FDR < 0.01"
  189. ) + {
  190. if (!any(y$sign == "neg"))
  191. lims(x = c(-max(abs(y$NES)), NA))
  192. }
  193. }
  194. )
  195. )
  196. pwalk(
  197. ps,
  198. \(contrast_batch, p, ...) {
  199. ggsave(
  200. here("plots", "gene_set_analysis", paste0("fgsea_single_perturbation_", contrast_batch, "_no_go_bars.pdf")),
  201. p, width = 8, height = 8
  202. )
  203. }
  204. )
  205. ```
  206. ### Compare batch 1 vs batch 2
  207. ```{r}
  208. fgsea_res_pert_vs <- fgsea_res_pert %>%
  209. mutate(
  210. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS)"),
  211. pathway = str_remove(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS)_"),
  212. sign = if_else(NES > 0, "pos", "neg"),
  213. signed_padj = -sign(NES) * log10(padj)
  214. ) %>%
  215. pivot_wider(
  216. names_from = batch,
  217. values_from = c(pval, padj, log2err, ES, NES, leadingEdge, sign, size, signed_padj)
  218. )
  219. p <- fgsea_res_pert_vs %>%
  220. ggplot(
  221. aes(
  222. x = signed_padj_batch1,
  223. y = signed_padj_batch2
  224. )
  225. ) +
  226. geom_point(
  227. shape = 16, alpha = .7
  228. ) +
  229. facet_grid(
  230. contrast ~ metric
  231. )
  232. ```
  233. ### Comparison with combination treatment
  234. ```{r}
  235. all_contrasts <- fgsea_res_pert$contrast %>%
  236. unique()
  237. contrast_plotting_pairs <- bind_rows(
  238. tibble(
  239. contrast_1 = "PTX",
  240. contrast_2 = all_contrasts[
  241. str_ends(all_contrasts, fixed("+ PTX")) |
  242. str_ends(all_contrasts, fixed("+ PTX vs PTX"))
  243. ]
  244. ),
  245. tibble(
  246. contrast_1 = "GNE-495 + PTX",
  247. contrast_2 = "JNK-inh + PTX"
  248. )
  249. )
  250. fgsea_res_ptx_vs_combo <- fgsea_res_pert %>%
  251. filter(
  252. metric == "signed_padj"
  253. ) %>%
  254. separate_wider_delim(
  255. pathway,
  256. delim = "_",
  257. names = c("database", "pathway"),
  258. too_many = "merge"
  259. ) %>%
  260. mutate(
  261. signed_padj = -sign(NES) * log10(padj)
  262. ) %>% {
  263. d <- .
  264. power_inner_join(
  265. contrast_plotting_pairs,
  266. d,
  267. by = join_by(contrast_1 == contrast),
  268. suffix = c("", "_1"),
  269. check = check_specs(
  270. unmatched_keys_left = "warn"
  271. )
  272. ) %>%
  273. power_inner_join(
  274. d,
  275. by = join_by(contrast_2 == contrast, metric, database, pathway, batch),
  276. suffix = c("_1", "_2"),
  277. check = check_specs(
  278. unmatched_keys_left = "warn"
  279. )
  280. )
  281. } %>%
  282. mutate(
  283. sig_class = case_when(
  284. padj_1 < .05 & padj_2 < .05 ~ "both",
  285. padj_1 < .05 ~ "1",
  286. padj_2 < .05 ~ "2",
  287. TRUE ~ "none"
  288. )
  289. )
  290. sig_class_colors <- c(
  291. "both" = "purple",
  292. "1" = "red",
  293. "2" = "blue",
  294. "none" = "grey"
  295. )
  296. ps <- fgsea_res_ptx_vs_combo %>%
  297. group_nest(batch, contrast_1, contrast_2) %>%
  298. mutate(
  299. p = pmap(
  300. .,
  301. \(contrast_1, contrast_2, data, ...) {
  302. ggplot(
  303. data %>%
  304. arrange(match(sig_class, names(sig_class_colors))),
  305. aes(
  306. x = signed_padj_1,
  307. y = signed_padj_2
  308. )
  309. ) +
  310. geom_point(
  311. aes(
  312. color = sig_class,
  313. text = pathway
  314. ),
  315. alpha = .8
  316. ) +
  317. scale_color_manual(
  318. name = "Significant\nenrichment\nFDR < 0.05",
  319. values = sig_class_colors,
  320. breaks = names(sig_class_colors),
  321. labels = c(
  322. "Both",
  323. contrast_1,
  324. contrast_2,
  325. "None"
  326. )
  327. ) +
  328. theme_minimal() +
  329. labs(
  330. x = paste("Enrichment", contrast_1),
  331. y = paste("Enrichment", contrast_2)
  332. )
  333. }
  334. )
  335. )
  336. pwalk(
  337. ps,
  338. \(batch, contrast_1, contrast_2, p, ...) {
  339. ggsave(
  340. here("plots", "gene_set_analysis", paste0("fgsea_single_perturbation_", contrast_1, "_vs_", contrast_2, "_", batch, "_scatter_padj.pdf")),
  341. p, width = 7, height = 6
  342. )
  343. htmlwidgets::saveWidget(
  344. ggplotly(p),
  345. here("plots", "gene_set_analysis", paste0("fgsea_single_perturbation_", contrast_1, "_vs_", contrast_2, "_", batch, "_scatter_padj.html")),
  346. selfcontained = FALSE
  347. )
  348. }
  349. )
  350. ```
  351. ### Highlighting certain pathways
  352. ```{r}
  353. highlighted_pathways <- tribble(
  354. ~contrast_1, ~contrast_2, ~pathways,
  355. "GNE-495 + PTX", "JNK-inh + PTX", c(
  356. "METABOLISM_OF_LIPIDS",
  357. "CHOLESTEROL_BIOSYNTHESIS",
  358. "ACTIVATION_OF_GENE_EXPRESSION_BY_SREBF_SREBP",
  359. "METABOLISM_OF_STEROIDS",
  360. "REGULATION_OF_CHOLESTEROL_BIOSYNTHESIS_BY_SREBP_SREBF",
  361. "FATTY_ACID_METABOLISM",
  362. "FATTY_ACYL_COA_BIOSYNTHESIS",
  363. "PHOSPHOLIPID_METABOLISM",
  364. "NEUTROPHIL_DEGRANULATION",
  365. "VESICLE_MEDIATED_TRANSPORT",
  366. "ER_TO_GOLGI_ANTEROGRADE_TRANSPORT",
  367. "CELL_CYCLE_MITOTIC",
  368. "RHO_GTPASE_EFFECTORS",
  369. "CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM",
  370. "EXTRACELLULAR_MATRIX_ORGANIZATION",
  371. "COPI_MEDIATED_ANTEROGRADE_TRANSPORT",
  372. "SYNDECAN_INTERACTIONS",
  373. ##
  374. "ACTIVATION_OF_GENE_EXPRESSION_BY_SREBF_SREBP",
  375. "FATTY_ACID_METABOLISM",
  376. "METABOLISM_OF_LIPIDS",
  377. "TRNA_AMINOACYLATION",
  378. "CELL_CYCLE",
  379. "REGULATION_OF_EXPRESSION_OF_SLITS_AND_ROBOS",
  380. "GAP_JUNCTION_TRAFFICKING_AND_REGULATION",
  381. "ER_TO_GOLGI_ANTEROGRADE_TRANSPORT",
  382. "COPI_MEDIATED_ANTEROGRADE_TRANSPORT"
  383. ) %>%
  384. unique()
  385. )
  386. ps <- fgsea_res_ptx_vs_combo %>%
  387. group_nest(contrast_1, contrast_2) %>%
  388. inner_join(
  389. highlighted_pathways,
  390. by = c("contrast_1", "contrast_2")
  391. ) %>%
  392. mutate(
  393. p = pmap(
  394. .,
  395. \(contrast_1, contrast_2, data, pathways, ...) {
  396. ggplot(
  397. data %>%
  398. arrange(match(sig_class, names(sig_class_colors))),
  399. aes(
  400. x = signed_padj_1,
  401. y = signed_padj_2
  402. )
  403. ) +
  404. geom_point(
  405. aes(
  406. color = sig_class,
  407. text = pathway
  408. ),
  409. alpha = .8
  410. ) +
  411. geom_text_repel(
  412. data = \(x) mutate(
  413. x,
  414. pathway_short = if_else(
  415. pathway %in% pathways,
  416. pathway,
  417. ""
  418. ) %>%
  419. str_replace_all(fixed("_"), " ") %>%
  420. str_wrap(20)
  421. ),
  422. aes(
  423. label = pathway_short
  424. ),
  425. size = 2,
  426. # force = 3,
  427. max.iter = 1e6,
  428. seed = 42,
  429. max.overlaps = Inf,
  430. min.segment.length = 0.3
  431. ) +
  432. scale_color_manual(
  433. name = "Significant\nenrichment\nFDR < 0.05",
  434. values = sig_class_colors,
  435. breaks = names(sig_class_colors),
  436. labels = c(
  437. "Both",
  438. contrast_1,
  439. contrast_2,
  440. "None"
  441. )
  442. ) +
  443. scale_x_continuous(
  444. expand = expansion(mult = c(.18, .05))
  445. ) +
  446. scale_y_continuous(
  447. expand = expansion(mult = c(.1, .05))
  448. ) +
  449. theme_minimal() +
  450. labs(
  451. x = paste("Enrichment", contrast_1),
  452. y = paste("Enrichment", contrast_2)
  453. )
  454. }
  455. )
  456. )
  457. pwalk(
  458. ps,
  459. \(contrast_1, contrast_2, p, ...) {
  460. ggsave(
  461. file.path("plots", paste0("reactome_", contrast_1, "_vs_", contrast_2, "_scatter_padj_highlighted.pdf")),
  462. p, width = 7.5, height = 6
  463. )
  464. # htmlwidgets::saveWidget(
  465. # ggplotly(p),
  466. # file.path("plots", paste0("reactome_", contrast_ptx, "_vs_", contrast_combo, "_scatter_padj.html"))
  467. # )
  468. }
  469. )
  470. ```
  471. ### Dotplot of single comparison FGSEA
  472. ```{r}
  473. fgsea_single_dot_data <- fgsea_res_pert %>%
  474. filter(
  475. metric == "signed_padj",
  476. batch != "batch_1_riki"
  477. ) %>%
  478. mutate(
  479. contrast_batch = paste(contrast, recode_batch(batch)),
  480. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  481. pathway = abbreviate_prefix(pathway)
  482. ) %>%
  483. semi_join(
  484. filter(
  485. .,
  486. padj < .01,
  487. contrast %in% c("PTX", "GNE-495 + PTX vs PTX", "JNK-inh + PTX vs PTX")
  488. ),
  489. by = "pathway"
  490. ) %>%
  491. mutate(
  492. padj_bin = cut(
  493. padj, breaks = c(-Inf, 0.001, 0.01, 0.05, Inf), labels = c("<0.001", "<0.01", "<0.05", ">=0.05"),
  494. ordered_result = TRUE
  495. )
  496. )
  497. fgsea_single_dot_data_filtered <- fgsea_single_dot_data %>%
  498. filter(
  499. contrast == "PTX" |
  500. str_detect(contrast, coll("vs PTX")),
  501. !str_starts(database, coll("GO"))
  502. ) %>%
  503. mutate(
  504. contrast_batch = paste(contrast, recode_batch(batch))
  505. ) %>%
  506. cluster_df(
  507. row_var = pathway,
  508. col_var = contrast_batch,
  509. value_var = NES
  510. )
  511. est_perc <- quantile(fgsea_single_dot_data_filtered$NES, c(.05, .95), na.rm = TRUE)
  512. abs_max_est_perc <- max(abs(est_perc))
  513. fgsea_single_dot_plot <- ggplot(
  514. fgsea_single_dot_data_filtered,
  515. aes(
  516. x = contrast,
  517. y = pathway,
  518. size = padj_bin,
  519. color = NES
  520. )
  521. ) +
  522. geom_point() +
  523. paletteer::scale_color_paletteer_c(
  524. "ggthemes::Orange-Blue-White Diverging",
  525. limits = c(-abs_max_est_perc, abs_max_est_perc),
  526. oob = scales::squish
  527. ) +
  528. scale_size_manual(
  529. values = c(
  530. "<0.001" = 5,
  531. "<0.01" = 4,
  532. "<0.05" = 3,
  533. ">=0.05" = 1
  534. )
  535. ) +
  536. facet_wrap(
  537. ~batch,
  538. axes = "all",
  539. axis.labels = "margins"
  540. ) +
  541. labs(
  542. x = "Contrast",
  543. y = "Pathway",
  544. color = "Normalized\nenrichment\nscore"
  545. ) +
  546. theme_minimal() +
  547. theme(
  548. axis.text.x = element_text(angle = 45, hjust = 1)
  549. )
  550. fgsea_single_dot_plot
  551. ggsave(
  552. file.path("plots", "fgsea_single_perturbation_dotplot.pdf"),
  553. fgsea_single_dot_plot,
  554. width = 10,
  555. height = 12,
  556. device = cairo_pdf
  557. )
  558. ```
  559. ## Heatmap of Reactome PTX and combo
  560. ```{r}
  561. library(seriation)
  562. cluster_df <- function(df, row_var, col_var, value_var, values_fill = 0) {
  563. # browser()
  564. mat <- df %>%
  565. select({{row_var}}, {{col_var}}, {{value_var}}) %>%
  566. pivot_wider(names_from = {{col_var}}, values_from = {{value_var}}, values_fill = values_fill) %>%
  567. column_to_rownames(rlang::as_name(rlang::enquo(row_var)))
  568. # browser()
  569. if (rlang::is_bare_numeric(pull(df, {{value_var}}))) {
  570. dist_rows <- dist(mat, method = "euclidian")
  571. dist_cols <- dist(t(mat), method = "euclidian")
  572. } else {
  573. # browser()
  574. dist_rows <- cluster::daisy(mat, metric = "gower")
  575. dist_cols <- t(mat) %>%
  576. as.data.frame() %>%
  577. mutate(across(everything(), \(x) factor(x, levels = levels(pull(df, {{value_var}}))))) %>%
  578. cluster::daisy(metric = "gower")
  579. }
  580. clust_rows <- hclust(dist_rows, method = "average") %>%
  581. reorder(dist_rows, method = "olo")
  582. clust_cols <- hclust(dist_cols, method = "average") %>%
  583. reorder(dist_cols, method = "olo")
  584. df %>%
  585. mutate(
  586. "{{row_var}}" := factor({{row_var}}, levels = clust_rows$labels[clust_rows$order]),
  587. "{{col_var}}" := factor({{col_var}}, levels = clust_cols$labels[clust_cols$order])
  588. )
  589. }
  590. cluster_fun_eucl <- function(mat, sample_in_col = TRUE) {
  591. # if (!sample_in_col) {
  592. # mat <- t(mat)
  593. # }
  594. # mat_imp <- impute.knn(
  595. # mat, rng.seed = 42
  596. # )[["data"]]
  597. # if (!sample_in_col) {
  598. # mat_imp <- t(mat_imp)
  599. # mat <- t(mat)
  600. # }
  601. # browser()
  602. dist_mat <- dist(mat)
  603. # dist_mat <- as.dist(mat)
  604. clust <- hclust(dist_mat, method = "average")
  605. reorder(clust, dist_mat, method = "OLO")
  606. }
  607. cluster_fun_binary <- function(mat, sample_in_col = TRUE) {
  608. # Convert logical/character to binary if needed
  609. if (is.logical(mat) || all(mat %in% c("TRUE", "FALSE", TRUE, FALSE))) {
  610. mat <- matrix(as.logical(mat), nrow = nrow(mat), ncol = ncol(mat))
  611. mat <- mat * 1 # Convert to 0/1
  612. }
  613. # Use binary distance (Jaccard is good for binary data)
  614. # method = "binary" uses Jaccard distance for binary data
  615. dist_mat <- dist(mat, method = "binary")
  616. clust <- hclust(dist_mat, method = "average")
  617. reorder(clust, dist_mat, method = "OLO")
  618. }
  619. ```
  620. ```{r}
  621. fgsea_hm_conditions <- c(
  622. "PTX", "GNE-495", "JNK-inh",
  623. "GNE-495 + PTX", "JNK-inh + PTX"
  624. )
  625. fgsea_res_pert_hm_data <- fgsea_res_pert %>%
  626. filter(
  627. metric == "signed_padj",
  628. contrast %in% fgsea_hm_conditions,
  629. !(contrast == "PTX" & batch == "batch_2"),
  630. batch != "batch_1_riki"
  631. ) %>%
  632. mutate(
  633. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  634. pathway = abbreviate_prefix(pathway),
  635. contrast_batch = paste(contrast, recode_batch(batch)),
  636. signed_padj = -sign(NES) * log10(padj)
  637. ) %>%
  638. filter(
  639. !str_starts(database, coll("GO"))
  640. ) %>%
  641. cluster_df(
  642. pathway, contrast_batch, signed_padj, values_fill = 0
  643. )
  644. fgsea_res_pert_mat <- fgsea_res_pert_hm_data %>%
  645. arrange(pathway, contrast_batch) %>%
  646. select(pathway, contrast_batch, signed_padj) %>%
  647. pivot_wider(names_from = contrast_batch, values_from = signed_padj, values_fill = 0) %>%
  648. column_to_rownames("pathway") %>%
  649. as.matrix()
  650. fgsea_res_pert_hm_pathways <- fgsea_res_pert_hm_data %>%
  651. filter(
  652. padj < 0.01
  653. ) %>%
  654. pull(pathway) %>%
  655. unique()
  656. ```
  657. ```{r}
  658. row_clust <- cluster_fun_eucl(
  659. fgsea_res_pert_mat[as.character(fgsea_res_pert_hm_pathways), ]
  660. )
  661. row_clust_df_long <- tibble(k = 4:10) %>%
  662. mutate(
  663. res = map(k, \(x) enframe(cutree(row_clust, k = x), name = "pathway", value = "cluster"))
  664. ) %>%
  665. unnest(res)
  666. row_clust_df <- row_clust_df_long %>%
  667. mutate(
  668. cluster = fct_inseq(as.character(cluster))
  669. ) %>%
  670. pivot_wider(
  671. names_from = k,
  672. values_from = cluster,
  673. names_prefix = "k_"
  674. )
  675. ```
  676. k = 8 seems to work well
  677. ```{r}
  678. dir.create(here("results"), showWarnings = FALSE)
  679. write_csv(
  680. row_clust_df_long %>%
  681. filter(k == 8),
  682. here("results", "reactome_pert_clusters_k8.csv")
  683. )
  684. ```
  685. ```{r}
  686. library(ComplexHeatmap)
  687. hm_contrast_batch_order <- fgsea_res_pert_hm_data %>%
  688. distinct(batch, contrast = factor(contrast, levels = fgsea_hm_conditions), contrast_batch) %>%
  689. arrange(contrast, batch)
  690. hm <- Heatmap(
  691. fgsea_res_pert_mat[as.character(fgsea_res_pert_hm_pathways), hm_contrast_batch_order$contrast_batch],
  692. cluster_rows = cluster_fun_eucl,
  693. cluster_columns = FALSE,
  694. show_row_names = FALSE,
  695. left_annotation = HeatmapAnnotation(
  696. df = column_to_rownames(row_clust_df, "pathway")[as.character(fgsea_res_pert_hm_pathways), ],
  697. which = "row"
  698. )
  699. )
  700. fgsea_picked_to_highlight <- c(
  701. "METABOLISM_OF_LIPIDS",
  702. "METABOLISM_OF_STEROIDS",
  703. "CHOLESTEROL_BIOSYNTHESIS",
  704. "ACTIVATION_OF_GENE_EXPRESSION_BY_SREBF_SREBP",
  705. "REGULATION_OF_CHOLESTEROL_BIOSYNTHESIS_BY_SREBP_SREBF",
  706. "REGULATION_OF_EXPRESSION_OF_SLITS_AND_ROBOS",
  707. "SIGNALING_BY_ROBO_RECEPTORS",
  708. "FATTY_ACID_METABOLISM",
  709. "FATTY_ACYL_COA_BIOSYNTHESIS",
  710. "PHOSPHOLIPID_METABOLISM",
  711. "GLYCEROPHOSPHOLIPID_BIOSYNTHESIS",
  712. "GAP_JUNCTION_ASSEMBLY",
  713. "KINESINS",
  714. "GAP_JUNCTION_TRAFFICKING_AND_REGULATION",
  715. "ORGANELLE_BIOGENESIS_AND_MAINTENANCE",
  716. "RHO_GTPASES_ACTIVATE_IQGAPS",
  717. "COPI_INDEPENDENT_GOLGI_TO_ER_RETROGRADE_TRAFFIC",
  718. "RHO_GTPASES_ACTIVATE_FORMINS",
  719. "HDACS_DEACETYLATE_HISTONES",
  720. "COPI_DEPENDENT_GOLGI_TO_ER_RETROGRADE_TRAFFIC",
  721. "GOLGI_TO_ER_RETROGRADE_TRANSPORT",
  722. "INTRA_GOLGI_AND_RETROGRADE_GOLGI_TO_ER_TRAFFIC",
  723. "SIGNALING_BY_RHO_GTPASES_MIRO_GTPASES_AND_RHOBTB3",
  724. "TRANSPORT_TO_THE_GOLGI_AND_SUBSEQUENT_MODIFICATION",
  725. "RHO_GTPASE_EFFECTORS",
  726. "APOPTOSIS",
  727. "ER_TO_GOLGI_ANTEROGRADE_TRANSPORT",
  728. "VESICLE_MEDIATED_TRANSPORT",
  729. "MEMBRANE_TRAFFICKING"
  730. )
  731. mat <- fgsea_res_pert_mat[as.character(fgsea_res_pert_hm_pathways), hm_contrast_batch_order$contrast_batch]
  732. mat_max_abs <- max(abs(quantile(mat, c(.025, .975), na.rm = TRUE)))
  733. hm <- Heatmap(
  734. mat,
  735. col = circlize::colorRamp2(seq(from = -mat_max_abs, to = mat_max_abs, length.out = 51), paletteer::paletteer_c("pals::ocean.balance", n = 51)),
  736. name = "Normalized enrichment score",
  737. cluster_rows = cluster_fun_eucl,
  738. cluster_columns = FALSE,
  739. show_row_names = FALSE,
  740. left_annotation = HeatmapAnnotation(
  741. df = row_clust_df %>%
  742. select(pathway, cluster = k_8) %>%
  743. slice(match(as.character(fgsea_res_pert_hm_pathways), pathway)) %>%
  744. column_to_rownames("pathway"),
  745. col = list(
  746. cluster = set_names(
  747. ggokabeito::palette_okabe_ito(1:8), 1:8
  748. )
  749. ),
  750. which = "row"
  751. ),
  752. right_annotation = HeatmapAnnotation(
  753. pathways = fgsea_picked_to_highlight %>%
  754. {
  755. anno_mark(
  756. at = match(., fgsea_res_pert_hm_pathways),
  757. labels = str_wrap(
  758. str_replace_all(., "_", " "),
  759. 20
  760. ),
  761. labels_gp = gpar(fontsize = 6),
  762. padding = unit(2, "mm")
  763. )
  764. },
  765. which = "row"
  766. )
  767. )
  768. withr::with_pdf(
  769. here("plots", "gene_set_analysis", "fgsea_single_perturbation_heatmap_nes_k8.pdf"),
  770. draw(hm),
  771. width = 6, height = 8
  772. )
  773. InteractiveComplexHeatmap::htShiny(draw(hm), save = here("plots/reactome_pert_heatmap_k8"))
  774. hm_with_names <- Heatmap(
  775. mat,
  776. col = circlize::colorRamp2(seq(from = -mat_max_abs, to = mat_max_abs, length.out = 51), paletteer::paletteer_c("pals::ocean.balance", n = 51)),
  777. name = "Normalized enrichment score",
  778. cluster_rows = cluster_fun_eucl,
  779. cluster_columns = FALSE,
  780. show_row_names = TRUE,
  781. row_names_gp = gpar(fontsize = 8),
  782. left_annotation = HeatmapAnnotation(
  783. df = row_clust_df %>%
  784. select(pathway, cluster = k_8) %>%
  785. slice(match(as.character(fgsea_res_pert_hm_pathways), pathway)) %>%
  786. column_to_rownames("pathway"),
  787. col = list(
  788. cluster = set_names(
  789. ggokabeito::palette_okabe_ito(1:8), 1:8
  790. )
  791. ),
  792. which = "row"
  793. )
  794. )
  795. withr::with_pdf(
  796. here("plots", "reactome_pert_heatmap_k8_with_names.pdf"),
  797. draw(hm_with_names),
  798. width = 9, height = 16
  799. )
  800. ```
  801. ## Plotting rescue vector FGSEA results
  802. ```{r}
  803. fgsea_res_rescue_data <- fgsea_res_rescue %>%
  804. filter(metric == "signed_padj_rescue_clamped") %>%
  805. separate_wider_delim(
  806. pathway,
  807. delim = "_",
  808. names = c("database", "pathway"),
  809. too_many = "merge"
  810. ) %>%
  811. mutate(
  812. sign = if_else(NES > 0, "pos", "neg"),
  813. pathway_db = paste(str_sub(database, 1, 1), pathway, sep = "_")
  814. )
  815. ps <- fgsea_res_rescue_data %>%
  816. group_nest(
  817. compound
  818. ) %>%
  819. mutate(
  820. p = map2(
  821. compound, data,
  822. \(x, y) {
  823. y %>%
  824. filter(
  825. padj < .05
  826. ) %>%
  827. mutate(
  828. across(pathway_db, \(x) fct_reorder(x, NES))
  829. ) %>%
  830. ggplot(
  831. aes(
  832. x = NES,
  833. y = pathway_db,
  834. fill = sign
  835. )
  836. ) +
  837. geom_col() +
  838. geom_text(
  839. aes(
  840. x = if_else(sign == "pos", -.05, .05),
  841. hjust = if_else(sign == "pos", 1, 0),
  842. label = pathway_db
  843. ),
  844. size = 2
  845. ) +
  846. scale_fill_discrete(guide = "none", direction = -1) +
  847. theme(
  848. axis.text.y = element_blank(),
  849. panel.grid.major.y = element_blank(),
  850. ) +
  851. labs(
  852. x = "Normalized enrichment score",
  853. y = NULL,
  854. title = paste0("Pathways enriched in genes rescued by ", x),
  855. subtitle = "FDR < 0.05"
  856. ) + {
  857. if (!any(y$sign == "neg"))
  858. lims(x = c(-max(abs(y$NES)), NA))
  859. }
  860. }
  861. )
  862. )
  863. pwalk(
  864. ps,
  865. \(compound, p, ...) {
  866. ggsave(
  867. file.path("plots", paste0("reactome_", compound, "_bars_rescue.pdf")),
  868. p, width = 8, height = 8
  869. )
  870. }
  871. )
  872. ```
  873. ### Comparing rescue vector enrichment between combos
  874. ```{r}
  875. fgsea_rescue_combo_vs_combo <- fgsea_res_rescue_data %>%
  876. mutate(
  877. signed_padj = -sign(NES) * log10(padj)
  878. ) %>%
  879. # filter(
  880. # database == "REACTOME"
  881. # ) %>%
  882. pivot_wider(
  883. names_from = compound,
  884. values_from = -c(metric, compound, pathway, database)
  885. ) %>%
  886. mutate(
  887. sig_class = case_when(
  888. `padj_GNE-495` < .05 & `padj_JNK-inh` < .05 ~ "both",
  889. `padj_GNE-495` < .05 ~ "GNE-495",
  890. `padj_JNK-inh` < .05 ~ "JNK-inh",
  891. TRUE ~ "none"
  892. )
  893. )
  894. sig_class_colors <- c(
  895. "both" = "purple",
  896. "GNE-495" = "red",
  897. "JNK-inh" = "blue",
  898. "none" = "grey"
  899. )
  900. p <- fgsea_rescue_combo_vs_combo %>%
  901. arrange(match(sig_class, names(sig_class_colors))) %>%
  902. ggplot(
  903. aes(
  904. x = `signed_padj_GNE-495`,
  905. y = `signed_padj_JNK-inh`
  906. )
  907. ) +
  908. geom_point(
  909. aes(
  910. color = sig_class,
  911. text = pathway
  912. ),
  913. alpha = .8
  914. ) +
  915. scale_color_manual(
  916. values = sig_class_colors
  917. ) +
  918. labs(
  919. x = paste("Enrichment GNE-495 rescue"),
  920. y = paste("Enrichment JNK-inh rescue")
  921. )
  922. ggsave(
  923. file.path("plots", paste0("reactome_rescue_gne_vs_jnk_scatter_padj.pdf")),
  924. p, width = 8, height = 8
  925. )
  926. htmlwidgets::saveWidget(
  927. ggplotly(p),
  928. file.path("plots", paste0("reactome_rescue_gne_vs_jnk_scatter_padj.html"))
  929. )
  930. ```
  931. ### Comparing rescue vector enrichment against PTX alone
  932. ```{r}
  933. fgsea_rescue_combo_vs_tx <- fgsea_res_rescue_data %>%
  934. mutate(
  935. signed_padj = -sign(NES) * log10(padj)
  936. ) %>%
  937. power_inner_join(
  938. fgsea_res_pert %>%
  939. filter(
  940. contrast == "PTX",
  941. metric == "signed_padj"
  942. ) %>%
  943. mutate(
  944. signed_padj = -sign(NES) * log10(padj)
  945. ) %>%
  946. separate_wider_delim(
  947. pathway,
  948. delim = "_",
  949. names = c("database", "pathway"),
  950. too_many = "merge"
  951. ),
  952. by = c("database", "pathway"),
  953. suffix = c("_rescue", "_PTX"),
  954. check = check_specs(
  955. unmatched_keys_left = "warn",
  956. unmatched_keys_right = "warn",
  957. duplicate_keys_right = "warn"
  958. )
  959. )
  960. p <- fgsea_rescue_combo_vs_tx %>%
  961. ggplot(
  962. aes(
  963. x = signed_padj_PTX,
  964. y = signed_padj_rescue
  965. )
  966. ) +
  967. geom_point(
  968. aes(
  969. text = pathway
  970. )
  971. ) +
  972. facet_wrap(~compound)
  973. htmlwidgets::saveWidget(
  974. ggplotly(p),
  975. file.path("plots", "reactome_rescue_vs_ptx_scatter_padj.html")
  976. )
  977. ```
  978. ## Plot LFC heatmap of genes in enriched pathways PTX vs combo
  979. ```{r}
  980. library(msigdbr)
  981. all_msigdbr <- msigdbr()
  982. msigdbr_of_interest <- all_msigdbr %>%
  983. filter(
  984. gs_collection %in% c("H") |
  985. gs_subcollection %in% c(
  986. "CP:PID",
  987. "CP:REACTOME",
  988. "CP:KEGG_MEDICUS",
  989. "GO:BP",
  990. "GO:MF",
  991. "GO:CC"
  992. ),
  993. !str_detect(gs_name, coll("MEDICUS_PATHOGEN")),
  994. !str_detect(gs_name, coll("MEDICUS_VARIANT"))
  995. ) %>%
  996. mutate(
  997. database = str_extract(gs_name, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  998. pathway = abbreviate_prefix(gs_name)
  999. )
  1000. msigdbr_of_interest_mat <- msigdbr_of_interest %>%
  1001. transmute(gs_name, gene_symbol, dummy = TRUE) %>%
  1002. distinct() %>%
  1003. pivot_wider(
  1004. names_from = gs_name,
  1005. values_from = dummy,
  1006. values_fill = FALSE
  1007. ) %>%
  1008. column_to_rownames("gene_symbol") %>%
  1009. as.matrix()
  1010. msigdbr_of_interest_sim <- msigdbr_of_interest_mat %>%
  1011. proxy::dist(method = "Jaccard", by_rows = FALSE)
  1012. qsave(
  1013. msigdbr_of_interest_sim,
  1014. here("results", "msigdbr_of_interest_jaccard_sim.qs")
  1015. )
  1016. ```
  1017. ```{r}
  1018. fgsea_hm_conditions <- c(
  1019. "PTX", "GNE-495", "JNK-inh",
  1020. "GNE-495 + PTX", "JNK-inh + PTX",
  1021. "GNE-495 + PTX vs PTX", "JNK-inh + PTX vs PTX"
  1022. )
  1023. de_pert_hm_data <- de_long_selected %>%
  1024. filter(
  1025. contrast %in% fgsea_hm_conditions
  1026. ) %>%
  1027. mutate(
  1028. signed_padj = -sign(logFC) * log10(FDR_min_zero),
  1029. contrast_batch = paste(contrast, recode_batch(batch))
  1030. )
  1031. fgsea_hm_order <- de_pert_hm_data %>%
  1032. distinct(
  1033. batch,
  1034. contrast = factor(contrast, levels = fgsea_hm_conditions),
  1035. contrast_batch
  1036. ) %>%
  1037. arrange(contrast, batch)
  1038. gene_id_gene_symbol_map <- de_pert_hm_data %>%
  1039. distinct(gene_id, gene_name) %>%
  1040. {with(., set_names(gene_name, gene_id))}
  1041. plot_pathway_heatmap <- function(pathway, value_col = signed_padj) {
  1042. gene_sets <- msigdbr_of_interest %>%
  1043. filter(.data$pathway == .env$pathway) %>%
  1044. pull(ensembl_gene) %>%
  1045. unique()
  1046. de_mat <- de_pert_hm_data %>%
  1047. filter(gene_id %in% gene_sets) %>%
  1048. select(gene_id, contrast_batch, {{value_col}}) %>%
  1049. pivot_wider(names_from = contrast_batch, values_from = {{value_col}}, values_fill = 0) %>%
  1050. column_to_rownames("gene_id") %>%
  1051. as.matrix() %>% {
  1052. .[, fgsea_hm_order$contrast_batch]
  1053. }
  1054. # row_meta <- fgsea_res_pert %>%
  1055. # filter(
  1056. # pathway == !!pathway,
  1057. # metric == "signed_padj"
  1058. # ) %>%
  1059. # select(compound, gene_id = leadingEdge) %>%
  1060. # unchop(gene_id) %>%
  1061. # mutate(
  1062. # gene_id = factor(gene_id, levels = rownames(de_mat)),
  1063. # decoy = 1L
  1064. # ) %>%
  1065. # pivot_wider(
  1066. # names_from = compound, values_from = decoy,
  1067. # names_prefix = "leading_edge_",
  1068. # id_expand = TRUE, values_fill = 0L
  1069. # ) %>%
  1070. # column_to_rownames("gene_id") %>% {
  1071. # .[rownames(de_mat), ]
  1072. # }
  1073. mat_max_abs <- max(abs(quantile(de_mat, c(.025, .975), na.rm = TRUE)))
  1074. hm <- Heatmap(
  1075. de_mat,
  1076. name = "Signed -log10 FDR",
  1077. col = circlize::colorRamp2(seq(from = -mat_max_abs, to = mat_max_abs, length.out = 51), paletteer::paletteer_c("pals::ocean.balance", n = 51)),
  1078. cluster_rows = cluster_fun_eucl,
  1079. cluster_columns = FALSE,
  1080. column_split = fgsea_hm_order$contrast,
  1081. row_labels = gene_id_gene_symbol_map[rownames(de_mat)],
  1082. # right_annotation = HeatmapAnnotation(
  1083. # df = row_meta,
  1084. # which = "row"
  1085. # )
  1086. )
  1087. return(hm)
  1088. }
  1089. plot_pathway_heatmap("R_P38MAPK_EVENTS")
  1090. library(ComplexHeatmap)
  1091. de_pert_rescue_hm_data <- tibble(
  1092. pathway = c(
  1093. "METABOLISM_OF_LIPIDS",
  1094. "RECYCLING_PATHWAY_OF_L1",
  1095. "REGULATION_OF_EXPRESSION_OF_SLITS_AND_ROBOS",
  1096. "SRP_DEPENDENT_COTRANSLATIONAL_PROTEIN_TARGETING_TO_MEMBRANE",
  1097. # "EPH_EPHRIN_SIGNALIN",
  1098. "EPHB_MEDIATED_FORWARD_SIGNALING",
  1099. "P38MAPK_EVENTS",
  1100. "RHO_GTPASE_EFFECTORS"
  1101. )
  1102. ) %>%
  1103. mutate(
  1104. genes = map(
  1105. pathway,
  1106. \(x) msigdbr_of_interest %>%
  1107. filter(
  1108. pathway == x
  1109. ) %>%
  1110. pull(ensembl_gene) %>%
  1111. unique()
  1112. ),
  1113. mat = map(
  1114. genes,
  1115. \(x) de_pert_hm_data %>%
  1116. filter(
  1117. gene_id %in% x
  1118. ) %>%
  1119. select(gene_id, contrast, logFC) %>%
  1120. pivot_wider(names_from = contrast, values_from = logFC, values_fill = 0) %>%
  1121. column_to_rownames("gene_id") %>%
  1122. as.matrix() %>% {
  1123. .[, fgsea_hm_conditions]
  1124. }
  1125. ),
  1126. row_meta = map2(
  1127. pathway, mat,
  1128. \(x, y) {
  1129. tibble(
  1130. gene_id = rownames(y)
  1131. ) %>%
  1132. fgsea_res_rescue_data %>%
  1133. filter(pathway == x) %>%
  1134. select(compound, gene_id = leadingEdge) %>%
  1135. unchop(gene_id) %>%
  1136. mutate(
  1137. gene_id = factor(gene_id, levels = rownames(y)),
  1138. decoy = 1L
  1139. ) %>%
  1140. pivot_wider(
  1141. names_from = compound, values_from = decoy,
  1142. names_prefix = "leading_edgge_",
  1143. id_expand = TRUE, values_fill = 0L
  1144. ) %>%
  1145. column_to_rownames("gene_id") %>% {
  1146. .[rownames(y), ]
  1147. }
  1148. }
  1149. ),
  1150. hm = pmap(
  1151. list(pathway, mat, row_meta),
  1152. \(x, y, z) {
  1153. mat_max_abs <- max(abs(quantile(y, c(.025, .975), na.rm = TRUE)))
  1154. Heatmap(
  1155. y,
  1156. name = "log2 fold change",
  1157. col = circlize::colorRamp2(seq(from = -mat_max_abs, to = mat_max_abs, length.out = 51), paletteer::paletteer_c("pals::ocean.balance", n = 51)),
  1158. cluster_rows = cluster_fun_eucl,
  1159. cluster_columns = FALSE,
  1160. right_annotation = HeatmapAnnotation(
  1161. df = z,
  1162. which = "row"
  1163. )
  1164. )
  1165. }
  1166. )
  1167. )
  1168. dir.create(
  1169. here("plots", "de_heatmaps"),
  1170. showWarnings = FALSE
  1171. )
  1172. pwalk(
  1173. de_pert_rescue_hm_data,
  1174. \(pathway, hm, ...) {
  1175. withr::with_pdf(
  1176. file.path("plots", "de_heatmaps", paste0("de_heatmap_", pathway, ".pdf")),
  1177. draw(hm),
  1178. width = 6, height = 10
  1179. )
  1180. }
  1181. )
  1182. ```
  1183. ## Overrepresentation analysis of rescue gene sets
  1184. ### Plotting single conditions as bars
  1185. ```{r}
  1186. bar_plot_colors <- c(
  1187. up = "#d73027",
  1188. down = "#4575b4"
  1189. )
  1190. fgsea_overrep_data <- fgsea_overrep %>%
  1191. mutate(
  1192. compound_batch = paste(compound, recode_batch(batch)),
  1193. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  1194. pathway = abbreviate_prefix(pathway)
  1195. ) %>%
  1196. mutate(
  1197. # sign = if_else(NES > 0, "pos", "neg"),
  1198. signed_padj = -if_else(perturbed == "up", 1, -1) * log10(padj),
  1199. pathway_direction = paste(perturbed, pathway, sep = "_")
  1200. )
  1201. ps <- fgsea_overrep_data %>%
  1202. group_nest(
  1203. perturbed_threshold, compound_batch, rescue_definition
  1204. ) %>%
  1205. mutate(
  1206. p = pmap(
  1207. list(compound_batch, data, rescue_definition),
  1208. \(x, y, z) {
  1209. y %>%
  1210. filter(
  1211. padj < .01
  1212. ) %>%
  1213. mutate(
  1214. across(pathway_direction, \(x) fct_reorder(x, signed_padj))
  1215. ) %>%
  1216. ggplot(
  1217. aes(
  1218. x = signed_padj,
  1219. y = pathway_direction,
  1220. fill = perturbed
  1221. )
  1222. ) +
  1223. geom_col() +
  1224. geom_text(
  1225. aes(
  1226. x = if_else(perturbed == "up", -.05, .05),
  1227. hjust = if_else(perturbed == "up", 1, 0),
  1228. label = pathway
  1229. ),
  1230. size = 2
  1231. ) +
  1232. scale_fill_manual(
  1233. values = alpha(bar_plot_colors, .7)
  1234. ) +
  1235. theme(
  1236. axis.text.y = element_blank(),
  1237. panel.grid.major.y = element_blank(),
  1238. ) +
  1239. labs(
  1240. x = "Signed adjusted p-value",
  1241. y = NULL,
  1242. title = paste0("Pathways enriched in genes ", z, " with ", x),
  1243. subtitle = "FDR < 0.01"
  1244. )
  1245. }
  1246. )
  1247. )
  1248. dir.create(
  1249. here("plots", "overrep"),
  1250. showWarnings = FALSE
  1251. )
  1252. pwalk(
  1253. ps,
  1254. \(compound_batch, p, rescue_definition, perturbed_threshold, ...) {
  1255. n <- nrow(layer_data(p))
  1256. h <- pmin(49, 2 + .1 * n)
  1257. ggsave(
  1258. here("plots", "gene_set_analysis", paste0("fgsea_single_rescue_", rescue_definition, "_", perturbed_threshold, "_", compound_batch, "_bars.pdf")),
  1259. p, width = 8, height = h
  1260. )
  1261. }
  1262. )
  1263. ```
  1264. ## vs PTX
  1265. ```{r}
  1266. fgsea_overrep_vs_ptx_pert <- fgsea_overrep %>%
  1267. filter(
  1268. rescue_definition != "perturbed"
  1269. ) %>%
  1270. mutate(
  1271. compound_batch = paste(compound, recode_batch(batch)),
  1272. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  1273. pathway = abbreviate_prefix(pathway)
  1274. ) %>%
  1275. mutate(
  1276. # sign = if_else(NES > 0, "pos", "neg"),
  1277. signed_padj = -if_else(perturbed == "up", 1, -1) * log10(padj),
  1278. pathway_direction = paste(perturbed, pathway, sep = "_")
  1279. ) %>%
  1280. power_inner_join(
  1281. fgsea_res_single_treatments %>%
  1282. filter(contrast == "PTX", metric == "signed_padj") %>%
  1283. mutate(
  1284. signed_padj = -if_else(NES > 0, 1, -1) * log10(padj)
  1285. ),
  1286. by = c("database", "pathway", "batch"),
  1287. suffix = c("_overrep", "_PTX"),
  1288. check = check_specs(
  1289. unmatched_keys_left = "info",
  1290. unmatched_keys_right = "warn",
  1291. duplicate_keys_right = "warn"
  1292. )
  1293. )
  1294. ps <- fgsea_overrep_vs_ptx_pert %>%
  1295. group_nest(
  1296. rescue_definition, perturbed_threshold
  1297. ) %>%
  1298. mutate(
  1299. p = map(
  1300. data,
  1301. \(d) {
  1302. ggplot(
  1303. d,
  1304. aes(
  1305. x = signed_padj_PTX,
  1306. y = signed_padj_overrep
  1307. )
  1308. ) +
  1309. geom_point(
  1310. aes(
  1311. text = pathway
  1312. )
  1313. ) +
  1314. facet_grid(
  1315. vars(compound), vars(batch)
  1316. )
  1317. }
  1318. )
  1319. )
  1320. pwalk(
  1321. ps,
  1322. \(rescue_definition, perturbed_threshold, p, ...) {
  1323. ggsave(
  1324. here("plots", "gene_set_analysis", paste0("fgsea_rescue_overrep_vs_ptx_fgsea_", rescue_definition, "_", perturbed_threshold, ".pdf")),
  1325. p, width = 10, height = 8
  1326. )
  1327. }
  1328. )
  1329. htmlwidgets::saveWidget(
  1330. ggplotly(p),
  1331. file.path("plots", "overrep", "overrep_vs_fgsea_ptx_scatter_padj.html")
  1332. )
  1333. ```
  1334. ## Plotting intersections of rescue gene sets
  1335. Have to be careful about perturbed `both_up_down` class, messes up signed_padj
  1336. ### intersections bar charts
  1337. ```{r}
  1338. fgsea_intersection_overrep_data <- fgsea_intersection_overrep %>%
  1339. mutate(
  1340. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  1341. pathway = abbreviate_prefix(pathway)
  1342. ) %>%
  1343. mutate(
  1344. # sign = if_else(NES > 0, "pos", "neg"),
  1345. signed_padj = -if_else(perturbed == "up", 1, -1) * log10(padj),
  1346. pathway_direction = paste(perturbed, pathway, sep = "_")
  1347. )
  1348. ps <- fgsea_intersection_overrep_data %>%
  1349. filter(perturbed %in% c("up", "down")) %>%
  1350. group_nest(
  1351. perturbed_threshold, batch, rescue_type, rescue_definition
  1352. ) %>%
  1353. mutate(
  1354. p = pmap(
  1355. list(rescue_type, rescue_definition, data),
  1356. \(x, y, z) {
  1357. d <- z %>%
  1358. filter(
  1359. padj < .01
  1360. ) %>%
  1361. mutate(
  1362. across(pathway_direction, \(x) fct_reorder(x, signed_padj))
  1363. )
  1364. ggplot(
  1365. d,
  1366. aes(
  1367. x = signed_padj,
  1368. y = pathway_direction,
  1369. fill = perturbed
  1370. )
  1371. ) +
  1372. geom_col() +
  1373. geom_text(
  1374. aes(
  1375. x = if_else(perturbed == "up", -.05, .05),
  1376. hjust = if_else(perturbed == "up", 1, 0),
  1377. label = pathway
  1378. ),
  1379. size = 2
  1380. ) +
  1381. scale_fill_manual(
  1382. values = alpha(bar_plot_colors, .7)
  1383. ) +
  1384. theme(
  1385. axis.text.y = element_blank(),
  1386. panel.grid.major.y = element_blank(),
  1387. ) + {
  1388. if (!any(d$perturbed == "down"))
  1389. lims(x = c(-max(abs(d$signed_padj)), NA))
  1390. } +
  1391. labs(
  1392. x = "Signed adjusted p-value",
  1393. y = NULL,
  1394. title = paste0("Pathways enriched in genes rescued by ", x),
  1395. subtitle = paste("FDR < 0.01", y)
  1396. )
  1397. }
  1398. )
  1399. )
  1400. pwalk(
  1401. ps,
  1402. \(perturbed_threshold, batch, rescue_definition, rescue_type, p, ...) {
  1403. ggsave(
  1404. here("plots", "gene_set_analysis", paste0("fgsea_rescue_overrep_bars_", rescue_definition, "_", rescue_type, "_", batch, "_", perturbed_threshold, ".pdf")),
  1405. p, width = 10, height = 8
  1406. )
  1407. }
  1408. )
  1409. ```
  1410. ### Dot plot
  1411. ```{r}
  1412. fgsea_intersection_dotplot_data <- fgsea_intersection_overrep_data %>%
  1413. mutate(
  1414. padj_bin = cut(
  1415. padj,
  1416. breaks = c(0, .01, .05, 1),
  1417. labels = c("FDR < 0.01", "0.01 < FDR < 0.05", "ns")
  1418. ),
  1419. estimate = log2(foldEnrichment)
  1420. ) %>%
  1421. filter(
  1422. rescue_type %in% c("GNE-495 only", "JNK-inh only"),
  1423. perturbed %in% c("up", "down")
  1424. ) %>%
  1425. # inner_join(
  1426. # fgsea_intersection_overrep_data_single,
  1427. # by = c("rescue_definition", "rescue_type", "pathway_db")
  1428. # ) %>%
  1429. # mutate(
  1430. # directed_estimate = if_else(direction == "up", estimate, -estimate)
  1431. # ) %>%
  1432. group_nest(
  1433. perturbed_threshold, batch, rescue_definition
  1434. ) %>%
  1435. mutate(
  1436. p = map(
  1437. data,
  1438. \(x) {
  1439. d <- group_by(
  1440. x,
  1441. pathway
  1442. ) %>%
  1443. filter(
  1444. any(padj < .01)
  1445. ) %>%
  1446. ungroup()
  1447. dmats <- d %>%
  1448. select(rescue_type, pathway, perturbed, estimate) %>%
  1449. arrange(pathway) %>%
  1450. split(.$perturbed) %>%
  1451. map(
  1452. \(y) select(y, -perturbed) %>%
  1453. pivot_wider(
  1454. names_from = rescue_type,
  1455. values_from = estimate
  1456. ) %>%
  1457. column_to_rownames("pathway") %>%
  1458. as.matrix() %>%
  1459. dist()
  1460. )
  1461. dmat <- reduce(dmats, `+`)
  1462. browser()
  1463. clust <- hclust(dmat, method = "average") %>%
  1464. reorder(dmat, method = "OLO")
  1465. # browser()
  1466. ggplot(
  1467. d %>%
  1468. mutate(
  1469. pathway = factor(pathway, levels = clust$labels[clust$order])
  1470. ),
  1471. aes(
  1472. x = rescue_type,
  1473. y = pathway,
  1474. color = estimate,
  1475. size = padj_bin
  1476. )
  1477. ) +
  1478. geom_point(shape = 16) +
  1479. scale_color_viridis_c(trans = "pseudo_log") +
  1480. scale_size_manual(
  1481. values = c("FDR < 0.01" = 4, "0.01 < FDR < 0.05" = 2, "ns" = 1)
  1482. ) +
  1483. # scale_color_distiller(palette = "RdBu") +
  1484. facet_wrap(~perturbed)
  1485. }
  1486. )
  1487. )
  1488. ```
  1489. Check what genes are in JNK-inh rescued mitosis related pathways
  1490. ```{r}
  1491. x <- fgsea_intersection_overrep_data %>%
  1492. arrange(p.value_fgsea) %>%
  1493. filter(
  1494. rescue_definition == "significant_opposite",
  1495. rescue_type == "JNK-inh only"
  1496. )
  1497. x %>%
  1498. filter(
  1499. pathway == "METALLOPROTEASE_DUBS"
  1500. ) %>%
  1501. chuck("overlapGenes", 1) %>%
  1502. {gene_id_gene_symbol_map[.]}
  1503. gene_id_gene_symbol_map[x$overlapGenes[[1]]]
  1504. ```
  1505. ```{r}
  1506. flag_similar_adjacent_leaves <- function(clust, sim_matrix, threshold) {
  1507. # Get the dendrogram representation
  1508. dend <- as.dendrogram(clust)
  1509. # Function to find adjacent leaves and check their similarity
  1510. find_adjacent_pairs <- function(node, parent_leaves = NULL) {
  1511. if (is.leaf(node)) {
  1512. # For leaf nodes, return the label
  1513. return(list(leaves = labels(node), pairs = data.frame()))
  1514. } else {
  1515. # For internal nodes, process left and right children
  1516. left_result <- find_adjacent_pairs(node[[1]], parent_leaves)
  1517. right_result <- find_adjacent_pairs(node[[2]], left_result$leaves)
  1518. # Combine leaves from both children
  1519. all_leaves <- c(left_result$leaves, right_result$leaves)
  1520. # Identify pairs of leaves that are adjacent across the left-right boundary
  1521. # (the rightmost leaf of left subtree and leftmost leaf of right subtree)
  1522. boundary_pairs <- data.frame(
  1523. leaf1 = tail(left_result$leaves, 1),
  1524. leaf2 = head(right_result$leaves, 1),
  1525. stringsAsFactors = FALSE
  1526. )
  1527. # Calculate similarity for these boundary pairs
  1528. if (nrow(boundary_pairs) > 0) {
  1529. boundary_pairs$similarity <- sim_matrix[boundary_pairs$leaf1, boundary_pairs$leaf2]
  1530. # Flag pairs below threshold
  1531. boundary_pairs <- boundary_pairs[boundary_pairs$similarity <= threshold, ]
  1532. }
  1533. # Combine all pairs found
  1534. all_pairs <- rbind(left_result$pairs, right_result$pairs, boundary_pairs)
  1535. return(list(leaves = all_leaves, pairs = all_pairs))
  1536. }
  1537. }
  1538. # Start the recursive search
  1539. result <- find_adjacent_pairs(dend)
  1540. return(result$pairs)
  1541. }
  1542. ```
  1543. ```{r}
  1544. msigdbr_of_interest_sim_trans <- msigdbr_of_interest_sim
  1545. attr(msigdbr_of_interest_sim_trans, "Labels") <- distinct(
  1546. msigdbr_of_interest, gs_name, database, pathway
  1547. ) %>%
  1548. transmute(
  1549. pathway_db = paste(str_sub(database, 1, 1), pathway, sep = "_"),
  1550. gs_name
  1551. ) %>%
  1552. slice(match(labels(msigdbr_of_interest_sim_trans), gs_name)) %>%
  1553. pull(pathway_db)
  1554. msigdbr_of_interest_sim_trans <- as.matrix(msigdbr_of_interest_sim_trans)
  1555. fgsea_intersection_overrep_data_single <- fgsea_intersection_overrep_data %>%
  1556. filter(
  1557. perturbed %in% c("up", "down")
  1558. ) %>%
  1559. mutate(
  1560. # Replacing -Inf with -1 for log2 fold enrichment
  1561. estimate = log2(if_else(foldEnrichment == 0, .5, foldEnrichment))
  1562. ) %>%
  1563. group_by(
  1564. perturbed_threshold, batch, rescue_definition, rescue_type, database, pathway
  1565. ) %>%
  1566. summarize(
  1567. direction = case_when(
  1568. all(padj >= .01) ~ "none",
  1569. padj[1] < .2 * padj[2] | padj[2] < .2 * padj[1] ~ perturbed[which.min(padj)],
  1570. TRUE ~ "both"
  1571. ),
  1572. estimate = estimate[order(padj)[1]],
  1573. padj = min(padj),
  1574. # estimate = estimate[which.min(padj)],
  1575. .groups = "drop"
  1576. )
  1577. fgsea_blacklist <- c(
  1578. "HCMV EARLY EVENTS",
  1579. "HCMV INFECTION",
  1580. "INFECTIOUS DISEASE"
  1581. )
  1582. fgsea_intersection_dotplot_data_single <- fgsea_intersection_overrep_data_single %>%
  1583. mutate(
  1584. padj_bin = cut(
  1585. padj,
  1586. breaks = c(0, .001, .01, 1),
  1587. labels = c("FDR < 0.001", "FDR < 0.01", "ns")
  1588. ),
  1589. # across(
  1590. # pathway_db,
  1591. # \(x) str_remove_all(
  1592. # x, coll("_")
  1593. # ) %>%
  1594. # str_sub(start = 3)
  1595. # )
  1596. ) %>%
  1597. filter(
  1598. !database %in% c("KEGG", "GOBP", "GOMF", "GOCC"),
  1599. rescue_type %in% c("GNE-495 only", "JNK-inh only"),
  1600. ) %>%
  1601. mutate(
  1602. directed_estimate = if_else(direction == "up", estimate, -estimate),
  1603. rescute_type_batch = paste(rescue_type, recode_batch(batch), sep = "_")
  1604. ) %>%
  1605. group_nest(
  1606. perturbed_threshold, rescue_definition
  1607. ) %>%
  1608. crossing(
  1609. p_threshold = c(0.005, 0.001, .01)
  1610. ) %>%
  1611. mutate(
  1612. res = map2(
  1613. data, p_threshold,
  1614. \(x, y) {
  1615. # browser()
  1616. d <- group_by(
  1617. x,
  1618. pathway
  1619. ) %>%
  1620. filter(
  1621. any(padj < y)
  1622. ) %>%
  1623. ungroup()
  1624. dwide <- d %>%
  1625. select(rescute_type_batch, pathway, directed_estimate) %>%
  1626. arrange(pathway) %>%
  1627. pivot_wider(
  1628. names_from = rescute_type_batch,
  1629. values_from = directed_estimate
  1630. )
  1631. dmat <- dwide %>%
  1632. column_to_rownames("pathway") %>%
  1633. as.matrix() %>%
  1634. dist()
  1635. # browser()
  1636. clust <- hclust(dmat, method = "average") %>%
  1637. reorder(dmat, method = "OLO")
  1638. row_labels <- clust$labels[clust$order] %>% {
  1639. set_names(
  1640. str_replace_all(., coll("_"), " ") %>%
  1641. str_sub(start = 2),
  1642. .
  1643. )
  1644. }
  1645. # sim_hm <- msigdbr_of_interest_sim_trans[
  1646. # rev(clust$labels[clust$order]), rev(clust$labels[clust$order])
  1647. # ] %>%
  1648. # pheatmap(
  1649. # cluster_rows = FALSE,
  1650. # cluster_cols = FALSE
  1651. # )
  1652. est_perc <- quantile(d$directed_estimate, c(.05, .95))
  1653. abs_max_est_perc <- max(abs(est_perc))
  1654. p <- ggplot(
  1655. d %>%
  1656. mutate(
  1657. pathway = factor(pathway, levels = clust$labels[clust$order])
  1658. ),
  1659. aes(
  1660. x = rescute_type_batch,
  1661. y = pathway,
  1662. color = directed_estimate,
  1663. size = padj_bin
  1664. )
  1665. ) +
  1666. geom_point(shape = 16) +
  1667. # scale_color_viridis_c(trans = "pseudo_log") +
  1668. # scale_color_distiller(
  1669. # limits = c(-abs_max_est_perc, abs_max_est_perc),
  1670. # palette = "RdYlBu", trans = "pseudo_log",
  1671. # oob = scales::squish
  1672. # ) +
  1673. paletteer::scale_color_paletteer_c(
  1674. palette = "pals::ocean.balance",
  1675. limits = c(-abs_max_est_perc, abs_max_est_perc),
  1676. oob = scales::squish
  1677. ) +
  1678. scale_size_manual(
  1679. values = rev(c("FDR < 0.001" = 6, "FDR < 0.01" = 4, "ns" = 2))
  1680. ) +
  1681. scale_x_discrete(position = "top") +
  1682. scale_y_discrete(
  1683. breaks = names(row_labels),
  1684. labels = unname(row_labels)
  1685. ) +
  1686. theme(
  1687. axis.text.x = element_text(angle = 45, hjust = 0)
  1688. ) +
  1689. labs(
  1690. x = "Rescue by",
  1691. y = NULL,
  1692. color = "Rescue of\ngenes",
  1693. size = NULL
  1694. )
  1695. list(
  1696. p = p
  1697. # sim_hm = sim_hm
  1698. ) %>%
  1699. map(list)
  1700. }
  1701. )
  1702. ) %>%
  1703. select(-data) %>%
  1704. unnest_wider(
  1705. res
  1706. )
  1707. flag_similar_adjacent_leaves(clust, msigdbr_of_interest_sim_trans, .6)
  1708. pwalk(
  1709. fgsea_intersection_dotplot_data_single,
  1710. \(p, rescue_definition, p_threshold, perturbed_threshold, ...) {
  1711. ggsave(
  1712. here("plots", "gene_set_analysis", paste0("fgsea_rescue_overrep_dotplot_", rescue_definition, "_", perturbed_threshold, "_", p_threshold, ".pdf")),
  1713. p[[1]], width = 9.8, height = 9
  1714. )
  1715. # withr::with_pdf(
  1716. # file.path("plots", "overrep", paste0("overrep_intersection_dotplot_", rescue_definition, "_", p_threshold, "_similarity.pdf")),
  1717. # draw(sim_hm[[1]]),
  1718. # width = 9.8, height = 9
  1719. # )
  1720. }
  1721. )
  1722. fgsea_intersection_dotplot_data_single <- fgsea_intersection_overrep_data_single %>%
  1723. mutate(
  1724. padj_bin = cut(
  1725. padj,
  1726. breaks = c(0, .01, .05, 1),
  1727. labels = c("FDR < 0.01", "FDR < 0.05", "ns")
  1728. ),
  1729. # across(
  1730. # pathway_db,
  1731. # \(x) str_remove_all(
  1732. # x, coll("_")
  1733. # ) %>%
  1734. # str_sub(start = 3)
  1735. # )
  1736. ) %>%
  1737. filter(
  1738. !database %in% c("KEGG", "GOBP", "GOMF", "GOCC"),
  1739. rescue_type %in% c("GNE-495 only", "JNK-inh only"),,
  1740. batch != "batch_1_riki"
  1741. ) %>%
  1742. mutate(
  1743. directed_estimate = if_else(direction == "up", estimate, -estimate),
  1744. rescute_type_batch = paste(rescue_type, recode_batch(batch), sep = "_")
  1745. ) %>%
  1746. group_nest(
  1747. perturbed_threshold, rescue_definition
  1748. ) %>%
  1749. crossing(
  1750. p_threshold = c(0.005, 0.001, .01)
  1751. ) %>%
  1752. mutate(
  1753. res = map2(
  1754. data, p_threshold,
  1755. possibly(\(x, y) {
  1756. # browser()
  1757. d <- group_by(
  1758. x,
  1759. pathway
  1760. ) %>%
  1761. filter(
  1762. any(padj < y)
  1763. ) %>%
  1764. ungroup()
  1765. dwide <- d %>%
  1766. select(rescute_type_batch, pathway, directed_estimate) %>%
  1767. arrange(pathway) %>%
  1768. pivot_wider(
  1769. names_from = rescute_type_batch,
  1770. values_from = directed_estimate
  1771. )
  1772. dmat <- dwide %>%
  1773. column_to_rownames("pathway") %>%
  1774. as.matrix() %>%
  1775. dist()
  1776. # browser()
  1777. clust <- hclust(dmat, method = "average") %>%
  1778. reorder(dmat, method = "OLO")
  1779. row_labels <- clust$labels[clust$order] %>% {
  1780. set_names(
  1781. str_replace_all(., coll("_"), " ") %>%
  1782. str_sub(start = 2),
  1783. .
  1784. )
  1785. }
  1786. # sim_hm <- msigdbr_of_interest_sim_trans[
  1787. # rev(clust$labels[clust$order]), rev(clust$labels[clust$order])
  1788. # ] %>%
  1789. # pheatmap(
  1790. # cluster_rows = FALSE,
  1791. # cluster_cols = FALSE
  1792. # )
  1793. est_perc <- quantile(d$directed_estimate, c(.05, .95))
  1794. abs_max_est_perc <- max(abs(est_perc))
  1795. p <- ggplot(
  1796. d %>%
  1797. mutate(
  1798. pathway = factor(pathway, levels = clust$labels[clust$order])
  1799. ),
  1800. aes(
  1801. x = rescute_type_batch,
  1802. y = pathway,
  1803. color = directed_estimate,
  1804. size = padj_bin
  1805. )
  1806. ) +
  1807. geom_point(shape = 16) +
  1808. # scale_color_viridis_c(trans = "pseudo_log") +
  1809. # scale_color_distiller(
  1810. # limits = c(-abs_max_est_perc, abs_max_est_perc),
  1811. # palette = "RdYlBu", trans = "pseudo_log",
  1812. # oob = scales::squish
  1813. # ) +
  1814. paletteer::scale_color_paletteer_c(
  1815. palette = "pals::ocean.balance",
  1816. limits = c(-abs_max_est_perc, abs_max_est_perc),
  1817. oob = scales::squish
  1818. ) +
  1819. scale_size_manual(
  1820. values = rev(c("FDR < 0.01" = 6, "FDR < 0.05" = 4, "ns" = 2))
  1821. ) +
  1822. scale_x_discrete(position = "top") +
  1823. scale_y_discrete(
  1824. breaks = names(row_labels),
  1825. labels = unname(row_labels)
  1826. ) +
  1827. theme(
  1828. axis.text.x = element_text(angle = 45, hjust = 0)
  1829. ) +
  1830. labs(
  1831. x = "Rescue by",
  1832. y = NULL,
  1833. color = "Rescue of\ngenes",
  1834. size = NULL
  1835. )
  1836. list(
  1837. p = p
  1838. # sim_hm = sim_hm
  1839. ) %>%
  1840. map(list)
  1841. })
  1842. )
  1843. ) %>%
  1844. select(-data) %>%
  1845. unnest_wider(
  1846. res
  1847. )
  1848. pwalk(
  1849. fgsea_intersection_dotplot_data_single %>%
  1850. filter(map_lgl(p, \(x) !is.null(x))),
  1851. \(p, rescue_definition, p_threshold, perturbed_threshold, ...) {
  1852. ggsave(
  1853. here("plots", "gene_set_analysis", paste0("fgsea_rescue_overrep_dotplot_batch1_2_", rescue_definition, "_", perturbed_threshold, "_", p_threshold, ".pdf")),
  1854. p[[1]], width = 9.8, height = 9
  1855. )
  1856. # withr::with_pdf(
  1857. # file.path("plots", "overrep", paste0("overrep_intersection_dotplot_", rescue_definition, "_", p_threshold, "_similarity.pdf")),
  1858. # draw(sim_hm[[1]]),
  1859. # width = 9.8, height = 9
  1860. # )
  1861. }
  1862. )
  1863. ```
  1864. ### intersections versus each other and vs PTX
  1865. ```{r}
  1866. fgsea_intersection_vs_ptx <- fgsea_intersection_overrep_data %>%
  1867. power_inner_join(
  1868. fgsea_res_pert %>%
  1869. separate_wider_delim(
  1870. pathway,
  1871. delim = "_",
  1872. names = c("database", "pathway"),
  1873. too_many = "merge"
  1874. ) %>%
  1875. mutate(
  1876. signed_padj = -sign(NES) * log10(padj)
  1877. ) %>%
  1878. filter(contrast == "PTX", metric == "signed_padj"),
  1879. by = c("database", "pathway"),
  1880. suffix = c("_rescue", "_ptx"),
  1881. check = check_specs(
  1882. unmatched_keys_left = "warn",
  1883. unmatched_keys_right = "warn",
  1884. duplicate_keys_right = "warn"
  1885. )
  1886. )
  1887. ps <- fgsea_intersection_vs_ptx %>%
  1888. filter(perturbed %in% c("up", "down")) %>%
  1889. group_nest(
  1890. rescue_definition
  1891. ) %>%
  1892. rowwise() %>%
  1893. mutate(
  1894. p = list(
  1895. ggplot(
  1896. data,
  1897. aes(
  1898. signed_padj_ptx,
  1899. signed_padj_rescue,
  1900. color = padj_fgsea < .05
  1901. )
  1902. ) +
  1903. geom_point(
  1904. aes(
  1905. text = paste(
  1906. pathway,
  1907. database,
  1908. "\n",
  1909. paste(
  1910. "padj rescue:", signif(padj_fgsea, 2),
  1911. "overlap:", overlap,
  1912. "gene set size:", size_ptx,
  1913. "odds ratio rescue:", signif(estimate, 2),
  1914. "padj ptx:", signif(padj, 2),
  1915. "NES:", signif(NES, 2)
  1916. )
  1917. )
  1918. ),
  1919. alpha = .5,
  1920. shape = 16
  1921. ) +
  1922. scale_color_manual(
  1923. values = c(
  1924. `TRUE` = "red",
  1925. `FALSE` = "black"
  1926. # up = "red",
  1927. # down = "blue",
  1928. # both_up_down = "black"
  1929. )
  1930. ) +
  1931. ggh4x::facet_wrap2(~rescue_type, scales = "free", axes = "y")
  1932. )
  1933. ) %>%
  1934. ungroup()
  1935. pwalk(
  1936. ps,
  1937. \(rescue_definition, p, ...) {
  1938. ggsave(
  1939. file.path("plots", "overrep", paste0("overrep_intersection_vs_ptx_", rescue_definition, ".pdf")),
  1940. p, width = 8, height = 8
  1941. )
  1942. htmlwidgets::saveWidget(
  1943. ggplotly(p),
  1944. file.path("plots", "overrep", paste0("overrep_intersection_vs_ptx_", rescue_definition, ".html"))
  1945. )
  1946. }
  1947. )
  1948. fgsea_intersection_overrep_gne_vs_jnk <- fgsea_intersection_overrep_data %>%
  1949. filter(
  1950. rescue_definition == "significant_opposite",
  1951. perturbed %in% c("up", "down"),
  1952. rescue_type %in% c("GNE-495 only", "JNK-inh only")
  1953. ) %>%
  1954. select(
  1955. perturbed, rescue_type, database, pathway, pathway_db, pathway_db_direction,
  1956. signed_padj, estimate
  1957. ) %>%
  1958. pivot_wider(
  1959. names_from = rescue_type,
  1960. values_from = c(signed_padj, estimate)
  1961. )
  1962. p <- fgsea_intersection_overrep_gne_vs_jnk %>%
  1963. ggplot(
  1964. aes(
  1965. x = `signed_padj_JNK-inh only`,
  1966. y = `signed_padj_GNE-495 only`,
  1967. text = pathway_db
  1968. )
  1969. ) +
  1970. geom_point()
  1971. p
  1972. plotly::ggplotly(p)
  1973. ```
  1974. ### Heatmap of intersection enrichments
  1975. ```{r}
  1976. intersection_overrep_hm_rescure_type_order <- c(
  1977. "both", "GNE-495 only", "JNK-inh only", "no rescue"
  1978. )
  1979. de_pert_hm_data <- de_long_selected %>%
  1980. filter(
  1981. contrast %in% fgsea_hm_conditions
  1982. ) %>%
  1983. mutate(
  1984. signed_padj = -sign(logFC) * log10(FDR_min_zero)
  1985. )
  1986. library(ComplexHeatmap)
  1987. de_pert_rescue_hm_data <- tibble(
  1988. pathway = c(
  1989. "RECYCLING_PATHWAY_OF_L1",
  1990. "REGULATION_OF_EXPRESSION_OF_SLITS_AND_ROBOS",
  1991. "SRP_DEPENDENT_COTRANSLATIONAL_PROTEIN_TARGETING_TO_MEMBRANE",
  1992. # "EPH_EPHRIN_SIGNALIN",
  1993. "EPHB_MEDIATED_FORWARD_SIGNALING",
  1994. "P38MAPK_EVENTS",
  1995. "RHO_GTPASE_EFFECTORS",
  1996. "RAC1_PATHWAY",
  1997. "RHO_GTPASES_ACTIVATE_ROCKS"
  1998. )
  1999. ) %>%
  2000. mutate(
  2001. genes = map(
  2002. pathway,
  2003. \(x) msigdbr_of_interest %>%
  2004. filter(
  2005. pathway == x
  2006. ) %>%
  2007. distinct(gene_symbol, ensembl_gene) %>%
  2008. semi_join(
  2009. de_pert_hm_data,
  2010. by = c("ensembl_gene" = "gene_id")
  2011. )
  2012. ),
  2013. mat = map(
  2014. genes,
  2015. \(x) de_pert_hm_data %>%
  2016. semi_join(
  2017. x,
  2018. by = c("gene_id" = "ensembl_gene")
  2019. ) %>%
  2020. select(gene_id, contrast, logFC) %>%
  2021. pivot_wider(names_from = contrast, values_from = logFC, values_fill = 0) %>%
  2022. column_to_rownames("gene_id") %>%
  2023. as.matrix() %>% {
  2024. .[x$ensembl_gene, fgsea_hm_conditions]
  2025. }
  2026. ),
  2027. row_meta = map2(
  2028. pathway, genes,
  2029. \(x, y) {
  2030. power_left_join(
  2031. y,
  2032. fgsea_res_rescue_data %>%
  2033. filter(pathway == x) %>%
  2034. select(compound, gene_id = leadingEdge) %>%
  2035. unchop(gene_id) %>%
  2036. mutate(decoy = 1L) %>%
  2037. pivot_wider(
  2038. names_from = compound, values_from = decoy,
  2039. names_prefix = "leading_edge_",
  2040. values_fill = 0L
  2041. ),
  2042. by = c("ensembl_gene" = "gene_id"),
  2043. check = check_specs(
  2044. unmatched_keys_right = "warn",
  2045. duplicate_keys_right = "warn",
  2046. duplicate_keys_left = "warn"
  2047. )
  2048. ) %>%
  2049. mutate(
  2050. across(
  2051. starts_with("leading_edge_"),
  2052. \(x) if_else(is.na(x), 0L, x)
  2053. )
  2054. )
  2055. }
  2056. ),
  2057. hm = pmap(
  2058. list(pathway, mat, row_meta, genes),
  2059. \(x, y, z, g) {
  2060. # browser()
  2061. mat_max_abs <- max(abs(quantile(y, c(.025, .975), na.rm = TRUE)))
  2062. Heatmap(
  2063. y,
  2064. name = "log2 fold change",
  2065. col = circlize::colorRamp2(seq(from = -mat_max_abs, to = mat_max_abs, length.out = 51), paletteer::paletteer_c("pals::ocean.balance", n = 51)),
  2066. cluster_rows = cluster_fun_eucl,
  2067. cluster_columns = FALSE,
  2068. row_labels = g$gene_symbol,
  2069. right_annotation = HeatmapAnnotation(
  2070. df = z %>%
  2071. select(ensembl_gene, starts_with("leading_edge_")) %>%
  2072. column_to_rownames("ensembl_gene") %>%
  2073. as.matrix(),
  2074. which = "row"
  2075. )
  2076. )
  2077. }
  2078. )
  2079. )
  2080. dir.create(
  2081. here("plots", "de_heatmaps"),
  2082. showWarnings = FALSE
  2083. )
  2084. pwalk(
  2085. de_pert_rescue_hm_data,
  2086. \(pathway, hm, ...) {
  2087. withr::with_pdf(
  2088. file.path("plots", "de_heatmaps", paste0("de_heatmap_", pathway, ".pdf")),
  2089. draw(hm),
  2090. width = 6, height = 10
  2091. )
  2092. }
  2093. )
  2094. ```
  2095. ## TopGO
  2096. check if sometimes the same pathway is enriched in both up and downregulated genes
  2097. ```{r}
  2098. topgo_res_pert %>%
  2099. pivot_wider(
  2100. names_from = direction,
  2101. values_from = -c(contrast, direction, ontology, GO.ID, Term, term_unique, Annotated)
  2102. ) %>%
  2103. filter(
  2104. fisher_double_down < .05,
  2105. fisher_double_up < .05
  2106. ) %>%
  2107. View()
  2108. ```
  2109. Make directed topgo results object by taking the log10 p-value
  2110. and the sign corresponds to the direction of the enrichment
  2111. If the same pathway is enriched in both directions, the pathway
  2112. occurs twice with different signs
  2113. ```{r}
  2114. topgo_res_pert_directed <- topgo_res_pert %>%
  2115. mutate(
  2116. log_odds_ratio = {Significant / Expected} %>%
  2117. if_else(
  2118. !is.finite(.) | . == 0,
  2119. sign(.) * min(.[. > 0]) * .5,
  2120. .
  2121. ) %>%
  2122. log10()
  2123. ) %>%
  2124. group_by(
  2125. contrast, ontology, GO.ID, Term, term_unique, Annotated
  2126. ) %>%
  2127. slice(
  2128. if (sum(fisher_double < .05) < 2) {
  2129. order(fisher_double)[1]
  2130. } else {
  2131. order(fisher_double)
  2132. }
  2133. ) %>%
  2134. mutate(
  2135. picked_direction = if (n() < 2) direction else "both",
  2136. signed_padj = -sign(log_odds_ratio) * log10(fisher_double)
  2137. ) %>%
  2138. ungroup()
  2139. ```
  2140. ```{r}
  2141. all_contrasts <- topgo_res_pert_directed$contrast %>%
  2142. unique()
  2143. contrast_plotting_pairs <- bind_rows(
  2144. tibble(
  2145. contrast_1 = "PTX",
  2146. contrast_2 = all_contrasts[
  2147. str_ends(all_contrasts, fixed("+ PTX")) |
  2148. str_ends(all_contrasts, fixed("+ PTX vs PTX"))
  2149. ]
  2150. ),
  2151. tibble(
  2152. contrast_1 = "GNE-495 + PTX",
  2153. contrast_2 = "JNK-inh + PTX"
  2154. )
  2155. )
  2156. ```
  2157. ```{r}
  2158. topgo_res_ptx_vs_combo <- topgo_res_pert_directed %>%
  2159. filter(
  2160. ontology == "BP"
  2161. ) %>% {
  2162. d <- .
  2163. power_inner_join(
  2164. contrast_plotting_pairs,
  2165. d,
  2166. by = join_by(contrast_1 == contrast),
  2167. suffix = c("", "_1"),
  2168. check = check_specs(
  2169. unmatched_keys_left = "warn"
  2170. )
  2171. ) %>%
  2172. power_inner_join(
  2173. d,
  2174. by = join_by(contrast_2 == contrast, ontology, GO.ID, Term, term_unique, Annotated),
  2175. suffix = c("_1", "_2"),
  2176. check = check_specs(
  2177. unmatched_keys_left = "warn"
  2178. )
  2179. )
  2180. } %>%
  2181. mutate(
  2182. sig_class = case_when(
  2183. fisher_double_1 < .05 & fisher_double_2 < .05 ~ "both",
  2184. fisher_double_1 < .05 ~ "1",
  2185. fisher_double_2 < .05 ~ "2",
  2186. TRUE ~ "none"
  2187. )
  2188. )
  2189. sig_class_colors <- c(
  2190. "both" = "purple",
  2191. "1" = "red",
  2192. "2" = "blue",
  2193. "none" = "grey"
  2194. )
  2195. ps <- topgo_res_ptx_vs_combo %>%
  2196. group_nest(contrast_1, contrast_2) %>%
  2197. mutate(
  2198. p = pmap(
  2199. .,
  2200. \(contrast_1, contrast_2, data, ...) {
  2201. ggplot(
  2202. data %>%
  2203. arrange(match(sig_class, names(sig_class_colors))),
  2204. aes(
  2205. x = signed_padj_1,
  2206. y = signed_padj_2
  2207. )
  2208. ) +
  2209. geom_point(
  2210. aes(
  2211. color = sig_class,
  2212. text = Term
  2213. ),
  2214. alpha = .8
  2215. ) +
  2216. scale_color_manual(
  2217. values = sig_class_colors
  2218. ) +
  2219. labs(
  2220. x = paste("Enrichment", contrast_1),
  2221. y = paste("Enrichment", contrast_2)
  2222. )
  2223. }
  2224. )
  2225. )
  2226. pwalk(
  2227. ps,
  2228. \(contrast_1, contrast_2, p, ...) {
  2229. ggsave(
  2230. file.path("plots", paste0("topgo_", contrast_1, "_vs_", contrast_2, "_scatter_padj.pdf")),
  2231. p, width = 8, height = 8
  2232. )
  2233. htmlwidgets::saveWidget(
  2234. ggplotly(p),
  2235. file.path("plots", paste0("topgo_", contrast_1, "_vs_", contrast_2, "_scatter_padj.html"))
  2236. )
  2237. }
  2238. )
  2239. ```
  2240. ## Create Excel table
  2241. 1. Differential expression
  2242. 2. FGSEA on raw perturbations
  2243. 3. FGSEA on rescue vectors
  2244. 4. Overrepresentation on rescue gene sets
  2245. 5. Overrepresentation on rescue gene set intersections
  2246. ```{r}
  2247. gene_id_symbol_map <- de_long_selected %>%
  2248. distinct(gene_id, gene_name)
  2249. gene_id_symbol_vec <- with(
  2250. gene_id_symbol_map,
  2251. set_names(gene_name, gene_id)
  2252. )
  2253. excel_de <- de_long_selected %>%
  2254. select(-FDR_min_zero)
  2255. excel_fgsea_raw <- fgsea_res_pert %>%
  2256. filter(metric == "signed_padj") %>%
  2257. separate_wider_delim(
  2258. pathway,
  2259. delim = "_",
  2260. names = c("database", "pathway"),
  2261. too_many = "merge"
  2262. ) %>%
  2263. select(-c(metric, log2err, ES)) %>%
  2264. mutate(
  2265. contrast = if_else(str_ends(contrast, fixed("vs PTX")), contrast, paste(contrast, "vs DMSO")),
  2266. leadingEdge = map_chr(
  2267. leadingEdge,
  2268. \(x) str_flatten(gene_id_symbol_vec[x], collapse = ",")
  2269. )
  2270. )
  2271. excel_fgsea_rescue <- fgsea_res_rescue %>%
  2272. filter(metric == "signed_padj_rescue_clamped") %>%
  2273. separate_wider_delim(
  2274. pathway,
  2275. delim = "_",
  2276. names = c("database", "pathway"),
  2277. too_many = "merge"
  2278. ) %>%
  2279. select(-c(metric, log2err, ES)) %>%
  2280. mutate(
  2281. leadingEdge = map_chr(
  2282. leadingEdge,
  2283. \(x) str_flatten(gene_id_symbol_vec[x], collapse = ",")
  2284. )
  2285. )
  2286. excel_overrep_rescue <- fgsea_overrep %>%
  2287. filter(rescue_definition == "significant_opposite") %>%
  2288. separate_wider_delim(
  2289. pathway,
  2290. delim = "_",
  2291. names = c("database", "pathway"),
  2292. too_many = "merge"
  2293. ) %>%
  2294. transmute(
  2295. compound, ptx_direction = perturbed,
  2296. database, pathway,
  2297. pval = p.value_fisher, padj = padj_fisher,
  2298. odds_ratio = estimate,
  2299. size, overlap,
  2300. leadingEdge = map_chr(
  2301. overlapGenes,
  2302. \(x) str_flatten(gene_id_symbol_vec[x], collapse = ",")
  2303. )
  2304. )
  2305. excel_overrep_intersections <- fgsea_intersection_overrep %>%
  2306. filter(rescue_definition == "significant_opposite") %>%
  2307. separate_wider_delim(
  2308. pathway,
  2309. delim = "_",
  2310. names = c("database", "pathway"),
  2311. too_many = "merge"
  2312. ) %>%
  2313. transmute(
  2314. rescue_type, ptx_direction = perturbed,
  2315. database, pathway,
  2316. pval = p.value_fisher, padj = padj_fisher,
  2317. odds_ratio = estimate,
  2318. size, overlap,
  2319. leadingEdge = map_chr(
  2320. overlapGenes,
  2321. \(x) str_flatten(gene_id_symbol_vec[x], collapse = ",")
  2322. )
  2323. )
  2324. ```
  2325. ```{r}
  2326. library(openxlsx)
  2327. openxlsx::write.xlsx(
  2328. list(
  2329. "Differential expression" = excel_de,
  2330. "FGSEA perturbations" = excel_fgsea_raw,
  2331. "FGSEA rescue vectors" = excel_fgsea_rescue,
  2332. "Overrep rescue gene sets" = excel_overrep_rescue,
  2333. "Overrep rescue intersections" = excel_overrep_intersections
  2334. ),
  2335. file = here("results", "gene_enrichment_analysis_results.xlsx"),
  2336. asTable = TRUE
  2337. )
  2338. ```
  2339. ## RAGs and ion channels
  2340. ```{r}
  2341. library(biomaRt)
  2342. # mouse = useMart("ensembl", dataset = "mmusculus_gene_ensembl")
  2343. human = useMart("ensembl", dataset = "hsapiens_gene_ensembl")
  2344. # Install packages if you haven't already
  2345. # install.packages("httr")
  2346. # install.packages("jsonlite")
  2347. library(httr)
  2348. library(jsonlite)
  2349. # Define a function to query mygene.info for a given gene symbol (or alias)
  2350. query_mygene <- function(query_term, scopes = "symbol,alias", species = "mouse", fields = "ensembl.gene") {
  2351. base_url <- "http://mygene.info/v3/query"
  2352. # Build the query parameters
  2353. params <- list(
  2354. q = query_term,
  2355. scopes = scopes,
  2356. species = species,
  2357. fields = fields
  2358. )
  2359. # Make the GET request
  2360. res <- POST(url = base_url, body = params, encode = "json")
  2361. # Check for a successful request (status code 200)
  2362. if (status_code(res) == 200) {
  2363. # Parse the JSON content
  2364. data <- fromJSON(content(res, as = "text", encoding = "UTF-8"))
  2365. return(data)
  2366. } else {
  2367. stop("Query failed with status: ", status_code(res))
  2368. }
  2369. }
  2370. mouse_gene_id_mapping <- query_mygene(
  2371. unique(rag_ion_channel_genes$gene_symbol_mouse)
  2372. ) %>%
  2373. transmute(
  2374. gene_symbol_mouse = query,
  2375. ensembl_gene_id_mouse = map(
  2376. ensembl,
  2377. \(x) if (class(x) == "list")
  2378. tibble(gene = x[[1]])
  2379. else
  2380. x
  2381. )
  2382. ) %>%
  2383. unnest(ensembl_gene_id_mouse)
  2384. mouse_homologue_mapping <- getHomologs(
  2385. unique(mouse_gene_id_mapping$gene),
  2386. "mus_musculus",
  2387. "homo_sapiens"
  2388. )
  2389. human_gene_id_symbol_map <- getBM(
  2390. attributes = c("ensembl_gene_id", "hgnc_symbol"),
  2391. filters = "ensembl_gene_id",
  2392. values = unique(na.omit(mouse_homologue_mapping$hsapiens_homolog_ensembl_gene)),
  2393. mart = human
  2394. )
  2395. rag_ion_channel_genes_mapped <- rag_ion_channel_genes %>%
  2396. left_join(
  2397. mouse_gene_id_mapping %>%
  2398. dplyr::distinct(gene_symbol_mouse, ensembl_gene_id_mouse = gene),
  2399. by = "gene_symbol_mouse"
  2400. ) %>%
  2401. left_join(
  2402. mouse_homologue_mapping %>%
  2403. dplyr::select(
  2404. ensembl_gene_id_mouse = ensembl_gene_id,
  2405. ensembl_gene_id_human = hsapiens_homolog_ensembl_gene
  2406. ),
  2407. by = "ensembl_gene_id_mouse"
  2408. ) %>%
  2409. left_join(
  2410. human_gene_id_symbol_map %>%
  2411. dplyr::select(
  2412. ensembl_gene_id_human = ensembl_gene_id,
  2413. hgnc_symbol
  2414. ),
  2415. by = "ensembl_gene_id_human"
  2416. ) %>%
  2417. mutate(
  2418. gene_display_name = coalesce(
  2419. hgnc_symbol,
  2420. gene_symbol_mouse,
  2421. ensembl_gene_id_human,
  2422. ensembl_gene_id_mouse
  2423. )
  2424. ) %>%
  2425. group_by(gene_display_name) %>%
  2426. mutate(
  2427. unique_gene_id = if (n() > 1)
  2428. paste0(gene_display_name, "-", seq_len(n()))
  2429. else
  2430. gene_display_name
  2431. ) %>%
  2432. ungroup()
  2433. ```
  2434. ```{r}
  2435. rag_hm_conditions <- c(
  2436. "PTX", "GNE-495", "JNK-inh",
  2437. "GNE-495 + PTX", "JNK-inh + PTX"
  2438. )
  2439. rag_ion_channel_genes_valid <- rag_ion_channel_genes_mapped %>%
  2440. drop_na(
  2441. ensembl_gene_id_human
  2442. ) %>%
  2443. filter(ensembl_gene_id_human %in% de_long_selected$gene_id)
  2444. de_rag_hm_data <- de_long_selected %>%
  2445. filter(
  2446. contrast %in% rag_hm_conditions
  2447. ) %>%
  2448. inner_join(
  2449. rag_ion_channel_genes_valid,
  2450. by = c("gene_id" = "ensembl_gene_id_human")
  2451. ) %>%
  2452. mutate(
  2453. signed_padj = -sign(logFC) * log10(FDR_min_zero)
  2454. )
  2455. de_rag_hm_mat <- de_rag_hm_data %>%
  2456. dplyr::select(
  2457. unique_gene_id, contrast, logFC
  2458. ) %>%
  2459. pivot_wider(names_from = contrast, values_from = logFC) %>%
  2460. column_to_rownames("unique_gene_id") %>%
  2461. as.matrix() %>% {
  2462. .[
  2463. rag_ion_channel_genes_valid$unique_gene_id,
  2464. rag_hm_conditions
  2465. ]
  2466. }
  2467. library(ComplexHeatmap)
  2468. hm <- Heatmap(
  2469. de_rag_hm_mat,
  2470. # t() %>%
  2471. # scale() %>%
  2472. # t(),
  2473. cluster_rows = cluster_fun_eucl,
  2474. cluster_columns = FALSE,
  2475. show_row_names = TRUE,
  2476. row_split = rag_ion_channel_genes_valid$gene_set
  2477. )
  2478. withr::with_pdf(
  2479. file.path("plots", "de_heatmaps", "de_heatmap_rag_ion_channels_lfc.pdf"),
  2480. draw(hm),
  2481. width = 6, height = 10
  2482. )
  2483. de_rag_hm_mat <- de_rag_hm_data %>%
  2484. dplyr::select(
  2485. unique_gene_id, contrast, signed_padj
  2486. ) %>%
  2487. pivot_wider(names_from = contrast, values_from = signed_padj) %>%
  2488. column_to_rownames("unique_gene_id") %>%
  2489. as.matrix() %>% {
  2490. .[
  2491. rag_ion_channel_genes_valid$unique_gene_id,
  2492. rag_hm_conditions
  2493. ]
  2494. }
  2495. library(ComplexHeatmap)
  2496. hm <- Heatmap(
  2497. # de_rag_hm_mat,
  2498. # Clamp to -10 to 10
  2499. de_rag_hm_mat %>% {
  2500. pmax(pmin(., 10), -10)
  2501. },
  2502. name = "-sign(log2FC) * log10(padj)",
  2503. # t() %>%
  2504. # scale() %>%
  2505. # t(),
  2506. col = circlize::colorRamp2(seq(-10, 10, length.out = 51), paletteer::paletteer_c("pals::ocean.balance", n = 51)),
  2507. cluster_rows = cluster_fun_eucl,
  2508. cluster_columns = FALSE,
  2509. show_row_names = FALSE,
  2510. row_split = rag_ion_channel_genes_valid$gene_set
  2511. )
  2512. withr::with_pdf(
  2513. file.path("plots", "de_heatmaps", "de_heatmap_rag_ion_channels_padj.pdf"),
  2514. draw(hm),
  2515. width = 6, height = 10
  2516. )
  2517. hm <- Heatmap(
  2518. de_rag_hm_mat,
  2519. # Clamp to -10 to 10
  2520. # de_rag_hm_mat %>% {
  2521. # pmax(pmin(., 10), -10)
  2522. # },
  2523. name = "-sign(log2FC) * log10(padj)",
  2524. # t() %>%
  2525. # scale() %>%
  2526. # t(),
  2527. col = circlize::colorRamp2(seq(-10, 10, length.out = 51), paletteer::paletteer_c("pals::ocean.balance", n = 51)),
  2528. cluster_rows = cluster_fun_eucl,
  2529. cluster_columns = FALSE,
  2530. show_row_names = FALSE,
  2531. row_split = rag_ion_channel_genes_valid$gene_set
  2532. )
  2533. withr::with_pdf(
  2534. file.path("plots", "de_heatmaps", "de_heatmap_rag_ion_channels_padj2.pdf"),
  2535. draw(hm),
  2536. width = 6, height = 10
  2537. )
  2538. ```
  2539. Overlap of RAGs and ion channels with GNE-495-only rescue genes
  2540. ```{r}
  2541. ```
  2542. ### INDRA plots
  2543. ```{r}
  2544. idnra_res_plot_data <- indra_res %>%
  2545. filter(
  2546. rescue_type %in% c(
  2547. "GNE-495 only", "JNK-inh only"
  2548. ),
  2549. str_starts(query_type, fixed("indra"))
  2550. ) %>%
  2551. mutate(
  2552. signed_q = if_else(
  2553. perturbed == "down",
  2554. 1, -1
  2555. ) * log10(q)
  2556. ) %>%
  2557. separate_wider_delim(
  2558. curie,
  2559. delim = ":",
  2560. names = c("database", "id")
  2561. ) %>%
  2562. filter(
  2563. database %in% c("fplx", "hgnc"),
  2564. q < .01
  2565. ) %>%
  2566. mutate(
  2567. sign = if_else(perturbed == "up", "pos", "neg")
  2568. )
  2569. ps <- idnra_res_plot_data %>%
  2570. group_nest(
  2571. rescue_type, query_type
  2572. ) %>%
  2573. mutate(
  2574. p = map2(
  2575. rescue_type, data,
  2576. \(x, y) {
  2577. y %>%
  2578. mutate(
  2579. name = fct_reorder(name, signed_q)
  2580. ) %>%
  2581. ggplot(
  2582. aes(
  2583. x = signed_q,
  2584. y = name,
  2585. fill = sign
  2586. )
  2587. ) +
  2588. geom_col() +
  2589. geom_text(
  2590. aes(
  2591. x = if_else(sign == "pos", -.05, .05),
  2592. hjust = if_else(sign == "pos", 1, 0),
  2593. label = name
  2594. ),
  2595. size = 2
  2596. ) +
  2597. scale_fill_discrete(guide = "none", direction = -1) +
  2598. theme(
  2599. axis.text.y = element_blank(),
  2600. panel.grid.major.y = element_blank(),
  2601. ) +
  2602. labs(
  2603. x = "Signed log10 p-value",
  2604. y = NULL,
  2605. title = paste0(query_type, " analysis of genes rescued by ", x),
  2606. subtitle = "FDR < 0.01"
  2607. )
  2608. }
  2609. )
  2610. )
  2611. pwalk(
  2612. ps,
  2613. \(rescue_type, query_type, p, ...) {
  2614. ggsave(
  2615. file.path("plots", paste0(query_type, "_", rescue_type, "_bars.pdf")),
  2616. p, width = 8, height = 8
  2617. )
  2618. }
  2619. )
  2620. ```
  2621. ## Check if p38 target TFs are differentially expressed
  2622. ```{r}
  2623. atf6_targets <- clipr::read_clip()
  2624. de_pert_hm_data_atf6 <- de_pert_hm_data %>%
  2625. filter(
  2626. gene_name %in% atf6_targets,
  2627. gene_id %in% {
  2628. de_pert_hm_data %>%
  2629. filter(
  2630. FDR < .05
  2631. ) %>%
  2632. pull(gene_id)
  2633. }
  2634. )
  2635. de_pert_hm_data_atf6_mat <- de_pert_hm_data_atf6 %>%
  2636. dplyr::select(
  2637. gene_id, contrast, logFC
  2638. ) %>%
  2639. pivot_wider(names_from = contrast, values_from = logFC, values_fill = 0) %>%
  2640. column_to_rownames("gene_id") %>%
  2641. as.matrix() %>%
  2642. t() %>%
  2643. scale() %>%
  2644. t() %>% {
  2645. .[, fgsea_hm_conditions]
  2646. }
  2647. hm <- Heatmap(
  2648. de_pert_hm_data_atf6_mat,
  2649. cluster_rows = cluster_fun_eucl,
  2650. cluster_columns = FALSE,
  2651. show_row_names = TRUE
  2652. )
  2653. hm
  2654. pca_fun <- function(mat, meta) {
  2655. pca <- prcomp(t(mat), center = TRUE, scale = FALSE)
  2656. pca_x <- as_tibble(pca$x) %>%
  2657. bind_cols(
  2658. meta
  2659. )
  2660. list(
  2661. x = pca_x,
  2662. sd = broom::tidy(pca, matrix = "eigenvalues"),
  2663. rotation = broom::tidy(pca, matrix = "rotation")
  2664. )
  2665. }
  2666. de_pert_hm_data_atf6_pca <- pca_fun(
  2667. de_pert_hm_data_atf6_mat,
  2668. tibble(contrast = fgsea_hm_conditions)
  2669. )
  2670. p <- ggplot(
  2671. de_pert_hm_data_atf6_pca$x,
  2672. aes(
  2673. x = PC1, y = PC3, color = contrast
  2674. )
  2675. ) +
  2676. geom_point() +
  2677. labs(
  2678. x = paste0("PC1 (", round(de_pert_hm_data_atf6_pca$sd$percent[1] * 100, 2), "%)"),
  2679. y = paste0("PC3 (", round(de_pert_hm_data_atf6_pca$sd$percent[3] * 100, 2), "%)")
  2680. ) +
  2681. theme_minimal()
  2682. hm <- Heatmap(
  2683. de_pert_hm_data_atf6_pca$x %>%
  2684. column_to_rownames("contrast") %>%
  2685. # scale() %>%
  2686. t(),
  2687. cluster_rows = cluster_fun_eucl,
  2688. cluster_columns = FALSE,
  2689. show_row_names = TRUE
  2690. )
  2691. hm
  2692. ```
  2693. VX-702
  2694. DORAMAPIMOD
  2695. LOSMAPIMOD
  2696. ## Visualize overlap between rescue type lists
  2697. ```{r}
  2698. ptx_rescue_overlap <- ptx_rescue_types %>%
  2699. filter(
  2700. rescue_type %in% c("GNE-495 only", "JNK-inh only", "both")
  2701. ) %>%
  2702. mutate(
  2703. # rescue_type_direction = paste(
  2704. # rescue_type, perturbed
  2705. # ),
  2706. dummy = TRUE,
  2707. across(batch, recode_batch)
  2708. ) %>%
  2709. select(-c(`GNE-495`, `JNK-inh`, perturbed)) %>%
  2710. pivot_wider(
  2711. names_from = c(rescue_type, batch),
  2712. values_from = dummy,
  2713. values_fill = list(dummy = FALSE)
  2714. )
  2715. ptx_rescue_overlap_hm <- ptx_rescue_overlap %>%
  2716. group_nest(
  2717. perturbed_threshold, rescue_definition
  2718. ) %>%
  2719. mutate(
  2720. hm = map(
  2721. data,
  2722. \(x) {
  2723. mat <- x %>%
  2724. # mutate(across(-gene_id, \(y) if_else(y, "TRUE", "FALSE"))) %>%
  2725. column_to_rownames("gene_id") %>%
  2726. as.matrix()
  2727. # browser()
  2728. Heatmap(
  2729. mat * 1L,
  2730. name = "Rescue",
  2731. col = c(`0` = "white", `1` = "blue"),
  2732. # cluster_rows = cluster_fun_binary,
  2733. # cluster_columns = cluster_fun_binary,
  2734. clustering_distance_rows = "binary",
  2735. clustering_distance_columns = "binary",
  2736. show_row_names = FALSE
  2737. )
  2738. }
  2739. )
  2740. )
  2741. pwalk(
  2742. ptx_rescue_overlap_hm,
  2743. \(perturbed_threshold, rescue_definition, hm, ...) {
  2744. withr::with_pdf(
  2745. file.path("plots", paste0("ptx_rescue_gene_set_overlap_hm_", rescue_definition, "_", perturbed_threshold, ".pdf")),
  2746. draw(hm),
  2747. width = 6, height = 10
  2748. )
  2749. }
  2750. )
  2751. ```
  2752. ## Dot plot of overrepresentation both doses
  2753. ```{r}
  2754. fgsea_overrep_both_doses %>%
  2755. filter(padj < .05) %>%
  2756. count(name, direction)
  2757. fgsea_overrep_single_treatments %>%
  2758. filter(padj < .05) %>%
  2759. count(contrast, direction, significant_threshold, batch) %>%
  2760. print(n = Inf)
  2761. ```
  2762. name direction n
  2763. <chr> <chr> <int>
  2764. 1 GNE-495 + PTX vs PTX both 1350
  2765. 2 GNE-495 + PTX vs PTX down 747
  2766. 3 GNE-495 + PTX vs PTX up 762
  2767. 4 JNK-inh + PTX vs PTX both 2955
  2768. 5 JNK-inh + PTX vs PTX down 1923
  2769. 6 JNK-inh + PTX vs PTX up 1404
  2770. ```{r}
  2771. combine_updown <- function(data, direction_col, fold_change_col, p_col, group_cols, p_threshold = .05) {
  2772. group_syms <- syms(group_cols)
  2773. combine_impl <- function(d, g) {
  2774. direction <- if (all(d[[p_col]] < p_threshold)) {
  2775. "both"
  2776. } else if (all(d[[p_col]] >= p_threshold)) {
  2777. "none"
  2778. } else {
  2779. d[[direction_col]][which.min(d[[p_col]])]
  2780. }
  2781. min_p_idx <- which.min(d[[p_col]])
  2782. fold_change <- if (direction == "both") {
  2783. NA_real_
  2784. } else {
  2785. d[[fold_change_col]][min_p_idx]
  2786. }
  2787. p_value <- d[[p_col]][min_p_idx]
  2788. tibble(
  2789. direction = direction,
  2790. fold_change = fold_change,
  2791. p_value = p_value
  2792. )
  2793. }
  2794. data %>%
  2795. group_by(!!!group_syms) %>%
  2796. group_modify(combine_impl) %>%
  2797. ungroup()
  2798. }
  2799. fgsea_overrep_ptx_updown_combined <- fgsea_overrep_single_treatments %>%
  2800. filter(
  2801. contrast == "PTX",
  2802. direction %in% c("up", "down"),
  2803. significant_threshold == .05,
  2804. batch %in% c("batch_1", "batch_2")
  2805. ) %>%
  2806. mutate(
  2807. log2_foldEnrichment = log2(if_else(foldEnrichment == 0, .5, foldEnrichment))
  2808. ) %>%
  2809. combine_updown(
  2810. direction_col = "direction",
  2811. fold_change_col = "log2_foldEnrichment",
  2812. p_col = "padj",
  2813. group_cols = c("contrast", "pathway", "batch"),
  2814. p_threshold = .01
  2815. )
  2816. fgsea_overrep_both_doses_updown_combined <- fgsea_overrep_both_doses %>%
  2817. filter(
  2818. direction %in% c("up", "down")
  2819. ) %>%
  2820. mutate(
  2821. # Replacing -Inf with -1 for log2 fold enrichment
  2822. estimate = log2(if_else(foldEnrichment == 0, .5, foldEnrichment))
  2823. ) %>%
  2824. combine_updown(
  2825. direction_col = "direction",
  2826. fold_change_col = "log2_foldEnrichment",
  2827. p_col = "padj",
  2828. group_cols = c("name", "pathway"),
  2829. p_threshold = .01
  2830. )
  2831. fgsea_overrep_both_doses_updown_combined %>%
  2832. filter(is.na(p_value))
  2833. ```
  2834. Can't use combination strategy so well for PTX because many gene sets are enriched
  2835. in both up and down directions. Using "both"
  2836. ```{r}
  2837. fgsea_overrep_ptx <- fgsea_overrep_single_treatments %>%
  2838. filter(
  2839. contrast == "PTX",
  2840. direction == "both",
  2841. significant_threshold == .05,
  2842. batch == "batch_1"
  2843. )
  2844. fgsea_overrep_both_doses_filtered <- fgsea_overrep_both_doses %>%
  2845. filter(
  2846. direction == "both"
  2847. )
  2848. fgsea_overrep_both_doses_and_ptx <- bind_rows(
  2849. fgsea_overrep_ptx,
  2850. fgsea_overrep_both_doses_filtered %>%
  2851. rename(contrast = name)
  2852. ) %>%
  2853. mutate(
  2854. # Replacing -Inf with -1 for log2 fold enrichment
  2855. log2_foldEnrichment = log2(if_else(foldEnrichment == 0, .5, foldEnrichment))
  2856. )
  2857. fgsea_overrep_both_doses_updown_combined_dot_plots <- fgsea_overrep_both_doses_and_ptx %>%
  2858. mutate(
  2859. padj_bin = cut(
  2860. padj,
  2861. breaks = c(0, .001, .01, .05, 1),
  2862. labels = c("<0.001", "<0.01", "<0.05", "ns")
  2863. ),
  2864. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  2865. pathway = abbreviate_prefix(pathway)
  2866. ) %>%
  2867. filter(
  2868. database %in% c("REACTOME", "HALLMARK", "PID", "KEGG_MEDICUS")
  2869. ) %>%
  2870. crossing(
  2871. p_threshold = c(0.001, .01, .05)
  2872. ) %>%
  2873. group_nest(p_threshold) %>%
  2874. mutate(
  2875. res = map2(
  2876. data, p_threshold,
  2877. \(x, y) {
  2878. # browser()
  2879. # d <- semi_join(
  2880. # x,
  2881. # group_by(x, contrast) %>%
  2882. # filter(padj < y),
  2883. # by = "pathway"
  2884. # )
  2885. # d <- semi_join(
  2886. # x,
  2887. # group_by(x, contrast) %>%
  2888. # arrange(padj) %>%
  2889. # slice_head(n = 20),
  2890. # by = "pathway"
  2891. # )
  2892. d <- semi_join(
  2893. x,
  2894. group_by(x, pathway) %>%
  2895. filter(padj < y) %>%
  2896. summarize(groups = paste(unique(contrast), collapse = "_"), p_mean = 1 / sum(1 / padj, na.rm = TRUE)) %>%
  2897. group_by(groups) %>%
  2898. arrange(p_mean) %>%
  2899. slice_head(n = 20),
  2900. by = "pathway"
  2901. )
  2902. dwide <- d %>%
  2903. arrange(pathway) %>%
  2904. pivot_wider(
  2905. id_cols = pathway,
  2906. names_from = contrast,
  2907. values_from = log2_foldEnrichment
  2908. )
  2909. dmat <- dwide %>%
  2910. column_to_rownames("pathway") %>%
  2911. as.matrix() %>%
  2912. dist()
  2913. # browser()
  2914. clust <- hclust(dmat, method = "average") %>%
  2915. reorder(dmat, method = "OLO")
  2916. row_labels <- clust$labels[clust$order] %>% {
  2917. set_names(
  2918. str_replace_all(., coll("_"), " ") %>%
  2919. str_sub(start = 2),
  2920. .
  2921. )
  2922. }
  2923. # sim_hm <- msigdbr_of_interest_sim_trans[
  2924. # rev(clust$labels[clust$order]), rev(clust$labels[clust$order])
  2925. # ] %>%
  2926. # pheatmap(
  2927. # cluster_rows = FALSE,
  2928. # cluster_cols = FALSE
  2929. # )
  2930. est_perc <- quantile(d$log2_foldEnrichment, c(.05, .95))
  2931. abs_max_est_perc <- max(abs(est_perc))
  2932. p <- ggplot(
  2933. d %>%
  2934. mutate(
  2935. pathway = factor(pathway, levels = clust$labels[clust$order])
  2936. ),
  2937. aes(
  2938. x = contrast,
  2939. y = pathway,
  2940. color = log2_foldEnrichment,
  2941. size = padj_bin
  2942. )
  2943. ) +
  2944. geom_point(shape = 16) +
  2945. # scale_color_viridis_c(trans = "pseudo_log") +
  2946. # scale_color_distiller(
  2947. # limits = c(-abs_max_est_perc, abs_max_est_perc),
  2948. # palette = "RdYlBu", trans = "pseudo_log",
  2949. # oob = scales::squish
  2950. # ) +
  2951. paletteer::scale_color_paletteer_c(
  2952. palette = "pals::ocean.balance",
  2953. limits = c(-abs_max_est_perc, abs_max_est_perc),
  2954. oob = scales::squish
  2955. ) +
  2956. scale_size_manual(
  2957. values = rev(c("<0.001" = 6, "<0.01" = 4, "<0.05" = 3, "ns" = 2))
  2958. ) +
  2959. scale_x_discrete(position = "top") +
  2960. scale_y_discrete(
  2961. breaks = names(row_labels),
  2962. labels = unname(row_labels)
  2963. ) +
  2964. theme(
  2965. axis.text.x = element_text(angle = 45, hjust = 0)
  2966. ) +
  2967. labs(
  2968. x = NULL,
  2969. y = NULL,
  2970. size = NULL
  2971. )
  2972. list(
  2973. p = p,
  2974. clust_data = d
  2975. # sim_hm = sim_hm
  2976. ) %>%
  2977. map(list)
  2978. }
  2979. )
  2980. ) %>%
  2981. select(-data) %>%
  2982. unnest_wider(
  2983. res
  2984. )
  2985. flag_similar_adjacent_leaves(clust, msigdbr_of_interest_sim_trans, .6)
  2986. pwalk(
  2987. fgsea_intersection_dotplot_data_single,
  2988. \(p, rescue_definition, p_threshold, perturbed_threshold, ...) {
  2989. ggsave(
  2990. here("plots", "gene_set_analysis", paste0("fgsea_rescue_overrep_dotplot_", rescue_definition, "_", perturbed_threshold, "_", p_threshold, ".pdf")),
  2991. p[[1]], width = 9.8, height = 9
  2992. )
  2993. # withr::with_pdf(
  2994. # file.path("plots", "overrep", paste0("overrep_intersection_dotplot_", rescue_definition, "_", p_threshold, "_similarity.pdf")),
  2995. # draw(sim_hm[[1]]),
  2996. # width = 9.8, height = 9
  2997. # )
  2998. }
  2999. )
  3000. ```
  3001. ### Up down separately
  3002. ```{r}
  3003. fgsea_overrep_ptx <- fgsea_overrep_single_treatments %>%
  3004. filter(
  3005. contrast == "PTX",
  3006. direction != "both",
  3007. significant_threshold == .05,
  3008. batch == "batch_1"
  3009. )
  3010. fgsea_overrep_both_doses_filtered <- fgsea_overrep_both_doses %>%
  3011. filter(
  3012. direction != "both"
  3013. )
  3014. fgsea_overrep_both_doses_and_ptx <- bind_rows(
  3015. fgsea_overrep_ptx,
  3016. fgsea_overrep_both_doses_filtered %>%
  3017. rename(contrast = name)
  3018. ) %>%
  3019. mutate(
  3020. # Replacing -Inf with -1 for log2 fold enrichment
  3021. log2_foldEnrichment = log2(if_else(foldEnrichment == 0, .5, foldEnrichment)),
  3022. directed_enrichment = pmax(
  3023. log2_foldEnrichment,
  3024. 0
  3025. ) * if_else(direction == "up", 1, -1)
  3026. )
  3027. fgsea_overrep_both_doses_updown_combined_dot_plots <- fgsea_overrep_both_doses_and_ptx %>%
  3028. mutate(
  3029. padj_bin = cut(
  3030. padj,
  3031. breaks = c(0, .001, .01, .05, 1),
  3032. labels = c("<0.001", "<0.01", "<0.05", "ns")
  3033. ),
  3034. database = str_extract(pathway, "^(REACTOME|HALLMARK|PID|KEGG_MEDICUS|GOBP|GOMF|GOCC)"),
  3035. pathway = abbreviate_prefix(pathway)
  3036. ) %>%
  3037. filter(
  3038. database %in% c("REACTOME", "HALLMARK", "PID", "KEGG_MEDICUS")
  3039. ) %>%
  3040. crossing(
  3041. p_threshold = c(0.001, .01, .05)
  3042. ) %>%
  3043. group_nest(p_threshold) %>%
  3044. mutate(
  3045. res = map2(
  3046. data, p_threshold,
  3047. \(x, y) {
  3048. # browser()
  3049. # d <- semi_join(
  3050. # x,
  3051. # group_by(x, contrast) %>%
  3052. # filter(padj < y),
  3053. # by = "pathway"
  3054. # )
  3055. # d <- semi_join(
  3056. # x,
  3057. # group_by(x, contrast) %>%
  3058. # arrange(padj) %>%
  3059. # slice_head(n = 20),
  3060. # by = "pathway"
  3061. # )
  3062. d <- semi_join(
  3063. x,
  3064. group_by(x, pathway) %>%
  3065. filter(padj < y, is_main) %>%
  3066. summarize(groups = paste(sort(unique(contrast)), collapse = "_"), p_mean = 1 / sum(1 / padj, na.rm = TRUE)) %>%
  3067. group_by(groups) %>%
  3068. arrange(p_mean) %>%
  3069. slice_head(n = 10) %>%
  3070. bind_rows(tibble(pathway = "P_P38_MK2_PATHWAY")),
  3071. by = "pathway"
  3072. )
  3073. dwide <- d %>%
  3074. arrange(pathway) %>%
  3075. pivot_wider(
  3076. id_cols = pathway,
  3077. names_from = c(direction, contrast),
  3078. values_from = directed_enrichment
  3079. )
  3080. dmat <- dwide %>%
  3081. column_to_rownames("pathway") %>%
  3082. as.matrix() %>%
  3083. dist()
  3084. # browser()
  3085. clust <- hclust(dmat, method = "average") %>%
  3086. reorder(dmat, method = "OLO")
  3087. row_labels <- clust$labels[clust$order] %>% {
  3088. set_names(
  3089. str_replace_all(., coll("_"), " ") %>%
  3090. str_sub(start = 2),
  3091. .
  3092. )
  3093. }
  3094. # sim_hm <- msigdbr_of_interest_sim_trans[
  3095. # rev(clust$labels[clust$order]), rev(clust$labels[clust$order])
  3096. # ] %>%
  3097. # pheatmap(
  3098. # cluster_rows = FALSE,
  3099. # cluster_cols = FALSE
  3100. # )
  3101. est_perc <- quantile(d$directed_enrichment, c(.05, .95))
  3102. abs_max_est_perc <- max(abs(est_perc))
  3103. p <- ggplot(
  3104. d %>%
  3105. mutate(
  3106. pathway = factor(pathway, levels = clust$labels[clust$order])
  3107. ),
  3108. aes(
  3109. x = contrast,
  3110. y = pathway,
  3111. color = directed_enrichment,
  3112. size = padj_bin,
  3113. shape = direction
  3114. )
  3115. ) +
  3116. geom_text(
  3117. aes(
  3118. label = if_else(direction == "up", "◖", "◗"),
  3119. hjust = if_else(direction == "up", .5, .5)
  3120. ),
  3121. vjust = .5
  3122. ) +
  3123. guides(
  3124. size = guide_legend(override.aes = list(shape = "◖"))
  3125. ) +
  3126. paletteer::scale_color_paletteer_c(
  3127. palette = "ggthemes::Orange-Blue-White Diverging",
  3128. limits = c(-abs_max_est_perc, abs_max_est_perc),
  3129. oob = scales::squish,
  3130. direction = -1
  3131. ) +
  3132. scale_size_manual(
  3133. values = rev(c("<0.001" = 8, "<0.01" = 6, "<0.05" = 4, "ns" = 2))
  3134. ) +
  3135. scale_x_discrete(position = "top") +
  3136. scale_y_discrete(
  3137. breaks = names(row_labels),
  3138. labels = unname(row_labels)
  3139. ) +
  3140. theme(
  3141. axis.text.x = element_text(angle = 45, hjust = 0),
  3142. panel.grid.major.x = element_blank()
  3143. ) +
  3144. labs(
  3145. x = NULL,
  3146. y = NULL,
  3147. size = "FDR",
  3148. color = "log2 fold\nenrichment"
  3149. )
  3150. list(
  3151. p = p,
  3152. clust_data = d
  3153. # sim_hm = sim_hm
  3154. ) %>%
  3155. map(list)
  3156. }
  3157. )
  3158. ) %>%
  3159. select(-data) %>%
  3160. unnest_wider(
  3161. res
  3162. )
  3163. pwalk(
  3164. fgsea_overrep_both_doses_updown_combined_dot_plots,
  3165. \(p_threshold, p, ...) {
  3166. ggsave(
  3167. file.path(
  3168. "plots", "gene_set_analysis", paste0("fgsea_overrep_both_doses_updown_dotplot_separate_", p_threshold, ".pdf")
  3169. ), p[[1]], width = 6, height = 12, dev = cairo_pdf
  3170. )
  3171. }
  3172. )
  3173. ```
  3174. ```{r}
  3175. fgsea_overrep_both_doses_and_ptx %>%
  3176. filter(
  3177. str_detect(pathway, coll("p38", ignore_case = TRUE))
  3178. ) %>%
  3179. arrange(pval) %>%
  3180. View()
  3181. fgsea_res_pert %>%
  3182. filter(
  3183. str_detect(pathway, coll("p38", ignore_case = TRUE)),
  3184. metric == "signed_padj"
  3185. ) %>%
  3186. View()
  3187. ```
  3188. ## Enrichment bar graphs on DE gene clusters
  3189. ```{r}
  3190. fgsea_overrep_clusters
  3191. ```
  3192. TODO:
  3193. 1. Heatmap of logFC all DE genes, including JNK+GNE double/triple treatments
  3194. 2. Dot plot of PTX, GNE vs DMSO, JNK vs DMOS, double treatments vs PTX (FGSEA enrichments)
  3195. 3. Scatter plots of FGSEA enrichments, highlighting interesting ones, labelling quadrants
  3196. 4. PCAs and volcano plo

gene_set_analysis_plotting.Rmd at commit 3329343, no license · at the source

Overview

Authors: Veselina Petrova1,2, Caitlin E Mills3,4, Clemens Hug3, Aysel Cetinkaya-Fisgin5, Jennifer Splaine6, Sepideh Fouladzadeh7, Sara Hakim1,2, Rasheen Powell1,2, Shannon Zhen1, Mirra Chung3,4, Gary A Bradshaw3, Tao Deng8, Ilyas Singec8, Qing Wang9,10, Riki Kawaguchi9,10, Harathi Jonnagaddala11, Lee B Barrett1,2, Jennifer A Smith6, Marian Kalocsay11, Benjamin M Gyori7,12, Ahmet Hoke5, Peter K Sorger3,4, Clifford J Woolf1,2
  1. F.M. Kirby Neurobiology Center, Program in Neurobiology, Boston Children’s Hospital, Boston, MA, USA
  2. Department of Neurobiology, Harvard Medical School, Boston, MA, USA
  3. Laboratory of Systems Pharmacology, Harvard Program in Therapeutic Science, Harvard Medical School, Boston, MA, USA
  4. Department of Systems Biology, Harvard Medical School, Boston, MA, USA
  5. Department of Neurology, Neuromuscular Division, Johns Hopkins School of Medicine, Baltimore, MD, USA
  6. ICCB-Longwood Screening Facility, Harvard Medical School, 250 Longwood Avenue, Boston, MA, USA
  7. Department of Bioengineering, Northeastern University, Boston, MA, USA
  8. National Center for Advancing Translational Sciences (NCATS), Division of Preclinical Innovation, Stem Cell Translation Laboratory (SCTL), National Institutes of Health (NIH), Rockville, MD, USA
  9. Department of Neurology, David Geffen School of Medicine, University of California, Los Angeles, Los Angeles, CA, USA
  10. Center for Neurobehavioral Genetics, Semel Institute for Neuroscience and Human Behavior, University of California, Los Angeles, Los Angeles, CA, USA
  11. Department of Experimental Radiation Oncology, The University of Texas MD Anderson Cancer Center, Houston, TX, USA
  12. Khoury College of Computer Sciences, Northeastern University, Boston, MA, USA
Journal: Cell reports. Medicine, volume 7, issue 5, article 102787
Dates: received 11 December 2025; accepted 8 April 2026; published online 6 May 2026; in print May 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1016/j.xcrm.2026.102787 · PMID 42097147 · PMCID PMC13198234 · OpenAlex W7160418959
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), mouse (organism), other condition (population)
Methods: Statistics, Machine learning, Smoothing, state filtering, decompositions
Keywords: chemotherapy-induced peripheral neuropathy, iPSC-derived sensory neurons, high-throughput screening, neuroprotective small molecules, axon degeneration, STE20 kinases
MeSH: Antineoplastic Agents*, High-Throughput Screening Assays*, Induced Pluripotent Stem Cells*, Neuroprotective Agents*, Peripheral Nervous System Diseases*, Sensory Receptor Cells*, Animals, Axons, Disease Models, Animal, Drug Discovery, Ganglia, Spinal, Humans, Mice, Paclitaxel, Protein Kinase Inhibitors (* major topic)
Topic: Cancer Treatment and Pharmacology (Oncology, Medicine), according to OpenAlex
Funding: Defense Advanced Research Projects Agency Defense Sciences Office (HR0011-19-2-0022); Defense Advanced Research Projects Agency; National Institute of Neurological Disorders and Stroke (R35NS105076, 5P50HD105351); Dr. Miriam and Sheldon G. Adelson Medical Research Foundation; Advanced Research Projects Agency for Health; Cancer Prevention and Research Institute of Texas (RR220032); Pharmaceutical Research and Manufacturers of America Foundation; National Institutes of Health
Citations: cited by 1 paper (Europe PMC); 78 references in the paper
Research resources: Chicken anti-Neurofilament H RRID:AB_11212161, Donkey anti-mouse Alexa Fluor 488 RRID:AB_141607, Rabbit anti-TAC1 RRID:AB_1623286, Rabbit anti-phospho-cJun (Ser73) RRID:AB_2129575, Donkey anti-rabbit Alex Fluor 568 RRID:AB_2534017, Donkey anti-rabbit Alexa Fluor 647 RRID:AB_2536183, Rabbit anti-PGP9.5 RRID:AB_2862594, Donkey anti-chicken Alexa Fluor 647 RRID:AB_2921074, Hoechst 33342 RRID:AB_3675235, Mouse anti-βIII-tubulin RRID:AB_477590, Rabbit anti-CGRP RRID:AB_572217, Anti-β-tubulin Alexa Fluor 555 RRID:AB_823665, Rabbit anti-Peripherin RRID:AB_90725, Rabbit anti-Brn3a RRID:AB_92154, R v4.4.2 RRID:SCR_001905, GraphPad Prism RRID:SCR_002798, limma v3.62.2 RRID:SCR_010943, DESeq2 v1.46.0 RRID:SCR_015687, MetaXpress Custom Module Editor RRID:SCR_016654, tximport v1.34.0 RRID:SCR_016752, Salmon v1.10.3 RRID:SCR_017036, Seaborn (Python) RRID:SCR_018132, the IncuCyte RRID:SCR_019874, RRID:SCR_020294, fgsea v1.32.4 RRID:SCR_020938, msigdbr v24.1.0 RRID:SCR_022870, IncuCyte RRID:SCR_025411

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repository

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

labsyspharm/peripheral-neuropathy

License: none: the authors keep all their rights
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 33293433fa769ac9d5a43bded03ad322ed2771c1, 2 December 2025
Languages: R (7)
Size: 8 files, 7 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: 7 notebooks
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: data.table (7 files), tidyverse (7 files), circlize (5 files), ComplexHeatmap (5 files), broom (3 files), Plotly (3 files), DESeq2 (1 file), edgeR (1 file), emmeans (1 file), ggplot2 (1 file), patchwork (1 file), psych (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
7 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;
  • 7 scripts, each with its path and the digest of its content;
  • 9 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

Code and data availability statement

The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1016/j.xcrm.2026.102787.

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 2, 28 September 2026

  • Authors: added Clifford J Woolf (0000-0002-6636-3897); removed Clifford J Woolf

Version 1, 28 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 23 authors, 6 keywords, 15 MeSH terms, 8 funders, 77 references, 27 RRIDs.

Cite

This paper

Petrova, V., Mills, C. E., Hug, C., Cetinkaya-Fisgin, A., Splaine, J., Fouladzadeh, S., Hakim, S., Powell, R., Zhen, S., Chung, M., Bradshaw, G. A., Deng, T., Singec, I., Wang, Q., Kawaguchi, R., Jonnagaddala, H., Barrett, L. B., Smith, J. A., Kalocsay, M., . . . Woolf, C. J. (2026). A human iPSC-derived sensory neuron platform for high-throughput discovery of neuroprotectants against chemotherapy-induced peripheral neuropathy. Cell reports. Medicine, 7(5), 102787. https://doi.org/10.1016/j.xcrm.2026.102787

BibTeX

@article{petrova2026human,
author = {Petrova, Veselina and Mills, Caitlin E and Hug, Clemens and Cetinkaya-Fisgin, Aysel and Splaine, Jennifer and Fouladzadeh, Sepideh and Hakim, Sara and Powell, Rasheen and Zhen, Shannon and Chung, Mirra and Bradshaw, Gary A and Deng, Tao and Singec, Ilyas and Wang, Qing and Kawaguchi, Riki and Jonnagaddala, Harathi and Barrett, Lee B and Smith, Jennifer A and Kalocsay, Marian and Gyori, Benjamin M and Hoke, Ahmet and Sorger, Peter K and Woolf, Clifford J},
title = {{A human iPSC-derived sensory neuron platform for high-throughput discovery of neuroprotectants against chemotherapy-induced peripheral neuropathy}},
journal = {Cell reports. Medicine},
year = {2026},
month = may,
volume = {7},
number = {5},
pages = {102787},
publisher = {Elsevier},
issn = {2666-3791},
doi = {10.1016/j.xcrm.2026.102787},
url = {https://doi.org/10.1016/j.xcrm.2026.102787},
pmid = {42097147},
pmcid = {PMC13198234}
}

RIS

TY - JOUR
AU - Petrova, Veselina
AU - Mills, Caitlin E
AU - Hug, Clemens
AU - Cetinkaya-Fisgin, Aysel
AU - Splaine, Jennifer
AU - Fouladzadeh, Sepideh
AU - Hakim, Sara
AU - Powell, Rasheen
AU - Zhen, Shannon
AU - Chung, Mirra
AU - Bradshaw, Gary A
AU - Deng, Tao
AU - Singec, Ilyas
AU - Wang, Qing
AU - Kawaguchi, Riki
AU - Jonnagaddala, Harathi
AU - Barrett, Lee B
AU - Smith, Jennifer A
AU - Kalocsay, Marian
AU - Gyori, Benjamin M
AU - Hoke, Ahmet
AU - Sorger, Peter K
AU - Woolf, Clifford J
TI - A human iPSC-derived sensory neuron platform for high-throughput discovery of neuroprotectants against chemotherapy-induced peripheral neuropathy
T2 - Cell reports. Medicine
J2 - Cell Rep Med
PY - 2026
DA - 2026/05/06
VL - 7
IS - 5
SP - 102787
SN - 2666-3791
PB - Elsevier
DO - 10.1016/j.xcrm.2026.102787
UR - https://doi.org/10.1016/j.xcrm.2026.102787
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.xcrm.2026.102787",
"type": "article-journal",
"title": "A human iPSC-derived sensory neuron platform for high-throughput discovery of neuroprotectants against chemotherapy-induced peripheral neuropathy",
"container-title": "Cell reports. Medicine",
"author": [
{
"family": "Petrova",
"given": "Veselina"
},
{
"family": "Mills",
"given": "Caitlin E"
},
{
"family": "Hug",
"given": "Clemens"
},
{
"family": "Cetinkaya-Fisgin",
"given": "Aysel"
},
{
"family": "Splaine",
"given": "Jennifer"
},
{
"family": "Fouladzadeh",
"given": "Sepideh"
},
{
"family": "Hakim",
"given": "Sara"
},
{
"family": "Powell",
"given": "Rasheen"
},
{
"family": "Zhen",
"given": "Shannon"
},
{
"family": "Chung",
"given": "Mirra"
},
{
"family": "Bradshaw",
"given": "Gary A"
},
{
"family": "Deng",
"given": "Tao"
},
{
"family": "Singec",
"given": "Ilyas"
},
{
"family": "Wang",
"given": "Qing"
},
{
"family": "Kawaguchi",
"given": "Riki"
},
{
"family": "Jonnagaddala",
"given": "Harathi"
},
{
"family": "Barrett",
"given": "Lee B"
},
{
"family": "Smith",
"given": "Jennifer A"
},
{
"family": "Kalocsay",
"given": "Marian"
},
{
"family": "Gyori",
"given": "Benjamin M"
},
{
"family": "Hoke",
"given": "Ahmet"
},
{
"family": "Sorger",
"given": "Peter K"
},
{
"family": "Woolf",
"given": "Clifford J"
}
],
"container-title-short": "Cell Rep Med",
"volume": "7",
"issue": "5",
"page": "102787",
"DOI": "10.1016/j.xcrm.2026.102787",
"PMID": "42097147",
"PMCID": "PMC13198234",
"ISSN": "2666-3791",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.xcrm.2026.102787",
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
6
]
]
}
}

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.1093/bioinformatics/btag592 [code]
Network-based stratification of allele-specific expression reveals patient subgroups in Huntington's disease.
Journal: Bioinformatics (Oxford, England)
In common: edgeR, broom, circlize, 8 other tools, other condition, 1 reference
[2] doi:10.1038/s42003-026-10180-5 [code]
Modeling Friedreich's ataxia with Bergmann glia-enriched human cerebellar organoids.
Journal: Communications biology
In common: circlize, DESeq2, ComplexHeatmap, 3 other tools, other condition, 1 reference, author Ilyas Singeç
[3] 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, psych, broom, 7 other tools, other condition
[4] 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, circlize, DESeq2, 6 other tools, other condition, 2 references
[5] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: edgeR, broom, circlize, 7 other tools, mouse
[6] doi:10.1038/s41467-026-70243-3 [code]
TYK2 mediates neuroinflammation in Alzheimer's disease brains with TDP-43 pathology.
Journal: Nature communications
In common: broom, circlize, ComplexHeatmap, 3 other tools, 1 reference, author Marian Kalocsay
[7] doi:10.1016/j.xcrm.2026.102682 [code]
TET CpG sequence-context-specific DNA demethylation shapes progression of IDH-mutant gliomas.
Journal: Cell reports. Medicine
In common: edgeR, broom, circlize, 6 other tools, other condition, 1 reference
[8] doi:10.1038/s41467-026-74753-y [code]
A human-specific microRNA controls the timing of excitatory synaptogenesis.
Journal: Nature communications
In common: edgeR, circlize, emmeans, 6 other tools, 1 reference
[9] doi:10.1038/s41386-026-02406-1 [code]
Functional genomic profiling of schizophrenia-associated genes reveals key microglial regulators.
Journal: Neuropsychopharmacology : official publication of the American College of Neuropsychopharmacology
In common: broom, circlize, DESeq2, 6 other tools, 1 reference
[10] doi:10.1016/j.isci.2026.115573 [code]
Female cortical cellular mosaicism underlies shared MeCP2 and PCB impacted gene pathways.
Journal: iScience
In common: edgeR, broom, circlize, 6 other tools, mouse

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.