R 数据可视化 —— ggplot 主题

前言

主题系统控制图形中所有非数据元素的显示,如标题、坐标轴标签、图例标签等文本字体的调整,以及网格线、背景、刻度的颜色等。

ggplot2 采用数据与非数据分离的方式,在绘图时,首先确定数据的展示,然后在通过主题系统对细节进行调整。

ggplot2 内置了一些主题可供使用,也可以使用 theme() 函数来更改现有主题的图形元素

1. 内置主题

默认的主题有:

theme_grey(
  base_size = 11,
  base_family = "",
  base_line_size = base_size/22,
  base_rect_size = base_size/22
)

theme_gray(...)
theme_bw(...)
theme_linedraw(...)
theme_light(...)
theme_dark(...)
theme_minimal(...)
theme_classic(...)
theme_void(...)
theme_test(...)

这些默认主题都包含 4 个参数:

  • base_size:轴标题字体大小,以 pts 为单位。图形标题比它大 20%,刻度标签比它小 20%
  • base_family:字体
  • base_line_size:线条元素的大小
  • base_rect_size:矩形元素的大小

示例

对于如下图形

mtcars2 <- within(mtcars, {
  vs <- factor(vs, labels = c("V-shaped", "Straight"))
  am <- factor(am, labels = c("Automatic", "Manual"))
  cyl  <- factor(cyl)
  gear <- factor(gear)
})

p1 <- ggplot(mtcars2) +
  geom_point(aes(x = wt, y = mpg, colour = gear)) +
  labs(title = "Fuel economy declines as weight increases",
       subtitle = "(1973-74)",
       caption = "Data from the 1974 Motor Trend US magazine.",
       tag = "Figure 1",
       x = "Weight (1000 lbs)",
       y = "Fuel economy (mpg)",
       colour = "Gears")

我们可以更改不同的主题

  1. theme_gray
p1 + theme_gray()

这个是默认的主题,浅灰色背景,白色网格线

  1. theme_bw
p1 + theme_bw()

这个主题是白色背景,深灰色网格线。

  1. 其他主题
p3 <- p1 + theme_linedraw()
p4 <- p1 + theme_light()
p5 <- p1 + theme_dark()
p6 <- p1 + theme_minimal()
p7 <- p1 + theme_classic()
p8 <- p1 + theme_void()

plot_grid(p3, p4, p5, p6, p7, p8, labels = LETTERS[1:6], nrow = 3)

对于分面图形,也是一样的

p2 <- p1 + facet_grid(vs ~ am)

p3 <- p2 + theme_linedraw()
p4 <- p2 + theme_light()
p5 <- p2 + theme_dark()
p6 <- p2 + theme_minimal()
p7 <- p2 + theme_classic()
p8 <- p2 + theme_void()

plot_grid(p2, p2 + theme_bw(), p3, p4, p5, p6, p7, p8, nrow = 4)

2. 修改主题

修改全局主题之后,该主题会应用于之后绘制的所有图形中,全局主题设置主要包含以下几个函数

theme_get()

theme_set(new)

theme_update(...)

theme_replace(...)

其中,theme_get() 用于获取当前主题,theme_set(new) 使用新主题完全覆盖当前主题。

theme_updatetheme_replace 用于修改某一图形元素

示例

对于

p <- ggplot(mtcars, aes(mpg, wt)) +
  geom_point()
p

设置主题为 theme_bw()theme_set 会返回之前的主题

old <- theme_set(theme_bw())
p

使用 theme_update 设置主题中某一图形元素,只会修改设置的元素,未显式设置的元素不会修改

theme_update(panel.grid.minor = element_line(colour = "red"))
p

不同于 theme_update,使用 theme_replace 函数来修改某一元素的值时,未显式设置的元素值都会赋值为 NULL

theme_replace(panel.grid.minor = element_line(colour = "red"))
p

设置回默认主题

theme_set(old)
p
注意

theme_updatetheme_replace 两个函数分别通过调用 +%+replace% 来实现的。

我们可以打印主题对象来感受它们之间的区别

> add_el <- theme_grey() +
+   theme(text = element_text(family = "Times"))
> add_el$text
List of 11
 $ family       : chr "Times"
 $ face         : chr "plain"
 $ colour       : chr "black"
 $ size         : num 11
 $ hjust        : num 0.5
 $ vjust        : num 0.5
 $ angle        : num 0
 $ lineheight   : num 0.9
 $ margin       : 'margin' num [1:4] 0pt 0pt 0pt 0pt
  ..- attr(*, "valid.unit")= int 8
  ..- attr(*, "unit")= chr "pt"
 $ debug        : logi FALSE
 $ inherit.blank: logi FALSE
 - attr(*, "class")= chr [1:2] "element_text" "element"
> 
> rep_el <- theme_grey() %+replace%
+   theme(text = element_text(family = "Times"))
> rep_el$text
List of 11
 $ family       : chr "Times"
 $ face         : NULL
 $ colour       : NULL
 $ size         : NULL
 $ hjust        : NULL
 $ vjust        : NULL
 $ angle        : NULL
 $ lineheight   : NULL
 $ margin       : NULL
 $ debug        : NULL
 $ inherit.blank: logi FALSE
 - attr(*, "class")= chr [1:2] "element_text" "element"

我们可以看到,在使用 + 来更新主题元素时,只修改了 family 属性的值,其他值并未改动。

而在使用 %+replace% 修改主题时,只有设置的 family 属性有值,而其他所有属性值都被设置为 NULL

3. 修改主题元素

在主题系统中,使用 element_* 函数来指定如何显示或绘图非数据组件

  • element_blank:什么也不画,也不分配空间
  • element_rect:设置边框和背景
  • element_line:设置线条
  • element_text:设置文本

还有 rel() 用于指定相对于父节点的大小,margin() 用于指定元素的边距

margin(
  t = 0,       # top
  r = 0,       # right
  b = 0,       # bottom
  l = 0,       # left
  unit = "pt"  # 默认的尺寸单位,默认为 "pt"。
)

element_blank()

element_rect(
  fill = NULL,           # 填充色
  colour = NULL,         # 边框颜色
  size = NULL,           # 边框大小,单位 mm
  linetype = NULL,       # 边框线条类型
  color = NULL,          # colour 的别名
  inherit.blank = FALSE  # 是否从父元素中继承 element_blank 空白元素
)

element_line(
  colour = NULL,         # 线条颜色
  size = NULL,           # 线条大小,单位 mm
  linetype = NULL,       # 线条类型
  lineend = NULL,        # 线条末端样式(ound, butt, square)
  color = NULL,          # 同上
  arrow = NULL,          # 箭头样式
  inherit.blank = FALSE  # 同上
)

element_text(
  family = NULL,         # 字体家族
  face = NULL,           # 粗斜体等"plain", "italic", "bold", "bold.italic"
  colour = NULL,         # 字体颜色
  size = NULL,           # 字体大小,单位 pts
  hjust = NULL,          # 水平对齐([0,1])
  vjust = NULL,          # 竖直对齐([0,1])
  angle = NULL,          # 旋转角度([0,360])
  lineheight = NULL,     # 行高
  color = NULL,          # 同上
  margin = NULL,         # 文本边距
  debug = NULL,          # 在文本区域后面添加实心矩形
  inherit.blank = FALSE  # 同上
)

rel(
  x  # 指定相对于父元素大小的数值
)

4. 主题元素

通过上面的介绍,我们对如何设置主题应该有了较全面的了解了。

但是光知道如何设置主题元素是不行的,我们还要知道能够设置哪些主题元素。

我们可以使用 theme() 函数来设置图中非数据元素,例如标题、标签、字体、背景、网格线和图例等,具体的参数如下

主题元素按层次结构从其他主题元素中继承属性,例如 axis.title.x.bottom 继承自 axis.title.x,而其又是继承自 axis.title,又继承自最顶层的 text。

也就是说,所有的文本元素继承自 text,所有的线条继承自 line,所有的矩形对象继承自 rect。因此,您可以通过设置一个顶层元素来修改多个子元素的的属性

1. 简单示例

p <- ggplot(mpg, aes(displ, hwy)) + geom_point()

p1 <- p + theme(
  panel.background = element_blank(),
  axis.text = element_blank()
)

p2 <- p + theme(
  axis.text = element_text(colour = "red", size = rel(1.5))
)

p3 <- p + theme(
  axis.line = element_line(arrow = arrow())
)

p4 <- p + theme(
  panel.background = element_rect(fill = "white"),
  plot.margin = margin(2, 2, 2, 2, "cm"),
  plot.background = element_rect(
    fill = "grey90",
    colour = "black",
    size = 1
  )
)

plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4], nrow = 2)

2. plot 元素设置

p1 <- ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  labs(title = "Fuel economy declines as weight increases")

p2 <- p1 + theme(plot.title = element_text(size = rel(2)))

p3 <- p1 + theme(plot.background = element_rect(fill = "green"))

plot_grid(p2, p3, labels = LETTERS[1:2], nrow = 2)

3. panel 元素设置

p2 <- p1 + theme(panel.background = element_rect(fill = "white", colour = "grey50"))

p3 <- p1 + theme(panel.border = element_rect(linetype = "dashed", fill = NA))

p4 <- p1 + theme(panel.grid.major = element_line(colour = "black"))

p5 <- p1 + theme(
  panel.grid.major.y = element_blank(),
  panel.grid.minor.y = element_blank()
)

plot_grid(p2, p3, p4, p5, labels = LETTERS[1:4], nrow = 2)

将网格放置在数据之上

p1 + theme(
  panel.background = element_rect(fill = NA),
  panel.grid.major = element_line(colour = "grey50"),
  panel.ontop = TRUE
)

4. 更改轴文本和线条的样式

p2 <- p1 + theme(axis.line = element_line(size = 3, colour = "grey80"))

p3 <- p1 + theme(axis.text = element_text(colour = "blue"))

p4 <- p1 + theme(axis.ticks = element_line(size = 2))

# 更改 y 轴的外观
p5 <- p1 + theme(axis.title.y = element_text(size = rel(1.5), angle = 90))

plot_grid(p2, p3, p4, p5, labels = LETTERS[1:4], nrow = 2)

设置 x 轴的刻度向内,y 轴的刻度向外

p1 + theme(
  axis.ticks.length.y = unit(.25, "cm"),
  axis.ticks.length.x = unit(-.25, "cm"),
  axis.text.x = element_text(margin = margin(t = .3, unit = "cm"))
)

5. 图例

p2 <- ggplot(mtcars, aes(wt, mpg)) +
  geom_point(aes(colour = factor(cyl), shape = factor(vs))) +
  labs(
    x = "Weight (1000 lbs)",
    y = "Fuel economy (mpg)",
    colour = "Cylinders",
    shape = "Transmission"
  )
p2

图例位置

p3 <- p2 + theme(legend.position = "none")

p4 <- p2 + theme(legend.justification = "top")

p5 <- p2 + theme(legend.position = "bottom")

# 使用 0-1 之间的相对坐标值
p6 <- p2 + theme(
  legend.position = c(.95, .95),
  legend.justification = c("right", "top"),
  legend.box.just = "right",
  legend.margin = margin(6, 6, 6, 6)
)

plot_grid(p3, p4, p5, p6, labels = LETTERS[1:4], nrow = 2)

设置图例的框线、键、标签和标题

# 设置图例框线
p3 <- p2 + theme(
  legend.box.background = element_rect(),
  legend.box.margin = margin(6, 6, 6, 6)
)
# 设置图例的键
p4 <- p2 + theme(legend.key = element_rect(fill = "white", colour = "black"))
# 设置图例标签
p5 <- p2 + theme(legend.text = element_text(size = 8, colour = "red"))
# 设置图例标题
p6 <- p2 + theme(legend.title = element_text(face = "bold"))

plot_grid(p3, p4, p5, p6, labels = LETTERS[1:4], nrow = 2)

6. 条带(Strips)

p3 <- ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  facet_wrap(~ cyl)

p4 <- p3 + theme(strip.background = element_rect(colour = "black", fill = "white"))

p5 <- p3 + theme(strip.text.x = element_text(colour = "white", face = "bold"))

p6 <- p3 + theme(panel.spacing = unit(1, "lines"))

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

推荐阅读更多精彩内容