首发于 R

R 数据可视化 —— ggplot 图例设置

前言

坐标轴和图例被统称为 guides,通常使用标度函数来控制,如 limits, breaks 和 labels 参数。

坐标轴与图例很类似,例如,坐标轴的轴标签与图例的标题都是对应到标度函数的 name 参数,而图例的标识(key label)和轴的刻度标签对应于标度函数的 break 参数

除了可以在每个标度函数中分开设置,也可以使用 guides() 函数来进行统一设置,两种设置方式是等价的。

例如,下面的设置方式是等价的

dat <- data.frame(x = 1:5, y = 1:5, p = 1:5, q = factor(1:5),
                  r = factor(1:5))
p <- ggplot(dat, aes(x, y, colour = p, size = q, shape = r)) + geom_point()
p1 <- p + guides(colour = "colorbar", size = "legend", shape = "legend")
p2 <- p + guides(colour = guide_colorbar(), size = guide_legend(),
           shape = guide_legend())
p3 <- p +
  scale_colour_continuous(guide = "colorbar") +
  scale_size_discrete(guide = "legend") +
  scale_shape(guide = "legend")

再比如,删除某一图例或整合所有图例

# 删除图例
p1 <- p + guides(colour = "none")
p2 <- p + guides(colour = "colorbar",size = "none")
# 整合图例
p3 <- p + guides(colour = guide_legend("title"), size = guide_legend("title"),
           shape = guide_legend("title"))
# 与上面的代码等价
g <- guide_legend("title")
p4 <- p + guides(colour = g, size = g, shape = g)
plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4], ncol = 2)

1. 图例符号

在之前的绘制中,通常图例会根据几何对象来生成图例。例如上图中,点图的图例符号也是点。

我们可以为 key_glyph 参数设置不同的 draw_key_*() 函数来自定义图例符号,其中 * 号表示的是各种符号。

p <- ggplot(economics, aes(date, psavert, color = "savings rate"))
p1 <- p + geom_line()
# 通过指定字符串名称
p2 <- p + geom_line(key_glyph = "timeseries")
# 或者对应的函数名
p3 <- p + geom_line(key_glyph = draw_key_timeseries)
# 其他形状
p4 <- p + geom_line(key_glyph = draw_key_rect)
plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4],
          nrow = 2)

2. 颜色条

简单的颜色条可以通过 scale_fill 和 scale_colour 的 guide 参数来设置

df <- expand.grid(X1 = 1:10, X2 = 1:10)
df$value <- df$X1 * df$X2
p1 <- ggplot(df, aes(X1, X2)) + geom_tile(aes(fill = value))
# 使用字符串名称
p1 + scale_fill_continuous(guide = "colourbar")
# 或者对应的函数
p1 + scale_fill_continuous(guide = guide_colourbar())

相当于下面的代码

p1 + guides(fill = guide_colourbar())

guide_colourbar 函数的参数非常多

guide_colourbar(
  title = waiver(),
  title.position = NULL,
  title.theme = NULL,
  title.hjust = NULL,
  title.vjust = NULL,
  label = TRUE,
  label.position = NULL,
  label.theme = NULL,
  label.hjust = NULL,
  label.vjust = NULL,
  barwidth = NULL,
  barheight = NULL,
  nbin = 300,
  raster = TRUE,
  frame.colour = NULL,
  frame.linewidth = 0.5,
  frame.linetype = 1,
  ticks = TRUE,
  ticks.colour = "white",
  ticks.linewidth = 0.5,
  draw.ulim = TRUE,
  draw.llim = TRUE,
  direction = NULL,
  default.unit = "line",
  reverse = FALSE,
  order = 0,
  available_aes = c("colour", "color", "fill"),

我们对部分参数进行说明,如设置颜色条的大小、删除标签或刻度、调整标签的位置以及设置标签的主题

# 设置大小
p2 <- p1 + guides(fill = guide_colourbar(barwidth = 0.5, barheight = 10))
# 删除标签
p3 <- p1 + guides(fill = guide_colourbar(label = FALSE))
# 删除刻度
p4 <- p1 + guides(fill = guide_colourbar(ticks = FALSE))
# 调整标签位置
p5 <- p1 + guides(fill = guide_colourbar(label.position = "left"))
# 设置标签主题
p6 <- p1 + guides(fill = guide_colourbar(label.theme = element_text(colour = "blue", angle = 0)))
plot_grid(p1, p2, p3, p4, p5, p6, labels = LETTERS[1:6],
          nrow = 3)

设置颜色条每个刻度间的分箱个数,分箱越多,颜色条看起来越平滑

# 颜色条的分箱个数,越大越平滑
p7 <- p1 + guides(fill = guide_colourbar(nbin = 3))
p8 <- p1 + guides(fill = guide_colourbar(nbin = 100))
plot_grid(p7, p8, labels = LETTERS[1:2])

如果存在多个图例,可以分别对每个图例进行独立的设置

p2 <- p1 + geom_point(aes(size = value))
# 多个图例,可以分开独立控制
p3 <- p2 +
  scale_fill_continuous(guide = "colourbar") +
  scale_size(guide = "legend")
# 或者使用字符串名称
p3 <- p2 + guides(fill = "colourbar", size = "legend")
p4 <- p2 +
  scale_fill_continuous(guide = guide_colourbar(direction = "horizontal")) +
  scale_size(guide = guide_legend(direction = "vertical"))
plot_grid(p3, p4, labels = LETTERS[1:2])

3. 基本图例设置

也可以使用 guide_legend 函数来设置图例的样式,该函数也有很多参数可供选择

guide_legend(
  title = waiver(),
  title.position = NULL,
  title.theme = NULL,
  title.hjust = NULL,
  title.vjust = NULL,
  label = TRUE,
  label.position = NULL,
  label.theme = NULL,
  label.hjust = NULL,
  label.vjust = NULL,
  keywidth = NULL,
  keyheight = NULL,
  direction = NULL,
  default.unit = "line",
  override.aes = list(),
  nrow = NULL,
  ncol = NULL,
  byrow = FALSE,
  reverse = FALSE,
  order = 0,

例如,下面的代码只绘制了一个简单的图例

df <- expand.grid(X1 = 1:10, X2 = 1:10)
df$value <- df$X1 * df$X2
p1 <- ggplot(df, aes(X1, X2)) + geom_tile(aes(fill = value))
p1 + scale_fill_continuous(guide = guide_legend())

我们可以为它设置各种样式,如

# 设置图例标题的位置
p2 <- p1 + guides(fill = guide_legend(title = "LEFT", title.position = "left"))
# 使用 element_text 设置图例标题的样式
p3 <- p1 + guides(fill =
              guide_legend(
                title.theme = element_text(
                  size = 15,
                  face = "italic",
                  colour = "red",
                  angle = 0
# 设置标签的位置
p4 <- p1 + guides(fill = guide_legend(label.position = "left", label.hjust = 1))
# 设置标签的样式
p5 <- p1 + scale_fill_continuous(breaks = c(5, 10, 15),
                           labels = paste("long", c(5, 10, 15)),
                           guide = guide_legend(
                             direction = "horizontal",
                             title.position = "top",
                             label.position = "bottom",
                             label.hjust = 0.5,
                             label.vjust = 1,
                             label.theme = element_text(angle = 90)
plot_grid(p2, p3, p4, p5, labels = LETTERS[1:4], nrow = 2)

也可以覆盖原理的属性映射

p3 <- ggplot(mtcars, aes(vs, am, colour = factor(cyl))) +
  geom_jitter(alpha = 1/5, width = 0.01, height = 0.01)
# override.aes overwrites the alpha
p4 <- p3 + guides(colour = guide_legend(override.aes = list(alpha = 1)))
plot_grid(p3, p4, labels = LETTERS[1:2])

在图 A 中,点的透明度太低了,看起来挺费劲的。然后在图 B 中,我们将透明度 alpha 设置为 1,更容易分辨不同类型的点。

对于某些图,由于类别太多导致图例非常的长,例如

df <- data.frame(x = 1:20, y = 1:20, color = letters[1:20])
p <- ggplot(df, aes(x, y)) +
  geom_point(aes(colour = color))

我们可以限制图例的行列数

# 限制行列的数目
p1 <- p + guides(col = guide_legend(nrow = 8))
p2 <- p + guides(col = guide_legend(ncol = 8))
p3 <- p + guides(col = guide_legend(nrow = 8, byrow = TRUE))
# 将顺序反转
p4 <- p + guides(col = guide_legend(reverse = TRUE))
plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4], ncol = 2)

让图例看起来更好看些。

我们在图 D 中,设置 reverse = TRUE 可以将标签顺序进行反转

4. 基本坐标轴设置

坐标轴的设置通常与 scale_(x|y)_continuous() scale_(x|y)_discrete() 两个标度函数搭配使用

guide_axis(
  title = waiver(),
  check.overlap = FALSE,
  angle = NULL,
  n.dodge = 1,
  order = 0,
  position = waiver()

例如

p <- ggplot(mpg, aes(cty * 100, hwy * 100)) +
  geom_point()
# 设置标签的行数(如果要设置列,需要传入一个向量)
p1 <- p + scale_x_continuous(guide = guide_axis(n.dodge = 2))
# 设置标签的角度
p2 <- p + guides(x = guide_axis(angle = 90))
# 复制一个轴
p3 <- p + guides(x = guide_axis(n.dodge = 2), y.sec = guide_axis())
plot_grid(p, p1, p2, p3, labels = LETTERS[1:4], ncol = 2)

5. 分箱图例

分箱图例使用 guide_bins 函数来设置,它是 guide_legend() 的分箱版本,通常需要和分箱的标度函数一起使用

如果你想要将其搭配离散型数据使用,需要确保离散数据的 level 必须遵从 base::cut 函数的命名规则,即名称必须为 (<lower>, <upper>] 形式

示例

p <- ggplot(mtcars) +
  geom_point(aes(disp, mpg, size = hp)) +
  scale_size_binned()
# 删除轴或样式
p1 <- p + guides(size = guide_bins(axis = FALSE))
# 显示分箱区间
p2 <- p + guides(size = guide_bins(show.limits = TRUE))
# 设置轴的箭头
p3 <- p + guides(size = guide_bins(
  axis.arrow = arrow(length = unit(1.5, 'mm'), ends = 'both')
# 默认会尽可能将图例合并
p4 <- ggplot(mtcars) +
  geom_point(aes(disp, mpg, size = hp, colour = hp)) +
  scale_size_binned() +
  scale_colour_binned(guide = "bins")
plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4], ncol = 2)

6. 分箱颜色条

分箱颜色条 guide_coloursteps 是 guide_colourbar() 的分箱版,它将断点之间的区域显示为一个固定的颜色

例如,

df <- expand.grid(X1 = 1:10, X2 = 1:10)
df$value <- df$X1 * df$X2
p <- ggplot(df, aes(X1, X2)) + geom_tile(aes(fill = value))
p1 <- p + scale_fill_binned()
# 默认是等间距的断点,可以自定义断点
p2 <- p + scale_fill_binned(breaks = c(10, 25, 50))
# 根据区间的数据占比显示长度
p3 <- p + scale_fill_binned(
  breaks = c(10, 25, 50),
  guide = guide_coloursteps(even.steps = FALSE)
# 是否显示所有刻度(外围的刻度)
p4 <- p + scale_fill_binned(guide = guide_coloursteps(show.limits = TRUE))
plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4], ncol = 2)

7. 设置次轴

sec_axis 和 dup_axis 函数与位置标度一起使用,来创建与主轴相对的次轴。

副轴必须基于主轴的一对一变换

sec_axis(
  trans = NULL,
  name = waiver(),
  breaks = waiver(),
  labels = waiver(),
  guide = waiver()
dup_axis(
  trans = ~.,
  name = derive(),
  breaks = derive(),
  labels = derive(),
  guide = derive()

sec_axis() 需要一个转换函数传递给 trans 参数,其他所有设置可以使用 derive() 从主轴继承

dup_axis() 创建一个主轴的拷贝作为次轴

示例

p <- ggplot(mtcars, aes(cyl, mpg)) +
  geom_point()
# 创建一个简单的次轴
p1 <- p + scale_y_continuous(sec.axis = sec_axis(~ . + 10))
# 从主轴中继承名称
p2 <- p + scale_y_continuous("Miles/gallon", sec.axis = sec_axis(~ . + 10, name = derive()))
# 复制一份主轴
p3 <- p + scale_y_continuous(sec.axis = dup_axis())
# 设置转换函数
p4 <- p + scale_y_continuous(sec.axis = ~ .^2)
plot_grid(p1, p2, p3, p4, labels = LETTERS[1:4],
          nrow = 2)

创建时间次轴

df <- data.frame(
  dx = seq(as.POSIXct("2012-02-29 12:00:00",
                      tz = "UTC",
                      format = "%Y-%m-%d %H:%M:%S"
  length.out = 10, by = "4 hour"
  price = seq(20, 200000, length.out = 10)
ggplot(df, aes(x = dx, y = price)) + geom_line() +
  scale_x_datetime("Date", date_labels = "%b %d",
  date_breaks = "6 hour",
  sec.axis = dup_axis(name = "Time of Day",