R 数据可视化 —— ggplot 柱状图/条形图
前言
ggplot2
中有两种绘制条形图的函数:geom_bar()
和 geom_col()
geom_bar()
使条形的高度与每个组中的观察值的数目成正比,或者如果设置了 weight
参数,则为分组内指定的所有权重变量值之和
如果你想直接使用条形图的高度来表示数据中的值,可以使用 geom_col()
geom_bar()
默认使用的统计变换方法是 count
,而 geom_col(
) 使用 identity
不做变换。
示例
1 简单条形图
g <- ggplot(mpg, aes(class))
g + geom_bar()
使用 weight
参数来统计分组内 displ
变量值之和
g + geom_bar(aes(weight = displ))
绘制水平条形图,有两种方式,反转坐标轴或者将数据设置在 y
轴
p1 <- ggplot(mpg) + geom_bar(aes(y = class))
p2 <- g + geom_bar() + coord_flip()
plot_grid(p1, p2, labels = LETTERS[1:2], ncol = 2)
推荐使用翻转坐标轴的方式,因为有些图形是无法修改属性映射的。
使用 geom_col
绘制条形图
df <- data.frame(trt = c("a", "b", "c"), outcome = c(2.3, 1.9, 3.2))
ggplot(df, aes(trt, outcome)) +
geom_col()
2. 设置颜色
p1 <- g + geom_bar(color = 'blue', fill='white')
p2 <- g + geom_bar(aes(fill=class))
p3 <- g + geom_bar(aes(colour=class), fill='white')
p4 <- g + geom_bar(aes(fill=class)) +
scale_fill_manual(values = c("#8c510a", "#d8b365", "#f6e8c3",
"#c7eae5", "#5ab4ac", "#01665e", "#af8dc3"))
plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4], ncol = 2)
3. 分组条形图
3.1 堆积条形图
简单堆积条形图
g <- ggplot(mpg, aes(class))
g + geom_bar(aes(fill = drv))
翻转堆积条形图,并反转堆积顺序
ggplot(mpg, aes(y = class)) +
geom_bar(aes(fill = drv), position = position_stack(reverse = TRUE)) +
theme(legend.position = "top")
本来的堆积顺序是,蓝色在最下面,然后是绿色,最后是粉色,使用 position_stack
将该顺序逆转
那如何在堆叠块之间设置空白间距呢?
我们可以先设置 lwd
参数,增加外框线的宽度,然后将外框线的颜色设置成背景颜色,就可以达到目的了
那我不知道背景颜色怎么办?也很简单。
我们在主题章节介绍过 theme_get
函数可以获取当前主题,只要找到当前主题的 panel.background
属性,并找到对应的填充色 fill
就可以知道啦。
> old<- theme_get()
> old$panel.background
List of 5
$ fill : chr "grey92"
$ colour : logi NA
$ size : NULL
$ linetype : NULL
$ inherit.blank: logi TRUE
- attr(*, "class")= chr [1:2] "element_rect" "element"
好了,现在知道了背景色为 grey92
,那么我就可以绘制了
g <- ggplot(mpg, aes(class))
g + geom_bar(aes(fill = drv), lwd=1.5, colour='grey92')
当然,你也可以指定分隔区的颜色,然后将背景色改为相应的颜色就可以了
3.2 百分比条形图
如果我们想知道每个分组中各部分的占比情况,可以设置 position = 'fill'
或者 position_fill()
函数来绘制百分比条形图,例如
g + geom_bar(aes(fill = drv),
position = 'fill')
3.3 并列条形图
条形图默认会绘制堆积条形图,我们可以使用 position_dodge
和 position_dodge2
来绘制并列条形图
position_dodge(width = NULL, preserve = c("total", "single"))
position_dodge2(
width = NULL,
preserve = c("total", "single"),
padding = 0.1,
reverse = FALSE
)
我们来看看这两个函数的区别,当不传递参数时,可以传入字符串 dodge
或 dodge2
,分别代表这两个函数
p1 <- g + geom_bar(aes(fill = drv), position = position_dodge())
p2 <- g + geom_bar(aes(fill = drv), position = position_dodge2())
plot_grid(p1, p2, labels = LETTERS[1:2], ncol = 2)
我们可以看到,dodge
同一组内的柱子是靠近在一起的,而 dodge2
有空白间距。
由于默认情况下,会保持分组的宽度一致,这就造成分类少的组内柱子宽度更大,我们可以设置 preserve = 'single'
保持每个柱子的宽度是一样的。
p1 <- g + geom_bar(aes(fill = drv), position = position_dodge(preserve = 'single'))
p2 <- g + geom_bar(aes(fill = drv), position = position_dodge2(preserve = 'single'))
plot_grid(p1, p2, labels = LETTERS[1:2], ncol = 2)
组内柱子之间的间距怎么设置呢?这两个函数的设置方式会有些差别
p1 <- g + geom_bar(aes(fill = drv),
position = position_dodge(
preserve = 'single', width = 0.5))
p2 <- g + geom_bar(aes(fill = drv),
position = position_dodge(
preserve = 'single', width = 1))
p3 <- g + geom_bar(aes(fill = drv),
position = position_dodge2(
preserve = 'single', padding = 0.5))
p4 <- g + geom_bar(aes(fill = drv),
position = position_dodge2(
preserve = 'single', padding = 1.2))
plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4], ncol = 2)
我们可以看到,图 A
、B
设置的是分组总的宽度,宽度小于 1
会导致组内柱子之间交叠,如果大于 1
会导致组间出现交叠
而图 C
、D
通过设置不同的 padding
值,会影响柱子的宽度,通过压缩柱子的宽度来增加组内间距。
3.3 设置条形图误差线
我们在并列条形图的基础上,绘制误差线
mpg %>%
group_by(class, drv) %>%
summarise(count = n()) %>%
ggplot(aes(class, count)) +
geom_col(aes(fill=drv), position = position_dodge2(preserve = 'single')) +
geom_errorbar(aes(ymin = count - 1, ymax = count + 1),
position = position_dodge2(preserve = 'single', padding = 0.5))
因为我们在绘制误差线时需要用到每个柱子的大小,所以在这里,我们先对数据进行了汇总,然后使用 geom_col
绘制条形图。
3.4 添加标注
有时候,我们可能想知道条形图的每个柱子的具体数值或占比情况,需要为每个柱子添加文本注释信息
mpg %>%
group_by(class, drv) %>%
summarise(count = n()) %>%
ggplot(aes(class, count)) +
geom_col(aes(fill=drv), position = position_dodge2(preserve = 'single')) +
geom_text(aes(label=count),
position = position_dodge2(width = 0.9, preserve = 'single'),
vjust = -0.2, hjust = 0.5)
我们使用 geom_text
添加标签注释,有几点需要注意
注意事项:
geom_text
中设置的position
信息需要与条形图中的设置对应,即堆叠对应堆叠,并列对应并列,上面的例子是并列的方式。柱子宽度需要一致,默认为
0.9
。同时,如果不设置preserve = 'single'
,则宽度为整个分组的宽度。
vjust
设置为负数是将文本上移,hjust=0.5
为了水平对齐。
堆叠的例子
mpg %>%
group_by(class, drv) %>%
summarise(count = n()) %>%
mutate(cumcount = cumsum(count)) %>%
ggplot(aes(class, count)) +
geom_col(aes(fill=drv), position = position_stack(reverse = TRUE)) +
geom_text(aes(label = cumcount),
position = position_stack(), # 可以不设置该参数
vjust = 0.5, hjust=0.5)
我们看到,标签都是放置在上面,那我想要把标签放中间怎么办?
mpg %>%
group_by(class, drv) %>%
summarise(count = n()) %>%
mutate(cumcount = cumsum(count), midcount = cumcount - count/2) %>%
ggplot(aes(class, count)) +
geom_col(aes(fill = drv), position = position_stack(reverse = TRUE)) +
geom_text(aes(y = midcount, label = cumcount), hjust=0.5)
注意,我们不再为 geom_text
的 position
参数设置值,因为不需要根据 y
的值进行堆叠
4. 条形图的变形
4.1 金字塔图
df <- tibble(
gene = factor(paste0("gene_", rep(1:16, 2)), levels = paste0("gene_", 16:1)),
stat = c(seq(-10, -100, -10), seq(-90, -40, 10), seq(10, 100, 10), seq(90, 40, -10)),
direct = rep(c("down", "up"), each=16)
)
ggplot(df, aes(gene, stat, fill = direct)) +
geom_col() +
coord_flip() +
scale_y_continuous(breaks = seq(-100, 100, 20),
labels = c(seq(100, 0, -20), seq(20, 100, 20)))
4.2 偏差图
df <- tibble(
gene = factor(paste0("gene_", 1:20), levels = paste0("gene_", 20:1)),
stat = c(seq(100, 10, -10), seq(-10, -100, -10)),
direct = factor(rep(c("up", "down"), each=10), levels = c("up", "down"))
)
ggplot(df, aes(gene, stat, fill = direct)) +
geom_col() +
coord_flip()