/R/show_group_enrichment.R

https://github.com/ShixiangWang/sigminer · R · 194 lines · 155 code · 11 blank · 28 comment · 27 complexity · 7807c832656d8ae99c71f185bbfab766 MD5 · raw file

  1. #' Show Group Enrichment Result
  2. #'
  3. #' See [group_enrichment] for examples.
  4. #' NOTE the box fill and the box text have different meanings.
  5. #'
  6. #' @inheritParams ggplot2::facet_wrap
  7. #' @param df_enrich result `data.frame` from [group_enrichment].
  8. #' @param return_list if `TRUE`, return a list of `ggplot` object so user
  9. #' can combine multiple plots by other R packages like `patchwork`.
  10. #' @param add_text_annotation if `TRUE`, add text annotation in box.
  11. #' When show p value with filled color, the text indicates relative change;
  12. #' when show relative change with filled color, the text indicates p value.
  13. #' @param fill_by_p_value if `TRUE`, show log10 based p values with filled color.
  14. #' The +/- of p values indicates change direction.
  15. #' @param use_fdr if `TRUE`, show FDR values instead of raw p-values.
  16. #' @param cut_p_value if `TRUE`, cut p values into 5 regions for better visualization.
  17. #' Only works when `fill_by_p_value = TRUE`.
  18. #' @param cut_breaks when `cut_p_value` is `TRUE`, this option set the (log10 based) breaks.
  19. #' @param cut_labels when `cut_p_value` is `TRUE`, this option set the labels.
  20. #' @param fill_scale a `Scale` object generated by `ggplot2` package to
  21. #' set color for continuous values.
  22. #' @param cluster_row if `TRUE`, cluster rows with Hierarchical Clustering ('complete' method).
  23. #' @param ... other parameters passing to [ggplot2::facet_wrap], only used
  24. #' when `return_list` is `FALSE`.
  25. #'
  26. #' @return a (list of) `ggplot` object.
  27. #' @export
  28. show_group_enrichment <- function(df_enrich,
  29. return_list = FALSE,
  30. scales = "free",
  31. add_text_annotation = TRUE,
  32. fill_by_p_value = TRUE,
  33. use_fdr = TRUE,
  34. cut_p_value = FALSE,
  35. cut_breaks = c(-Inf, -5, log10(0.05), -log10(0.05), 5, Inf),
  36. cut_labels = c("\u2193 1e-5", "\u2193 0.05", "non-significant", "\u2191 0.05", "\u2191 1e-5"),
  37. fill_scale = scale_fill_gradient2(
  38. low = "#08A76B", mid = "white", high = "red",
  39. midpoint = ifelse(fill_by_p_value, 0, 1)
  40. ),
  41. cluster_row = FALSE,
  42. ...) {
  43. if (fill_by_p_value) {
  44. df_enrich$p_value_up <- if (use_fdr) abs(log10(df_enrich$fdr)) else abs(log10(df_enrich$p_value))
  45. df_enrich$p_value_up <- data.table::fifelse(
  46. df_enrich$measure_observed >= 1,
  47. df_enrich$p_value_up,
  48. -df_enrich$p_value_up
  49. )
  50. }
  51. if (return_list) {
  52. df_enrich %>%
  53. dplyr::group_nest(.data$grp_var) %>%
  54. dplyr::mutate(
  55. gg = purrr::map(.data$data,
  56. plot_enrichment_simple,
  57. x = "enrich_var", y = "grp1",
  58. fill_scale = fill_scale,
  59. fill_by_p_value = fill_by_p_value,
  60. cut_p_value = cut_p_value,
  61. cut_breaks = cut_breaks,
  62. cut_labels = cut_labels,
  63. add_text_annotation = add_text_annotation,
  64. use_fdr = use_fdr,
  65. cluster_row = cluster_row
  66. )
  67. ) -> xx
  68. p <- xx$gg
  69. names(p) <- xx$grp_var
  70. } else {
  71. p <- plot_enrichment_simple(df_enrich,
  72. x = "enrich_var", y = "grp1",
  73. fill_scale = fill_scale,
  74. fill_by_p_value = fill_by_p_value,
  75. cut_p_value = cut_p_value,
  76. cut_breaks = cut_breaks,
  77. cut_labels = cut_labels,
  78. add_text_annotation = add_text_annotation,
  79. use_fdr = use_fdr,
  80. cluster_row = cluster_row
  81. ) +
  82. facet_wrap(~grp_var, scales = scales, ...)
  83. }
  84. return(p)
  85. }
  86. plot_enrichment_simple <- function(data, x, y, fill_scale,
  87. fill_by_p_value = TRUE,
  88. cut_p_value = FALSE,
  89. cut_breaks = c(-Inf, -10, -1.3, 1.3, 10, Inf),
  90. cut_labels = c("< -10", "< -1.3", "nosig", "> 1.3", "> 10"),
  91. add_text_annotation = TRUE,
  92. use_fdr = TRUE,
  93. cluster_row = FALSE) {
  94. if (fill_by_p_value) {
  95. data$measure_observed <- round(data$measure_observed, 2)
  96. } else {
  97. if (use_fdr) {
  98. data$fdr <- round(data$fdr, 3)
  99. } else {
  100. data$p_value <- round(data$p_value, 3)
  101. }
  102. }
  103. if (cut_p_value) {
  104. data$p_value_up <- cut(data$p_value_up,
  105. breaks = cut_breaks,
  106. labels = cut_labels,
  107. )
  108. }
  109. # 支持行聚类(subgroup)
  110. if (isTRUE(cluster_row)) {
  111. data2 <- data[, c(x, y, "grp_var", "measure_observed"), with = F]
  112. data2 <- tidyr::pivot_wider(data2, names_from = x, values_from = "measure_observed")
  113. get_cluster_order <- function(x) {
  114. x <- x %>%
  115. tibble::column_to_rownames("grp1")
  116. obj <- x %>%
  117. scale() %>%
  118. stats::dist() %>%
  119. stats::hclust() %>%
  120. stats::as.dendrogram()
  121. rownames(x)[stats::order.dendrogram(obj)]
  122. }
  123. orders <- data2 %>%
  124. dplyr::group_split(.data$grp_var, .keep = FALSE) %>%
  125. purrr::map(get_cluster_order) %>%
  126. purrr::reduce(c) %>%
  127. unique()
  128. message("All subgroup orders: ", paste(orders, collapse = ", "))
  129. data$grp1 <- factor(data$grp1, levels = orders)
  130. }
  131. p <- ggplot(
  132. data,
  133. aes_string(
  134. x = x,
  135. y = y
  136. )
  137. )
  138. if (cut_p_value) {
  139. p <- p +
  140. geom_tile(mapping = aes_string(fill = "p_value_up")) +
  141. scale_fill_manual(
  142. drop = FALSE,
  143. na.value = "grey",
  144. values = c("#08A76B", "#98FF97", "white", "orange", "red")
  145. )
  146. } else {
  147. p <- p +
  148. geom_tile(mapping = aes_string(fill = if (fill_by_p_value) "p_value_up" else "measure_observed")) +
  149. fill_scale
  150. }
  151. legend_label <- if (fill_by_p_value && use_fdr) {
  152. "FDR"
  153. } else if (fill_by_p_value && !use_fdr) {
  154. "P-value"
  155. } else {
  156. "FC"
  157. }
  158. if (!cut_p_value) legend_label <- paste0("log10\n(", legend_label, ")")
  159. p <- p +
  160. labs(
  161. x = "Variable",
  162. y = "Subgroup",
  163. fill = legend_label
  164. ) +
  165. scale_x_discrete(expand = expansion(mult = c(0, 0))) +
  166. scale_y_discrete(expand = expansion(mult = c(0, 0)))
  167. if (add_text_annotation) {
  168. p <- p +
  169. geom_text(
  170. mapping = aes_string(
  171. label = if (fill_by_p_value) {
  172. "measure_observed"
  173. } else if (use_fdr) {
  174. "fdr"
  175. } else {
  176. "p_value"
  177. }
  178. ),
  179. size = 3
  180. )
  181. }
  182. p
  183. }