ggplot2系列教程 第6节——主题(下)
往期中的内容多多少少有提到过如何通过theme系统来修改某些样式,到上一期我们也了解到了如何使用预先定义好的主题来对我们的图片进行一键修改,那么今天我们细细道来如何一步步精修图片。
初始图:
library(ggplot2)
library(ggthemes)
set.seed(100)
DT <- iris
DT$Class <- sample(LETTERS[1:2], nrow(DT), replace = T)
p <- ggplot(data = DT, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(aes(color = Petal.Length, size = Petal.Width, shape = Species)) +
scale_color_gradient(low = "#f0cf61", high = "#371722") +
scale_size_continuous(range = c(1, 4)) +
theme_igray() +
labs(x = "Sepal Length", y = "Sepal Width", title = "Iris Sepal")
p
element
要使用theme系统,首先逃不过以下元素:
函数 | 说明 |
---|---|
margin() | 边距 |
element_rect() | 矩形元素(修改背景或边框) |
element_line() | 线条元素 |
element_text() | 文本元素 |
element_blank() | 空白,不绘制任何内容 |
unit() | 单位 |
rel() | 相对父对象的大小 |
我们来熟悉一下这些函数的参数:
边距
margin(t = 0, r = 0, b = 0, l = 0, unit = "pt")
# 几个参数分别对应上/右/下/左的边界距离,以及距离单位
以绘图边距为例(其他边距同理):
p + theme(plot.margin = margin(t = 5, r = 2, b = 1, l = 10, unit = "cm"))
矩形元素
element_rect(
fill = NULL, # 填充色
colour = NULL, # 边框色
linewidth = NULL, # 边框线粗细
linetype = NULL, # 边框线线型 (0-8 或 blank, solid, dashed, dotted, dotdash, longdash, twodash)
color = NULL, # 边框色
inherit.blank = FALSE # 是否继承父元素中存在的element_blank
)
以面板背景为例(其他矩形元素同理):
p + theme(panel.background = element_rect(fill = "#FFFACD",
color = "red",
linewidth = 5,
linetype = "dashed"))
线条元素
element_line(
colour = NULL, # 线条颜色
linewidth = NULL, # 线条粗细
linetype = NULL, # 线型 (0-8 或者 blank, solid, dashed, dotted, dotdash, longdash, twodash)
lineend = NULL, # 线条端点样式 (round, butt, square)
color = NULL, # 线条颜色
arrow = NULL, # 箭头
inherit.blank = FALSE # 是否继承父元素中存在的element_blank
)
以坐标轴轴线为例(其他线条元素同理):
p + theme(axis.line.x = element_line(color = "blue",
linewidth = 3,
linetype = 1,
lineend = "square",
arrow = NULL),
axis.line.y = element_line(color = "red",
linewidth = 5,
linetype = 5,
lineend = "round",
arrow = arrow(angle = 40)))
文本元素
element_text(
family = NULL, # 字体
face = NULL, # 字体粗细 (plain, italic, bold, bold.italic)
colour = NULL, # 字体颜色
size = NULL, # 字体大小
hjust = NULL, # 字体水平对齐 [0,1]
vjust = NULL, # 字体垂直对齐 [0,1]
angle = NULL, # 字体角度 [0,360]
lineheight = NULL, # 字体行距
color = NULL, # 字体颜色
margin = NULL, # 文本周围边距
debug = NULL, # 是否进行视觉调试
inherit.blank = FALSE # 是否继承父元素中存在的element_blank
)
以坐标轴标签为例(其他文本元素同理):
p + theme(axis.text.x = element_text(family = "serif",
face = "bold",
color = "red",
size = 50,
hjust = 0,
margin = margin(t = 1,unit = "cm"),
debug = T),
axis.text.y = element_text(face = "italic",
color = "blue",
size = 30,
vjust = 1,
angle = 30))
空白
element_blank() # 无参数,参数后面直接跟这个函数即可清除指代的元素
如删除所有文本内容:
p + theme(text = element_blank())
单位
unit(x, # 数值
units, # 单位
data=NULL # 特殊单位类型
)
以图例图标宽度为例:
p + theme(legend.key.width = unit(2, "cm"))
相对大小
先来查看一下当前标题大小:
p$theme$text$size # 基本文字大小
p$theme$plot.title$size # 当前标题大小
如上,此时的标题为1.2倍基本文字大小即12✖1.2=14.4
使用rel()函数即可直接修改此倍数,如:
p_rel <- p + theme(plot.title = element_text(size = rel(2)))
p_rel
此时的文字大小即为1.2✖2=24
theme系统
接下来我们来瞧一瞧theme系统的组成,以及修改对应的参数要使用哪种元素修改函数:
theme(
line, # 设置默认线条元素 element_line()
rect, # 设置默认矩形元素 element_rect()
text, # 设置默认文本元素 element_text()
title, # 设置默认标题元素 element_text()
aspect.ratio, # 面板纵横比 (Y轴长度与X轴长度的比值)
# ------------------------------------------------------
# 坐标轴标题 👉 element_text()
axis.title, # 同时修改XY轴标题
axis.title.x, # 修改X轴标题
axis.title.x.top, # 仅修改顶部X轴标题
axis.title.x.bottom, # 仅修改底部X轴标题
axis.title.y, # 修改Y轴标题
axis.title.y.left, # 仅修改左侧Y轴标题
axis.title.y.right, # 仅修改右侧Y轴标题
# 坐标轴文字标签 👉 element_text()
axis.text, # 同时修改XY轴文字标签
axis.text.x, # 修改X轴文字标签
axis.text.x.top, # 仅修改顶部X轴文字标签
axis.text.x.bottom, # 仅修改底部X轴文字标签
axis.text.y, # 修改Y轴文字标签
axis.text.y.left, # 仅修改左侧Y轴文字标签
axis.text.y.right, # 仅修改右侧Y轴文字标签
# 坐标轴刻度 👉 element_line()
axis.ticks, # 同时修改XY轴刻度
axis.ticks.x, # 修改X轴刻度
axis.ticks.x.top, # 仅修改顶部X轴刻度
axis.ticks.x.bottom, # 仅修改底部X轴刻度
axis.ticks.y, # 修改Y轴刻度
axis.ticks.y.left, # 仅修改左侧Y轴刻度
axis.ticks.y.right, # 仅修改右侧Y轴刻度
# 坐标轴刻度线长度 👉 unit()
axis.ticks.length, # 同时修改XY轴刻度线长度
axis.ticks.length.x, # 修改X轴刻度线长度
axis.ticks.length.x.top, # 仅修改顶部X轴刻度线长度
axis.ticks.length.x.bottom, # 仅修改底部X轴刻度线长度
axis.ticks.length.y, # 修改Y轴刻度线长度
axis.ticks.length.y.left, # 仅修改左侧Y轴刻度线长度
axis.ticks.length.y.right, # 仅修改右侧Y轴刻度线长度
# 坐标轴轴线 👉 element_line()
axis.line, # 同时修改XY轴轴线
axis.line.x, # 修改X轴轴线
axis.line.x.top, # 仅修改顶部X轴轴线
axis.line.x.bottom, # 仅修改底部X轴轴线
axis.line.y, # 修改Y轴轴线
axis.line.y.left, # 仅修改左侧Y轴轴线
axis.line.y.right, # 仅修改右侧Y轴轴线
# ------------------------------------------------------
# 图例背景 👉 element_rect()
legend.box.background, # 修改整个图例区域的背景
legend.background, # 修改每一个图例的背景
legend.key, # 修改图例图标的背景
# 图例位置 👉 none/left/right/bottom/top 或数值如c(0.5, 0.5)
legend.position, # 修改图例位置
# 图例边距 👉 margin()
legend.box.margin, # 修改整个图例区域的边界距离
legend.margin, # 修改每一个图例的边界距离
# 图例间距 👉 unit()
legend.box.spacing, # 修改图例与绘图区域间距
legend.spacing, # 修改图例之间的间距
legend.spacing.x, # 修改X轴方向图例间距(水平方向)
legend.spacing.y, # 修改Y轴方向图例间距(垂直方向)
# 图例排列方向 👉 horizontal (水平排列)/vertical (垂直排列)
legend.box, # 修改多个图例整体的排列方向
legend.direction, # 修改每个图例所有元素的排列方向
# 图例区域对齐方式 👉 left/right/bottom/top
legend.box.just, # 修改图例区域的对齐方式
# 图例对齐方式 👉 center/left/right/bottom/top
# 也可用两个数值向量传递,如 c(0,1)
legend.justification, # 修改图例对齐方式
# 图例图标大小 👉 unit()
legend.key.size, # 修改图例图标大小
legend.key.height, # 修改图例图标高度
legend.key.width, # 修改图例图标宽度
# 图例文字标签/标题 👉 element_text()
legend.text, # 修改图例标签
legend.title, # 修改图例标题
# 图例文字标签/标题对齐方式 👉 0 (左对齐)/1 (右对齐)
legend.text.align, # 修改图例文字标签对齐方式
legend.title.align, # 修改图例标题对齐方式
# ------------------------------------------------------
# 面板背景 👉 element_rect()
panel.background, # 修改面板背景(图的底部)
panel.border, # 面板边界(图的上面,所以填充色要设置为NA,否则会覆盖绘图)
# 网格线 👉 element_line()
panel.grid.major, # 修改主要网格线
panel.grid.major.x, # 修改X轴主要网格线
panel.grid.major.y, # 修改Y轴主要网格线
panel.grid.minor, # 修改次要网格线
panel.grid.minor.x, # 修改X轴次要网格线
panel.grid.minor.y, # 修改Y轴次要网格线
# 面板及网格线是否置于图层顶部 👉 TRUE/FALSE
panel.ontop, # 面板及网格线是否置于图层顶部
# 分面面板间距 👉 unit()
panel.spacing, # 修改面板间距
panel.spacing.x, # 修改X轴方向面板间距(水平方向)
panel.spacing.y, # 修改Y轴方向面板间距(垂直方向)
# ------------------------------------------------------
# 分面标签背景 👉 element_rect()
strip.background, # 修改分面标签背景
strip.background.x, # 修改X轴方向分面标签背景(水平方向)
strip.background.y, # 修改Y轴方向分面标签背景(垂直方向)
# 分面标签位置 👉 inside/outside
# (仅当分面标签与坐标轴在同一位置时生效)
strip.placement, # 分面标签位置
# 分面标签文字 👉 element_text()
strip.text, # 修改所有分面标签
strip.text.x, # 仅修改X轴方向分面标签
strip.text.x.bottom, # 仅修改底部X轴方向分面标签
strip.text.x.top, # 仅修改顶部Y轴方向分面标签
strip.text.y, # 仅修改Y轴方向分面标签
strip.text.y.left, # 仅修改左侧Y轴方向分面标签
strip.text.y.right, # 仅修改右侧Y轴方向分面标签
# 是否裁剪分面标签 👉 inherit(继承父对象)/on(裁剪)/off(不裁剪)
strip.clip, # 裁剪分面标签至背景长度
# ------------------------------------------------------
# 整个图片
# 图片背景 👉 element_rect()
plot.background, # 修改图片背景(整张图背景)
# 图片标题/说明文字/标识 👉 element_text()
plot.title, # 修改标题
plot.subtitle, # 修改子标题
plot.caption, # 图片说明
plot.tag, # 图片标识
# 图片标题/说明文字对齐方式 👉 panel(与绘图面板对齐)/plot(与整幅图对齐)
plot.title.position, # 修改标题/子标题位置
plot.caption.position, # 修改图片说明位置
# 图片标识对齐方式 👉topleft/top/topright/left/right/bottomleft/bottom/
# bottomright 或 坐标
plot.tag.position, # 修改图片标识位置
# 绘图边距 👉 unit()
plot.margin, # 修改整个绘图区域的边界距离
...,
# 是否为完整主题/是否进行检查 👉 TRUE/FALSE
complete = FALSE,
validate = TRUE
)
这么多参数,如何快速熟悉呢?
坐标轴
用一张图总结下坐标轴相关参数:
以坐标轴文字为例,坐标轴标签的字体都设置为红色,大小为20:
p + theme(axis.text = element_text(color = "red", size = 20))
只修改X轴的标签:
p + theme(axis.text.x = element_text(color = "red", size = 20))
只修改顶部X轴标签啥意思呢?首先你得有个顶部X轴:
p + scale_x_continuous(position = "top")
然后可以修改了:
p + scale_x_continuous(position = "top") +
theme(axis.text.x.top = element_text(color = "red", size = 20))
坐标轴其它元素修改照葫芦画瓢即可。
图例
图例有些许复杂,咱们来一一演示:
图例背景:
p_legend <- p + theme(legend.box.background = element_rect(fill = "#f0cf61"),
legend.background = element_rect(fill = "#7FFFD4"),
legend.key = element_rect(fill = "#AB82FF"))
p_legend
图例位置:
p_legend + theme(legend.position = "top")
p_legend + theme(legend.position = c(0.5, 0.5))
图例边距:
p_legend + theme(legend.box.margin = margin(2, 2, 2, 2, "cm"))
p_legend + theme(legend.margin = margin(2, 2, 2, 2,"cm"))
图例间距:
p_legend + theme(legend.box.spacing = unit(2, "cm"))
p_legend + theme(legend.spacing = unit(2, "cm"))
p_legend + theme(legend.spacing.x = unit(2, "cm"))
p_legend + theme(legend.spacing.y = unit(2, "cm"))
图例排列方向:
p_legend + theme(legend.box = "horizontal")
p_legend + theme(legend.direction = "horizontal")
图例区域对齐方式:
p_legend + theme(legend.position = "top", legend.box.just = "top")
p_legend + theme(legend.position = "top", legend.box.just = "bottom")
p_legend + theme(legend.box.just = "left")
p_legend + theme(legend.box.just = "right")
图例对齐方式:
p_legend + theme(legend.justification = "center")
p_legend + theme(legend.justification = c(0, 0.5))
p_legend + theme(legend.justification = "bottom")
p_legend + theme(legend.justification = c(0, 0))
p_legend + theme(legend.justification = "top")
p_legend + theme(legend.justification = c(0, 1))
图例图标大小:
p_legend + theme(legend.key.size = unit(2, "cm"))
p_legend + theme(legend.key.height = unit(2, "cm"))
p_legend + theme(legend.key.width = unit(2, "cm"))
图例文字标签/标题:
p_legend + theme(legend.text = element_text(color = "red"),
legend.title = element_text(color = "blue"))
图例文字标签/标题对齐方式:
p_legend + theme(legend.text.align = 0, legend.title.align = 0)
p_legend + theme(legend.text.align = 0.5, legend.title.align = 0.5)
p_legend + theme(legend.text.align = 1, legend.title.align = 1)
面板
面板背景
p + theme(panel.background = element_rect(fill = "#f0cf61", color = "red"))
p + theme(panel.border = element_rect(fill = "#f0cf61", color = "red"))
网格线
p + theme(panel.grid.major = element_line(color = "red", linewidth = 2),
panel.grid.minor = element_line(color = "blue", linewidth = 2))
p + theme(panel.grid.major.x = element_line(color = "#f0cf61", linewidth = 2),
panel.grid.major.y = element_line(color = "darkgreen", linewidth = 2),
panel.grid.minor.x = element_line(color = "pink", linewidth = 2),
panel.grid.minor.y = element_line(color = "purple", linewidth = 2))
面板及网格线置于图层顶部
p + theme(panel.ontop = F)
p + theme(panel.ontop = T)
接下来的演示需要分面,我们来创建一个分面图
p_facet <- p + facet_grid(Class ~ Species) +
scale_x_continuous(position = "top") +
scale_y_continuous(position = "right")
p_facet
面板间距
p_facet + theme(panel.spacing = unit(2, "cm"))
p_facet + theme(panel.spacing.x = unit(2, "cm"))
p_facet + theme(panel.spacing.y = unit(2, "cm"))
分面标签
分面标签背景
p_facet + theme(strip.background = element_rect(fill = "#f0cf61", color = "red"))
p_facet + theme(strip.background.x = element_rect(fill = "#f0cf61", color = "red"),
strip.background.y = element_rect(fill = "pink", color = "blue"))
分面标签位置
p_facet + theme(strip.placement = "inside")
p_facet + theme(strip.placement = "outside")
分面标签文字
p_facet + theme(strip.text.x = element_text(color = "red", size = 20),
strip.text.y = element_text(color = "blue", size = 20))
是否裁剪分面标签
当分面标签过长超出背景长度时,是否裁剪文本:
p_facet + theme(strip.text.x = element_text(size = 50),
strip.clip = "on")
p_facet + theme(strip.text.x = element_text(size = 50),
strip.clip = "off")
整个图片
图片背景及标题/说明文字/标识
p_text <- p + labs(title = "title",
subtitle = "subtitle",
caption = "caption",
tag = "tag") +
theme(plot.background = element_rect(fill = "#f0cf61"),
plot.title = element_text(color = "red", size = 50),
plot.subtitle = element_text(color = "darkgreen", size = 30),
plot.caption = element_text(color = "blue", size = 25),
plot.tag = element_text(color = "black", size = 20))
图片标题/说明文字对齐方式
p_text + theme(plot.title.position = "panel",
plot.caption.position = "panel")
p_text + theme(plot.title.position = "plot",
plot.caption.position = "plot")
图片标识对齐方式
p_text + theme(plot.tag.position = "top")
p_text + theme(plot.tag.position = "bottomleft")
绘图边距
p_text + theme(panel.background = element_rect(color = "red"),
plot.margin = unit(c(4, 1, 3, 7),"cm"))
默认设置
默认设置,即line/ rect/ text/ title这四个参数,在此设置了以后,后面的相关元素都将继承此设置(理论上,但是貌似目前并没有实现所有的设置),如:
p_base <- p + theme(line = element_line(colour = "red", # 默认线条颜色设置为红色
linewidth = 2), # 默认线条粗细设置为2
rect = element_rect(colour = "red", # 默认矩形边框颜色设置为红色
fill = "#f0cf61"), # 默认矩形填充色为#f0cf61
text = element_text(colour = "red", # 默认字体颜色为红色
size = 20), # 默认字体大小为20
title = element_text(colour = "blue")) # 默认标题颜色为蓝色
p_base
上图中线条颜色及边框填充色均没有设置成功,另外像绘图背景及面板背景这两个矩形元素也没有修改
不过在设置成功的元素基础上,我们加上:
p_base + theme(axis.line.x = element_line(color = 2)) +
labs(subtitle = "subtitle")
可以看到新加的X轴轴线直接出来宽度就是2,还有子标题也如默认设置一样为蓝色
最后
- 感谢支持,希望对您有帮助!
- 有不足的地方欢迎指正!
- 关注我们,及时获取更多干货!