ggplot2:标度、坐标轴和图例

ggplot2:标度、坐标轴和图例

标度(scale)是控制着数据到图形属性的映射。标度将我们的数据转化为视觉上可以感知的东西:例如大小、颜色、位置或形状。标度也为我们提供了读图时所使用的工具:坐标轴和图例(总的来说可以称为引导元素)。

更准去地说,每一种标度都是从数据空间的某个区域(标度的定义域)到图形属性空间的某个区域的映射。执行标度的过程分为3步:变换(transformation)、训练(training)和映射(mapping)。

标度可以粗略地分为四类:位置标度、颜色标度、手动离散型标度以及同一型标度。

标度的另一个重要角色是生成一个允许读图者从图形属性空间到数据空间进行反向映射的引导元素(guide),并从图中读出取值。对于位置型图形属性,引导元素是坐标轴;对于所有其他图形属性来说,引导元素是图例。与其它绘图系统不同的是,在这里,我们基本无法直接控制坐标轴和图例的细节

用法

表:按图形属性和变量类别排列的各种标度
图形属性 离散型 连续型
colour / fill brewer gradient
  grey gradient2
  hue gradientn
  identity  
  manual  
position discreet continuous
    date
shape shape  
  identity  
  manual  
line type linetype  
  identity  
  manual  
size identity size
  manual  
1
2
3
4
library(ggplot2)
library(ggthemes)
theme_set(theme_igray(base_size = 20, base_family = "FiraSans-Regular"))
(plot <- qplot(cty, hwy, data = mpg))


1
plot + aes(x = drv) + scale_x_discrete()

似乎新版的ggplot2包变量类型和默认自动匹配。直接
plot + aes(x = drv)也是👌的。

1
(p <- qplot(sleep_total, sleep_cycle, data = msleep, colour = vore))


1
2
3
4
5
6
7
8
9
# 显示添加默认标度
p + scale_colour_hue()

# 修改默认标度参数,这里会改变图例的外观
p + scale_colour_hue(
"What does\nit eat?",
breaks = c("herbi", "carni", "omni", NA),
labels = c("plants", "meat", "both", "don't know")
)


1
2
## 更换标度
p + scale_colour_brewer(palette = "Set1")

标度详解

  1. 位置标度:用于将连续型、离散型和日期-时间型变量映射到绘图区域,以及构造对应的坐标轴。
  2. 颜色标度:用于将连续型和离散型变量映射到颜色。
  3. 手动标度:用于将离散型变量映射到我们选择的符号大小、线条类型、形状或颜色,以及创建对应的图例。
  4. 同一型标度:用于直接将变量值绘制为图形属性,而不是去映射它们。举例来说,假如我们想把变量映射为符号的颜色,而此变量本身就是一个由颜色值组成的向量,那么我么就无须将其映射为其它颜色,直接渲染这些值即可。

通用参数

name:设置坐标轴或图例上出现的标签

1
2
(p <- ggplot(mpg, aes(cty, hwy, colour = displ)) + geom_point())
p + scale_x_continuous("City mpg")


1
2
3
4
# 这等于
p + xlab("City mpg")

p + labs(x = "City mpg", y = "HIghway", colour = "Displcement")


1
2
# 使用表达式
p + xlab(expression(frac(miles, gallon)))

limits:固定标度的定义域

连续型标度接受一个长度为2的数值型向量;离散型标度接受一个字符型向量。一旦设定了limits,数据将不再进行任何训练。任何不在标度定义域内的值将被丢弃:丢弃过程发生在统计量的计算之前。

breaks和labels

breaks控制着显示在坐标轴或图例上的值,即坐标轴上应该显示哪些刻度线的值,或一个连续型标度在一个图例中将被如何分段。labels指定了应在断点处显示的标签。若设置了labels,则必须同时指定breaks,只有这样,这两个参数才能被正确匹配。

要区分breaks和limits,请牢记,breaks影响显示在坐标轴和图例上的元素,而limits影响在图形上的元素。

1
(p <- qplot(cyl, wt, data = mtcars))


1
p + scale_x_continuous(breaks = c(5.5, 6.5))


1
p + scale_x_continuous(limits = c(5.5, 6.5))


1
(p <- qplot(wt, cyl, data = mtcars, colour = cyl))


1
p + scale_colour_gradient(breaks = c(5.5, 6.5))


1
p + scale_colour_gradient(limits = c(5.5, 6.5))

formatter

如果未指定任何标签,则将在每个断点处自动调用格式刷(formatter)来格式化生成标签。对于连续型标度,可用的标签刷为:comma, percent, dollar和scientific;对于离散型标度,则为abbreviate。

位置标度

每一个图形都有两个位置标度,x和y。

  • xlim(10, 20):一个从10到20的连续型标度;
  • ylim(20, 10):一个从20到10的反转后连续型标度;
  • xlim(“a”, “b”, “c”):一个离散型标度;
  • ylim(as.Date(c(“2008-05-01”, “2008-08-01”))):一个日期型标度。

默认情况下,位置标度的limits会稍微超出数据的范围,这样就能保证数据与坐标轴不会发生重叠。我们可以使用expand来控制溢出量,此参数是长度为2的数值型向量。其中第一个元素给出的是乘式的溢出量,第二个参数给出的是加式的溢出量。如果你不想留任何多余的空间,就使用expand = c(0, 0)

连续型

最常用的连续型位置标度是scale_x_continuous和scale_y_continuous,它们均将数据映射到x轴和y轴。而最有趣的变式是通过变化来生成的。每个连续型标度都可以接受一个trans参数,允许指定若干种线性或非线性的变换。而每一种变换都是所谓的“变换器”(transformer)来实现的,变换器描述来变换本身和对应的逆变换,以及如何去绘制标签。下表列出来部分较为常用的变换器:

名称 变换函数 逆变换函数
$asn$ $tanh^{-1}(x)$ $tanh(y)$
$exp$ $e^{x}$ $log(y)$
$identity$ $x$ $y$
$log$ $log(x)$ $e^{y}$
$log10$ $log_{10}(x)$ $10^{y}$
$log2$ $log_{2}(x)$ $2^{y}$
$logit$ $log(\frac{x}{1-x})$ $\frac{1}{1 + e^{y}}$
$pow10$ $10^{x}$ $log_{10}(y)$
$probit$ $\Phi(x)$ $\Phi^{-1}(x)$
$recip$ $x^{-1}$ $$y^{-1}
$reverse$ $-x$ $-y$
$sqrt$ $x^{1/2}$ $y^2$

变换通常被用来修改位置标度,所以对于x,y和z标度都是有简便写法的

scale_x_log10() = scale_x_continuous(trans = “log10”)

参数trans对于任意连续型标度都是有效的,包括下文中描述的颜色标度,但简便写法仅针对位置标度存在。

当然我们也可以自行实施变换。举例来说,我们可以直接绘制log10(x),而不去使用scale_x_log()。这两种做法等效,但是坐标轴和刻度标签却是不同的。如果我们使用变换后的标度,坐标轴将以原始的数据空间进行标注。在这两种情况下,变换均在计算统计摘要之前发生:

1
qplot(log10(carat), log10(price), data = diamonds)


1
qplot(carat, price, data = diamonds) + scale_x_log10() + scale_y_log10()

变换器同样可以用在coord_trans()中,此时变换将在统计量计算完成之后进行,因此将影响图形主题的外观。

日期和时间

日期和时间值基本上属于连续型,但在标注坐标轴时有着特殊的处理方式。目前仅支持属于date类的日期值和POSIXct类的时间值。如果你的日期和时间值是属于其他格式的,则需要使用as.Date()或as.POSIXct()函数对其进行转换。

对于日期坐标轴,有三个参数可以用于控制其外观和刻度的位置:major、minor以及format。

  1. major和minor用以按照时间的单位来指定只要和次要断点的位置,并且允许这些单位的倍数出现。例如major = "2 weeks"将在每隔两周的位置上放置一个主刻度。如果未指定,日期标度可以自动选出合适的默认值。
  2. 参数format指定了坐标轴刻度标签的格式,从strptime的文档中提取的常用数据格式编码如下:
编码 含义
%S 秒(00-59)
%M 分钟(00-59)
%l 小时,12小时制(1-12)
%I 小时,12小时制(01-12)
%H 小时,24小时制(00-23)
%a 缩写的周几(Mon-Sun)
%A 全称的周几(Monday - Sunday)
%e 某月中的某天(1-31)
%d 某月中的某天(01-31)
%m 以数值表示的月份(01-12)
%b 缩写的月份(Jan - Dec)
%B 全称的月份(January - December)
%y 不含世纪的年份(00-99)
%Y 含世纪的年份(0000-9999)
1
2
3
4
library(scales)
(plot <- qplot(date, psavert, data = economics, geom = "line") +
ylab ("Personal savings rate") +
geom_hline(yintercept = 0, colour = "grey50"))


1
2
3
plot + scale_x_date(breaks = date_breaks("8 months"),
limits = as.Date(c("2001-01-01", "2005-01-01")),
labels = date_format('%Y-%m')) + ylim(2, 7)

离散型

离散型位置标度将输入中的各水平映射为整数。结果的顺序可用参数breaks进行控制,不想要的水平可以使用limits(或xlim()/ylim())进行丢弃。由于我们经常也会在图形的非整点位置放置标签和标注,所以离散型位置标度也可以接受连续型的值。如果你未调整breaks或limits,某个因子水平的所在位置的数值表示可以使用as.numeric()进行计算:从1开始的整数表示。

颜色标度

hcl色彩空间的现代方案

  1. 色相(hue):一个0和360之间的角度值,它将色彩赋以“颜色”属性:如蓝、红、橙等;
  2. 明度(luminance):指颜色的明暗程度。明度的高低,要看其接近白色或黑色的程度而定。明度为0为黑,明度为1为白;
  3. 彩度(chroma):指色彩的纯度。彩度为0 为灰色,彩度的最大值随明度的变化而不同。

连续型

根据颜色梯度中的色彩数量划分,共有三种连续型颜色梯度(即渐变色):

  1. sclae_colour_gradient()和scale_fill_gradient():双色梯度。顺序为从低到高。参数low和high用以控制此梯度两端的颜色。
  2. scale_colour_gradient2()和scale_fill_gradient2():三色梯度。顺序为低-中-高。参数low和high作用同上,这两种标度还在中点处拥有一个中间色。中点的默认值为0,但也可以使用参数midpoint将其设置为任意值。这个参数对生成发散型配色方案特别有用。
  3. scale_colour_gradientn()和scale_fill_gradientn():自定义的n色颜色标度。此标度需要赋给参数colours一个颜色向量。不加其它参数的话,这些颜色将按照数据的范围均匀分布。如果你需要让这些值不均匀的分布,可以使用参数values。如果rescale的取值为TRUE的话,这values应在0~1之间取值。如果rescale的取值为F,则values应在数据范围内取值。

颜色梯度常常被用来展示一个二维表面的高度。下面用faithful数据集的一个二维核密度估计为例进行说明,此数据集记录了黄石公园的老忠实泉两次喷发的间隔时间以及每次喷发的持续时间。下面三幅图分别展示了三种颜色梯度。

1
2
3
4
5
6
7
8
f2d <- with(faithful, MASS::kde2d(eruptions, waiting, h = c(1, 10), n =50))
df <- with(f2d, cbind(expand.grid(x, y), as.vector(z)))
names(df) <- c("eruptions", "waiting", "density")
erupt <- ggplot(df, aes(waiting, eruptions, fill = density)) +
geom_tile() +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0))
erupt + scale_fill_gradient(limits = c(0, 0.04))


1
2
3
erupt + scale_fill_gradient(limits = c(0, 0.04),
low = "white",
high = "black")


1
2
erupt + scale_fill_gradient2(limits = c(0, 0.04),
midpoint = mean(df$density))

要创建自定义的颜色梯度,使用scale_colour_gradientn()即可,这在颜色能够代表数据的特征时很有用。

离散型

离散型数据有两种颜色标度,一种可以自动选择颜色,另一种可以轻松地从手工选择的颜色集中选择颜色。

默认配色方案,即scale_colour_hue(),可以通过hcl色轮选取均匀分布的色相来生成颜色。这种方案对多至约8种颜色时都能有很好的效果,但对于更多的颜色,要区分开不同的颜色就变得比较困难了。默认配色的另外一个缺点是,由于所有颜色都拥有相同的明度和彩度,所以当我们进行黑白打印的时候,它们就会成为几近相同的灰影。

除了这种基于计算的方案外,另一种可选的方案是使用ColorBrewer配色,这些手工甄别的颜色在很多情形下都运作良好,尽管它更专注于地图,这些颜色也因此在展示较大的面积时表现更佳。对于类别型数据中的点而言,我们最感兴趣的调色板是“Set1”和“Dark2”,对面积而言则是“Set2”、“Pastel1”、“Pastel2”和“Accent”。使用
RColorBrewer::display.brewer.all可列出所有的调色板。

下图展示了应用于点和条形的三种调色板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
library(grid)
p1 <- point + scale_colour_brewer(palette = "Set1")
p2 <- point + scale_colour_brewer(palette = "Set2")
p3 <- point + scale_colour_brewer(palette = "Pastel1")
p4 <- area + scale_fill_brewer(palette = "Set1")
p5 <- area + scale_fill_brewer(palette = "Set2")
p6 <- area + scale_fill_brewer(palette = "Pastel1")
png("20181013a4.png", width = 1800, height = 900)
grid.newpage()
pushViewport(viewport(layout = grid.layout(2, 3)))
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
print(p1, vp = vplayout(1, 1))
print(p2, vp = vplayout(1, 2))
print(p3, vp = vplayout(1, 3))
print(p4, vp = vplayout(2, 1))
print(p5, vp = vplayout(2, 2))
print(p6, vp = vplayout(2, 3))
dev.off()

手动离散型标度

离散型标度scale_linetype(),scale_size_discrete()和scale_shape()基本没有任何选项(虽然对于形状标度,你可以选择点是空心可填充的还是实心的)。这些标度仅仅是按照一定的顺序将因子的水平映射到一系列的取值中。

如果你想要定制这些标度,你需要使用以下手动型标度创建型的标度:scale_shape_manual(),scale_linetype_manual(),scale_colour_manual()等等。手动型标度拥有一个重要参数values,你可以使用它来指定这个标度应该生成的值。如果这个向量中的元素是有名称的,则它将自动匹配输入和输出的值,否则它将按照离散型变量中水平的先后次序进行匹配。以下代码演示了scale_manual()的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
msleep2 <- msleep[!is.na(msleep$vore),]
plot <- qplot(brainwt, bodywt, data = msleep2, log = "xy")
q1 <- plot + aes(colour = vore) + scale_colour_manual(values = c("red", "orange", "yellow", "green", "blue"))
colours <- c(carni = "red", "NA" = "orange", insecti = "yellow", herbi = "green", omni = "blue")
q2 <- plot + aes(colour = vore) + scale_colour_manual(values = colours)
q3 <- plot + aes(shape = vore, colour = vore) + scale_shape_manual(values = c(1, 2, 6, 0, 23))
png("20181013b1.png", width = 1800, height = 450)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 3)))
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
print(q1, vp = vplayout(1, 1))
print(q2, vp = vplayout(1, 2))
print(q3, vp = vplayout(1, 3))
dev.off()

有时候你可能想在同一幅图上展示多个变量并显示一个有用的图例,下例就是展示了scale_colour_manual()在这种情况下的创意用法。在多数其它的图形系统中,你只需要像下图一样把线上色,然后添加一个图例说明哪种颜色对应着哪个变量就可以了。这对于ggplot2并不适用,因为图例是由标度控制的,但标度不知道要为线条添加何种标签。

1
2
3
4
huron <- data.frame(year = 1875:1972, level = LakeHuron)
ggplot(huron, aes(year)) +
geom_line(aes(y = level - 5), colour = "blue") +
geom_line(aes(y = level + 5), colour = "red")

你需要做的就是通过创建从数据到颜色图形属性的一个映射,来告诉颜色标度这两条线的信息。此数据中并没有出现变量,所以需要先创建一个:

1
2
3
ggplot(huron, aes(year)) +
geom_line(aes(y = level - 5, colour = "below")) +
geom_line(aes(y = level + 5, colour = "above"))

这样基本就得到了我们想要的效果,但是图例标注不正确且颜色有误,可以用scale_colour_manual进行修复:

1
2
3
4
5
ggplot(huron, aes(year)) +
geom_line(aes(y = level - 5, colour = "below")) +
geom_line(aes(y = level + 5, colour = "above")) +
scale_colour_manual("Direction",
values = c("below" = "blue", "above" = "red"))

# R

评论

程振兴

程振兴 @czxa.top
截止今天,我已经在本博客上写了658.8k个字了!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×