ggplot2系列教程 第6节——主题(下)

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,还有子标题也如默认设置一样为蓝色

最后

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

推荐阅读更多精彩内容