【问题标题】:Make stacked proportion bar graph for categorical and numerical data in ggplot在ggplot中为分类和数值数据制作堆叠比例条形图
【发布时间】:2018-12-22 17:16:20
【问题描述】:

我有一个如下的数据框:

df = data.frame(Age = c(rep(NA, 10), runif(40, 1, 100)), 
                Duration = c(rep(NA, 20), runif(30, 0, 4)), 
                cat = rep(c("A", "B", "C", "D", NA), each = 10), 
                cat2 = rep(c("X", "Y", "Z", NA, "W"), each = 10))

值得注意的是,它包含两个数值列和两个分类列。每列都有一些 NA 值。

我想制作一个堆叠条形图,显示分类列中每个组的比例以及数字列的颜色条。类别的顺序与分类无关,除了我希望 NA 始终位于顶部,并且数字应从底部的最低到顶部的最大排序(但 NA 也位于顶部)。

以下是我尝试制作但没有取得多大成功的简要草图。对于数字颜色条,我想在条上的 5 点处注释四舍五入到最接近整数的值。

我首先将数据框融化以使其变长,但不确定如何从这里开始。

library(reshape) 
df_m = melt(df, id = c())

如果您能对此提供帮助,我将不胜感激。

谢谢, 杰克

【问题讨论】:

  • +1 用于包含草图,即使您无法使用代码制作它。我看到出现了一些美学问题:您的列在不同的比例上显示不同类型的数据,但每个图只能使用一种颜色/填充比例。拥有一个跨越 0-100 岁和 0-4 持续时间的颜色条是没有意义的。分类列的类似问题。用cowplotpatchwork 将它们离散地组合在一起,你可能会有更好的运气。
  • @camille 感谢您的支持和您的回复。凭借我非常有限的 ggplot 知识,我也不知道该怎么做。你可以用这个发布解决方案吗?我真的很感激。谢谢!

标签: r ggplot2


【解决方案1】:

这可能是一个比您希望的要复杂得多的问题,并且需要几个步骤,所以这个解决方案感觉有点笨拙。它也可能不是您想要的,但仍有调整的余地。

我首先要做的是将数值列分解成区间,即因子,确保字符向量是因子,并为每一列提供"NA" 的显式因子级别,而不是值类型NA .这是一个微妙的区别(您可以将此级别称为其他东西),但它允许您将此级别放在每个因素的末尾,因此NA 条将全部放置在顶部。但是,填充比例会自动为NA 分配一个灰度值,因此您必须手动执行此操作。我通过拉出 ColorBrewer 调色板“Blues”来做到这一点,然后在 scale_fill_manual 中将灰色放在它旁边。

library(tidyverse)
library(patchwork)

set.seed(123)
df <- data_frame(Age = c(rep(NA, 10), runif(40, 1, 100)), 
                 Duration = c(rep(NA, 20), runif(30, 0, 4)), 
                 cat = rep(c("A", "B", "C", "D", NA), each = 10), 
                 cat2 = rep(c("X", "Y", "Z", NA, "W"), each = 10))

df_breaks <- df %>%
  arrange(Age) %>%
  mutate(Age = cut(Age, breaks = seq(0, 100, by = 25)),
         Duration = cut(Duration, breaks = seq(0, 4, by = 1))) %>%
  mutate_if(is.character, as.factor) %>%
  mutate_all(~fct_explicit_na(., na_level = "NA"))

df_breaks
#> # A tibble: 50 x 4
#>    Age     Duration cat   cat2 
#>    <fct>   <fct>    <fct> <fct>
#>  1 (0,25]  (3,4]    NA    W    
#>  2 (0,25]  (1,2]    C     Z    
#>  3 (0,25]  NA       B     Y    
#>  4 (0,25]  (0,1]    C     Z    
#>  5 (0,25]  (1,2]    D     NA   
#>  6 (0,25]  (3,4]    NA    W    
#>  7 (0,25]  (1,2]    NA    W    
#>  8 (25,50] (0,1]    C     Z    
#>  9 (25,50] NA       B     Y    
#> 10 (25,50] (3,4]    D     NA   
#> # ... with 40 more rows

palette <- RColorBrewer::brewer.pal(4, "Blues")

要为每一列制作单独的图,我使用purrr::imap 在每一列上调用一个函数,使用该列的名称和列本身创建一个新的数据框,计算休息时间,并制作一个条形图。我添加了geom_text 来制作标签,这也可以让您跳过图例。 (就像我在评论中说的那样,传说会给你带来麻烦,因为所有的比例都是不同的。)我还删除了左侧和右侧的情节边距,这样你就可以将情节放在每个旁边其他,并删除 x 轴标题,这将是多余的。

p <- imap(df_breaks, function(col, term) {
  data_frame(term = term, group = col) %>%
    count(term, group) %>%
    ggplot(aes(x = term, y = n, fill = fct_rev(group))) +
      geom_col(position = "fill") +
      geom_text(aes(label = fct_rev(group)), position = position_fill(vjust = 0.5)) +
      scale_fill_manual(values = c("gray70", palette)) +
      theme_minimal() +
      theme(legend.position = "none", plot.margin = margin(10, 0, 10, 0, "pt")) +
      labs(x = NULL)
})

这将为您提供ggplot 对象的列表。我正在按照你显示的顺序重新排列它。

p <- p[c("Age", "cat", "Duration", "cat2")]

然后使用patchwork::wrap_plots,您可以将绘图列表放在一起。

wrap_plots(p, nrow = 1)

如果你想让它看起来像一个单独的情节,有一些冗余,所以你可以从情节 2、3 和 4 中删除左侧的主题元素,然后 wrap_plots 再次使用原始的p$Age

p_no_y <- map(p[2:4], function(plot) {
  plot +
    theme(axis.title.y = element_blank(),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank())
})
wrap_plots(p$Age, p_no_y$cat, p_no_y$Duration, p_no_y$cat2, nrow = 1)

使用patchwork 而不是cowplot 的优点是patchwork 函数知道每个绘图中轴所占用的空间,因此列的宽度相同,尽管事实上一个plot 也有一个 y 轴。要明白我的意思,请将wrap_plots 替换为cowplot::plot_grid

所以这已经很多了!而且还有很多空间可以做更多:

  • 您可以进一步调整边距和其他主题元素以及 x 轴和 y 轴,以按照您的需要将绘图组合在一起。
  • 如果您想为不同的列使用不同的调色板,例如一个连续的,就像我在这里展示的数字变量,但一个定性的分类变量 - 你可以单独分配填充比例,而不是像我在 imap 函数中所做的那样。
  • 您可能希望通过在cut 中提供标签来设置不在区间表示法中的中断标签。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-06
    • 2022-11-23
    • 2016-04-26
    • 2018-03-17
    • 2016-01-29
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    相关资源
    最近更新 更多