为ggplot中的分类和数值数据制作堆叠的比例条形图

时间:2018-12-22 17:16:21

标签: r ggplot2

我有一个如下数据框:

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点处注释四舍五入到最接近的整数的值。

enter image description here

我首先将数据帧融化以使其更长,但是不确定如何从此处继续。

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

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

谢谢, 杰克

1 个答案:

答案 0 :(得分:3)

这可能是比您希望的要复杂得多的问题,并且分几个步骤进行,因此此解决方案感觉有些棘手。可能也不一定正是您要的东西,但仍有调整的余地。

我首先要做的是将数字列分解为区间,将区间划分为因子,确保字符向量为因子,并为每列赋予明确的因子级别"NA",而不是值类型{ {1}}。这是一个细微的差异(您可以将此级别称为其他级别),但是它可以让您将此级别放在每个因子的末尾,因此NA条形图都将放在顶部。但是,填充比例会自动为NA指定一个灰色值,因此您必须手动执行此操作。为此,我将拉出ColorBrewer调色板“蓝色”,然后在NA的旁边放置灰色。

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来制作标签,这也使您可以跳过图例。 (就像我在评论中说的那样,图例会给您带来麻烦,因为所有比例都不同。)我还要删除左侧和右侧的绘图边距,以便您可以在每个绘图旁边并排放置绘图其他,并删除x轴标题,这将是多余的。

geom_text

这将为您提供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

如果您希望将其看起来像是单个图,则有一些冗余,因此您可以从图2、3和4中删除左侧主题元素,然后使用原始的{{1}再次删除wrap_plots(p, nrow = 1) }}:

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函数知道每个图在坐标轴上占据的空间,因此列的宽度相同,尽管一个图也具有y轴。要了解我的意思,请将cowplot替换为patchwork

所以已经很多了!还有足够的空间做更多的事情:

  • 您可以进一步调整边距和其他主题元素以及x轴和y轴,以根据需要将图拟合在一起。
  • 如果您要为不同的列使用不同的调色板,例如像我在这里显示的用于连续变量的连续变量,但是对于分类变量定性的连续变量,您可以分别分配填充比例,而不是像在wrap_plots函数中一样。
  • 您可能想通过在cowplot::plot_grid中提供标签来设置间隔标记以外的中断标签。