ggplot2 分面操作

本文主要内容翻译整理自:Easy multi-panel plots in R using facet_wrap() and facet_grid() from ggplot2,部分代码有修改。

ggplot2 一个非常强大的功能就是进行 multi-panel plots 的呈现,也就是我们常说的分面(facet)。通过使用facet_wrap() 或者 facet_grid() 这样的函数我们就可以很方面的将单一的一个图变为多个相关的图。本文将通过一个具体的数据示例帮助你理解 ggplot2 分面的不同方法以及参数。

数据准备集

为了纪念 Captain Marvel 和即将到来的 Avengers: Endgame ,我们将使用来自 Kaggle 的漫威角色数据集

我们将主要用到其中的3个变量信息:

  • YEAR: 角色第一次出现的年份
  • SEX: 角色的性别
  • ALIGN:角色的人设,包括好坏和中立

在进行分析之前,首先对数据进行几步清洗,比如去除上述三个变量存在缺失值的数据,对变量进行更简单的重命名,同时因为涉及到的角色太多我们只选择那些出现次数大于100次的角色。

library(ggplot2)
library(dplyr)

marvel <- readr::read_csv("marvel-wikia-data.csv")

marvel <- filter(marvel, SEX != "", ALIGN != "", Year != "") %>% 
  filter(!is.na(APPEARANCES), APPEARANCES>100) %>% 
  mutate(SEX = stringr::str_replace(SEX, "Characters", "")) %>% 
  arrange(desc(APPEARANCES)) %>%
  rename(gender = SEX) %>% 
  rename_all(tolower)

按照年份统计角色出现次数

在整篇文章中,我们将生成按年份分组的演员数来作为整个分析过程的开始,在某些情况下还会生成其他一些分组变量。对于这个初始图我们仅是按年进行简单的计算。

marvel_count <- count(marvel, year)
glimpse(marvel_count)
# glimpse 可以展示数据的观测和变量数量以及每一列的名字和尽可能多的列信息,和structure类似。
## Observations: 57
## Variables: 2
## $ year <dbl> 1939, 1940, 1941, 1943, 1944, 1947, 1948, 1949, 1950, 195...
## $ n    <int> 3, 5, 4, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 7, 20, 36, 34, 21,...

首先画一个由线点构成的单一图形。

ggplot(data = marvel_count, aes(year, n)) +
    geom_line(color = "steelblue",size = 1) +
    geom_point(color="steelblue") + 
    theme_classic() +
    labs(title = "New Marvel characters by year",
         subtitle = "(limited to characters with more than 100 appearances)",
         y = "Count of new characters", x = "")

使用 facet_wrap() 按照角色人设分面

首先按照year和alignment来统计数目

marvel_count <- count(marvel, year, align)
glimpse(marvel_count)
## Observations: 114
## Variables: 3
## $ year  <dbl> 1939, 1939, 1940, 1940, 1941, 1941, 1943, 1944, 1947, 19...
## $ align <chr> "Good Characters", "Neutral Characters", "Bad Characters...
## $ n     <int> 2, 1, 1, 4, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 6, 4,...

只需要在上面绘图命令的结尾加上+ facet_wrap(~ align) 就可以绘制按照 alignment 分面的 multi-panel plot

ggplot(data = marvel_count, aes(year, n)) +
    geom_line(color = "steelblue",size = 1) +
    geom_point(color="steelblue") + 
    theme_classic() +
    labs(title = "New Marvel characters by year",
         subtitle = "(limited to characters with more than 100 appearances)",
         y = "Count of new characters", x = "") +
    facet_wrap(~ align)

这张图拥有了更大的信息量,比如我们可以发现在1963和1964年出现了大量的坏蛋,随后则逐渐减少;而好人在后面还是一直在稳定的加入。在未特殊指定的情况下,这里 facet_wrap选择了一行展示三个图。

如果对 facet_wrap() 使用两个变量,其实只需要简单的使用 + 来进行链接。但是通常情况下,为了更好的调整布局,建议使用facet_grid()

marvel_count <- count(marvel, year, align, gender)
ggplot(data = marvel_count, aes(year, n)) +
    geom_line(color = "steelblue",size = 1) +
    geom_point(color="steelblue") + 
    theme_classic() +
    labs(title = "New Marvel characters by year",
         subtitle = "(limited to characters with more than 100 appearances)",
         y = "Count of new characters", x = "") +
    facet_wrap(~ align + gender)

按照 facet_grid() 指定行列进行绘图

facet_grid(row_variable ~ column_variable) 可以通过指定行和列来进行绘图,例如使用align 作为行变量,gender 作为列变量

ggplot(data = marvel_count, aes(year, n)) +
    geom_line(color = "steelblue",size = 1) +
    geom_point(color="steelblue") + 
    theme_classic() +
    labs(title = "New Marvel characters by year",
         subtitle = "(limited to characters with more than 100 appearances)",
         y = "Count of new characters", x = "") +
    facet_grid(align ~ gender)

如果想要排除行或者列变量可以通过.来进行代替。如下所示:

ggplot(data = marvel_count, aes(year, n)) +
    geom_line(color = "steelblue",size = 1) +
    geom_point(color="steelblue") + 
    theme_classic() +
    labs(title = "New Marvel characters by year",
         subtitle = "(limited to characters with more than 100 appearances)",
         y = "Count of new characters", x = "") +
    facet_grid(. ~ gender)
ggplot(data = marvel_count, aes(year, n)) +
    geom_line(color = "steelblue",size = 1) +
    geom_point(color="steelblue") + 
    theme_classic() +
    labs(title = "New Marvel characters by year",
         subtitle = "(limited to characters with more than 100 appearances)",
         y = "Count of new characters", x = "") +
    facet_grid(align ~ .)

颜色有时效果更好

在时间序列数据中,使用两条不同颜色的线有时比分面效率要更高。

# Limit to male and female and change levels for drawing order
marvel_count <- filter(marvel_count, gender%in%c("Female ", "Male ")) %>% 
    mutate(gender = factor(gender, levels = c("Male ", "Female ")))

ggplot(data = marvel_count, aes(year, n, color = gender)) +
    geom_line(size = 1) +
    geom_point() + 
    theme_classic() +
    labs(title = "New Marvel characters by gender",
         subtitle = "(limited to characters with more than 100 appearances)",
         y = "Count of new characters", x = "")

颜色和分面混用也不失为一个高效的选择。

ggplot(data = marvel_count, aes(year, n, color = gender)) +
    geom_line(size = 1) +
    geom_point() + 
    theme_classic() +
    labs(title = "New Marvel characters by alignment & gender",
         subtitle = "(limited to characters with more than 100 appearances)",
         y = "Count of new characters", x = "")+ 
    facet_grid(. ~ align) 

几个常用参数

在faceting 函数中,有一些参数是通用的,只是在使用略有差别。

nrow 或者 ncol

  • 只对 facet_wrap() 有效
  • 控制图形布局
ggplot(data = marvel_count, aes(year, n)) +
  geom_line(color = "steelblue",size = 1) +
  geom_point(color = "steelblue") + 
  theme_classic() +
  facet_wrap(~ gender + align, nrow = 2) + 
  labs(title = "New Marvel characters by gender & alignment",
       subtitle = "(using nrow=2)",
       y = "Count of new characters", x = "")
ggplot(data = marvel_count, aes(year, n)) +
  geom_line(color = "steelblue", size = 1) +
  geom_point(color ="steelblue") + 
  theme_classic() +
  facet_wrap(~ gender + align, ncol = 6) + 
  labs(title = "New Marvel Characters by gender & alignment",
       subtitle = "(using ncol=6)", 
       y = "Count of new characters", x = "") +
  theme(
       axis.text.x = element_text(angle=50, hjust=1)
  )

margins

  • 只对 facet_grid 有效
  • 增加额外的一个分面进行汇总
marvel_count <- 
    mutate(marvel_count, align = stringr::str_replace(align, "Characters", ""))

ggplot(data = marvel_count, aes(year, n)) +
    geom_line(color = "steelblue", size = 1) +
    geom_point(color = "steelblue") + 
    theme_classic() +
    labs(title = "New Marvel characters by alignment & gender",
         subtitle = "(margins= TRUE)",
         y = "Count of new characters", x = "") + 
    facet_grid(align ~ gender, margins=TRUE) 

自由定义不一致的Y轴

可以使用scales = "free" 或者 scales = "free_x" 或者 "free_y"进行设置。但是一定要注意这样的图可能会使读者造成误解。

ggplot(marvel_count, aes(year, n)) + 
    geom_line(color = "steelblue", size = 1) + 
    facet_wrap(~gender, scales = "free_y")+
    theme_classic() +
    labs(title = 'with "free" y axes' ,
         y = "Count of new Marvel characters")

space

  • facet_grid() 有效
  • 控制每个panel 的高和宽
  • 默认所有的panels有一样的size
  • 可以设置"free", "free_y" "free_x" 三个参数
  • 需要和scales = "free" 一起连用
ggplot(data = marvel_count, aes(year, n)) +
    geom_line(color = "steelblue", size = 1) +
    geom_point(color = "steelblue") + 
    theme_classic() +
    labs(title = "New Marvel characters by alignment & gender",
         subtitle = '(space = "free")',
         y = "Count of new characters", x = "") + 
    facet_grid(align ~ gender, space="free", scales="free") 

strip.position

  • facet_wrap() 可用
  • 控制facet subset labels
  • 有四个选项 "top" (default), "bottom", "left" 和 "right"
ggplot(marvel_count, aes(year, n)) + 
  geom_line(color = "steelblue", size = 1) + 
  theme_classic() +
  facet_wrap(~gender, strip.position = "right") + 
  labs(title = 'strip.postition = "right"',
       y = "Count of new Marvel characters")

switch

  • 进对facet_grid() 有效
  • 默认是右上角
  • x 会让label在底部,y 右改为左,both 则改为左下
ggplot(marvel_count, aes(year, n)) + 
    geom_line(color = "Steelblue", size = 1) + 
    theme_classic() +
    facet_grid(~gender, switch = "x"  ) + 
    labs(title = 'switch = "x"',
         y = "Count of new Marvel characters")


加入靠谱熊基地,和大家一起交流
添加我的微信
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容