9.6 坐标轴
9.6.1 问题
你想要改变轴的顺序或方向。
9.6.2 方案
注意:下面的例子中提到的
scale_y_continuous()
、ylim()
等函数名中,y
都可以替换为x
。
下面使用内置的 PlantGrowth
数据集绘制一个基本的箱线图。
library(ggplot2)
bp <- ggplot(PlantGrowth, aes(x = group, y = weight)) +
geom_boxplot()
bp
9.6.2.1 交换 x 和 y 轴
交换 x 和 y 轴(让 x 垂直、y 水平)。
bp + coord_flip()
9.6.2.2 离散轴
9.6.2.2.1 改变条目的顺序
# 手动设定离散轴条目的顺序
bp + scale_x_discrete(limits = c("trt1", "trt2", "ctrl"))
## 逆转轴条目顺序 获取因子水平
flevels <- levels(PlantGrowth$group)
flevels
#> [1] "ctrl" "trt1" "trt2"
# 逆转顺序
flevels <- rev(flevels)
flevels
#> [1] "trt2" "trt1" "ctrl"
bp + scale_x_discrete(limits = flevels)
# 或者一行搞定
bp + scale_x_discrete(limits = rev(levels(PlantGrowth$group)))
9.6.2.2.2 设定标签
对于离散变量,标签来自于因子水平。然而,有时候短的因子水平名字并不适合展示。
bp + scale_x_discrete(breaks = c("ctrl", "trt1", "trt2"),
labels = c("Control", "Treat 1", "Treat 2"))
# 隐藏 x 刻度、标签和网格线
bp + scale_x_discrete(breaks = NULL)
# 隐藏所有的刻度和标签(X 轴),保留网格线
bp + theme(axis.ticks = element_blank(), axis.text.x = element_blank())
9.6.2.3 连续轴
9.6.2.3.1 设定范围和反转轴方向
如果你仅想简单地让轴包含某个值,可以使用 expand_limits()
,它会进行拓展而不是拉伸。
# 确保 y 轴包含 0
bp + expand_limits(y = 0)
# 确保 y 轴包含 0 和 8
bp + expand_limits(y = c(0, 8))
当然你也可以通过 y 刻度显式地指定。注意如果使用任何 scale_y_continuous
命令,它会覆盖任何 ylim
命令,而且 ylim
会被忽略。
# 设定连续值轴的范围 下面是相同的操作
bp + ylim(0, 8)
# bp + scale_y_continuous(limits=c(0, 8))
如果使用上述方法让 y 轴的范围变小,任何超出范围的数据都会被忽略。有时候这会产生一些问题,读者需要注意。
为了避免产生问题,你可以使用 coord_cartesian()
,相比于设定轴的范围,它设定数据可视化的区域。
# 这两个操作一致,超出范围的数据被删除了,导致产生一个误导的箱线图
bp + ylim(5, 7.5)
#> Warning: Removed 13 rows containing non-finite values
#> (stat_boxplot).
# bp + scale_y_continuous(limits=c(5, 7.5))
# 使用 coord_cartesian 'zooms' 区域
bp + coord_cartesian(ylim = c(5, 7.5))
# 直接指定刻度
bp + coord_cartesian(ylim = c(5, 7.5)) + scale_y_continuous(breaks = seq(0,
10, 0.25)) # Ticks from 0-10, every .25
9.6.2.3.2 反转轴方向
# 反转一个连续值轴的方向
bp + scale_y_reverse()
9.6.2.3.3 设置和隐藏刻度标记
# Setting the tick marks on an axis This will show tick
# marks on every 0.25 from 1 to 10 The scale will show
# only the ones that are within range (3.50-6.25 in this
# case)
bp + scale_y_continuous(breaks = seq(1, 10, 1/4))
# 刻度不平等变化
bp + scale_y_continuous(breaks = c(4, 4.25, 4.5, 5, 6, 8))
# 抑制标签和网格线
bp + scale_y_continuous(breaks = NULL)
# Hide tick marks and labels (on Y axis), but keep the
# gridlines
bp + theme(axis.ticks = element_blank(), axis.text.y = element_blank())
9.6.2.3.4 轴刻度 log、sqrt 等转换
默认轴是线性坐标,我们也可以将它转换为 log、幂、根等等。
有两种办法可以转换一个轴,一是使用 scale 进行转换,另外是使用 coordinate 进行转换。使用前者需要在先弄好刻度和轴的范围之前转换,而使用后者则相反,需要在弄好刻度和轴范围之后转换。这将产生不太一样的显示效果,如下所示。
# 创建指数分布数据
set.seed(201)
n <- 100
dat <- data.frame(xval = (1:n + rnorm(n, sd = 5))/20, yval = 2 *
2^((1:n + rnorm(n, sd = 5))/20))
# 创建常规的散点图
sp <- ggplot(dat, aes(xval, yval)) + geom_point()
sp
# log2 比例化(间隔相等)
library(scales) # 需要 scales 包
sp + scale_y_continuous(trans = log2_trans())
# log2 坐标转换,空间间隔不同
sp + coord_trans(y = "log2")
在标度转换中,我们还可以指定刻度值,让它们显示指数。
sp + scale_y_continuous(trans = log2_trans(), breaks = trans_breaks("log2",
function(x) 2^x), labels = trans_format("log2", math_format(2^.x)))
可以使用非常多的转换,参见 ?trans_new
查看所有可用转换的列表。如果你所需要的转换不在该列表上,可以自己写一个转换函数。
有一些非常便捷的函数:scale_y_log10()
和 scale_y_sqrt()
(有对应的 x 版本)。
set.seed(205)
n <- 100
dat10 <- data.frame(xval = (1:n + rnorm(n, sd = 5))/20,
yval = 10 * 10^((1:n + rnorm(n, sd = 5))/20))
sp10 <- ggplot(dat10, aes(xval, yval)) + geom_point()
# log10
sp10 + scale_y_log10()
# log10 转换,并设定指数标签
sp10 + scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
labels = trans_format("log10", math_format(10^.x)))
9.6.2.3.5 x 与 y 轴固定的比例
设置 x 与 y 轴比例宽度也是可以的。
# x 范围 0-10, y 范围 0-30
set.seed(202)
dat <- data.frame(xval = runif(40, 0, 10), yval = runif(40,
0, 30))
sp <- ggplot(dat, aes(xval, yval)) + geom_point()
# 强制比例相等
sp + coord_fixed()
# 相等的标度变化,让 x 的 1 个单位等同 y 的 3 个单位
sp + coord_fixed(ratio = 1/3)
9.6.2.4 轴标签和文字格式化
设置和隐藏轴标签:
bp + theme(axis.title.x = element_blank()) + # 移除 x 轴标签
ylab("Weight (Kg)") # 设置 y 轴标签
# 也可以通过标度设置
# 注意这里 x 轴标签的空间仍然保留
bp + scale_x_discrete(name="") +
scale_y_continuous(name="Weight (Kg)")
改变字体、颜色、旋转刻度标签:
# 改变字体选项: X-axis label: bold, red, and 20 points
# X-axis tick marks: rotate 90 degrees CCW, move to the
# left a bit (using vjust, since the labels are
# rotated), and 16 points
bp + theme(axis.title.x = element_text(face = "bold", colour = "#990000",
size = 20), axis.text.x = element_text(angle = 90, vjust = 0.5,
size = 16))
9.6.2.5 刻度标签
你可能想将值显示为百分比、或美元、或科学计数法。这里可以使用格式器,它是一个可以改变文本的函数。
# 标签格式器
library(scales) # 需要 scales 包
bp + scale_y_continuous(labels = percent) + scale_x_discrete(labels = abbreviate) # 在这个例子中它没作用
连续标度格式器有 comma
、percent
、dollar
以及 scientific
。离散标度格式器有 abbreviate
、date_format
等。
有时你需要自己创建格式化函数。下面的函数可以显示时间格式为 HH:MM:SS。
# 自定义时间格式化函数
timeHMS_formatter <- function(x) {
h <- floor(x/60)
m <- floor(x%%60)
s <- round(60 * (x%%1)) # Round to nearest second
lab <- sprintf("%02d:%02d:%02d", h, m, s) # Format the strings as HH:MM:SS
lab <- gsub("^00:", "", lab) # Remove leading 00: if present
lab <- gsub("^0", "", lab) # Remove leading 0 if present
}
bp + scale_y_continuous(label = timeHMS_formatter)
9.6.2.6 隐藏网格线
隐藏网格线:
# 隐藏所有网格线
bp + theme(panel.grid.minor = element_blank(), panel.grid.major = element_blank())
# 仅隐藏次级网格线
bp + theme(panel.grid.minor = element_blank())
也可以仅隐藏水平或垂直网格线:
# 隐藏所有垂直网格线
bp + theme(panel.grid.minor.x = element_blank(), panel.grid.major.x = element_blank())
# 隐藏所有水平网格线
bp + theme(panel.grid.minor.y = element_blank(), panel.grid.major.y = element_blank())