ggplot2 3.1.0 中的自定义 y 轴刻度和辅助 y 轴标签
Custom y-axis scale and secondary y-axis labels in ggplot2 3.1.0
编辑 2
ggplot2-package 的当前开发版本确实解决了我在下面的问题中提到的错误。使用
安装开发版本
devtools::install_github("tidyverse/ggplot2")
编辑
ggplot2 3.1.0 中 sec_axis
的错误行为似乎是一个错误。开发人员已认识到这一点,他们正在努力修复(请参阅 GitHub 上的 thread)。
目标
我有一个 y 轴范围从 0 到 1 的图形。我想添加一个范围从 0 到 0.5 的次要 y 轴(所以刚好是主要 y 轴值的一半) .目前没问题。
让事情变得复杂的是我有一个自定义的 y 轴变换,其中 y 轴的一部分以线性方式显示,其余部分以对数方式显示(请参见下面的代码示例)。有关参考,请参阅 or this one。
问题
这在使用 ggplot2 版本 3.0.0 时效果很好,但在使用最新版本 (3.1.0) 时不再有效。请参见下面的示例。我不知道如何在最新版本中修复它。
来自changelog:
sec_axis() and dup_axis() now return appropriate breaks for the
secondary axis when applied to log transformed scales
这个新功能似乎在混合变换 y 轴的情况下失效。
可重现的例子
这是一个使用最新版本 (3.1.0) 的 ggplot2 的示例:
library(ggplot2)
library(scales)
#-------------------------------------------------------------------------------------------------------
# Custom y-axis
#-------------------------------------------------------------------------------------------------------
magnify_trans_log <- function(interval_low = 0.05, interval_high = 1, reducer = 0.05, reducer2 = 8) {
trans <- Vectorize(function(x, i_low = interval_low, i_high = interval_high, r = reducer, r2 = reducer2) {
if(is.na(x) || (x >= i_low & x <= i_high)) {
x
} else if(x < i_low & !is.na(x)) {
(log10(x / r)/r2 + i_low)
} else {
log10((x - i_high) / r + i_high)/r2
}
})
inv <- Vectorize(function(x, i_low = interval_low, i_high = interval_high, r = reducer, r2 = reducer2) {
if(is.na(x) || (x >= i_low & x <= i_high)) {
x
} else if(x < i_low & !is.na(x)) {
10^(-(i_low - x)*r2)*r
} else {
i_high + 10^(x*r2)*r - i_high*r
}
})
trans_new(name = 'customlog', transform = trans, inverse = inv, domain = c(1e-16, Inf))
}
#-------------------------------------------------------------------------------------------------------
# Create data
#-------------------------------------------------------------------------------------------------------
x <- seq(-1, 1, length.out = 1000)
y <- c(x[x<0] + 1, -x[x>0] + 1)
dat <- data.frame(
x = x
, y = y
)
#-------------------------------------------------------------------------------------------------------
# Plot using ggplot2
#-------------------------------------------------------------------------------------------------------
theme_set(theme_bw())
ggplot(dat, aes(x = x, y = y)) +
geom_line(size = 1) +
scale_y_continuous(
, trans = magnify_trans_log(interval_low = 0.5, interval_high = 1, reducer = 0.5, reducer2 = 8)
, breaks = c(0.001, 0.01, 0.1, 0.5, 0.6, 0.7, 0.8, 0.9, 1)
, sec.axis = sec_axis(
trans = ~.*(1/2)
, breaks = c(0.001, 0.01, 0.1, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5)
)
) + theme(
axis.text.y=element_text(colour = "black", size=15)
)
这会产生以下情节:
辅助 y 轴的标记对于轴的对数部分(低于 0.5)是正确的,但对于轴的线性部分是错误的。
如果我使用
安装 ggplot2 3.0.0
require(devtools)
install_version("ggplot2", version = "3.0.0", repos = "http://cran.us.r-project.org")
和运行与上面相同的代码,我得到如下图,这就是我想要的:
问题
- 有没有办法在最新版本的ggplot2 (3.1.0) 中解决这个问题?理想情况下,我想避免使用旧版本的 ggplot2(即 3.0.0)。
- 在这种情况下,是否有
sec_axis
的替代方案?
你能为不同的 y 轴范围创建两个单独的图,并将它们堆叠在一起吗?以下对我有用,在 ggplot2 3.1.0 上:
library(cowplot)
theme_set(theme_bw())
p.bottom <- ggplot(dat, aes(x = x, y = y)) +
geom_line(size = 1) +
scale_y_log10(breaks = c(0.001, 0.01, 0.1, 0.5),
expand = c(0, 0),
sec.axis = sec_axis(trans = ~ . * (1/2),
breaks = c(0.001, 0.01, 0.1, 0.25))) +
coord_cartesian(ylim = c(0.001, 0.5)) + # limit y-axis range
theme(axis.text.y=element_text(colour = "black", size=15),
axis.title.y = element_blank(),
axis.ticks.length = unit(0, "pt"),
plot.margin = unit(c(0, 5.5, 5.5, 5.5), "pt")) #remove any space above plot panel
p.top <- ggplot(dat, aes(x = x, y = y)) +
geom_line(size = 1) +
scale_y_continuous(breaks = c(0.6, 0.7, 0.8, 0.9, 1),
labels = function(y) sprintf("%.3f", y), #ensure same label format as p.bottom
expand = c(0, 0),
sec.axis = sec_axis(trans = ~ . * (1/2),
breaks = c(0.3, 0.35, 0.4, 0.45, 0.5),
labels = function(y) sprintf("%.3f", y))) +
coord_cartesian(ylim = c(0.5, 1)) + # limit y-axis range
theme(axis.text.y=element_text(colour = "black", size=15),
axis.text.x = element_blank(), # remove x-axis labels / ticks / title &
axis.ticks.x = element_blank(), # any space below the plot panel
axis.title.x = element_blank(),
axis.ticks.length = unit(0, "pt"),
plot.margin = unit(c(5.5, 5.5, 0, 5.5), "pt"))
plot_grid(p.top, p.bottom,
align = "v", ncol = 1)
这是一个使用 sec_axis()
与 ggplot2
版本 3.1.0 一起使用的解决方案,它只需要创建一个图。我们仍然像以前一样使用 sec_axis()
,但不是将次轴的变换缩放 1/2,而是 我们反向缩放次轴上的中断 .
在这种特殊情况下,我们相当容易,因为我们只需将所需的断点位置乘以 2。然后,对于图形的对数和线性部分,生成的断点将正确定位。之后,我们所要做的就是用所需的值重新标记中断。这避免了 ggplot2
在必须缩放混合变换时被中断位置混淆的问题,因为我们自己进行缩放。简陋,但有效。
不幸的是,目前似乎没有 sec_axis()
的任何其他替代方案(除了 dup_axis()
,这在这里几乎没有帮助)。不过,我很乐意在这一点上得到纠正!祝你好运,我希望这个解决方案对你有所帮助!
代码如下:
# Vector of desired breakpoints for secondary axis
sec_breaks <- c(0.001, 0.01, 0.1, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5)
# Vector of scaled breakpoints that we will actually add to the plot
scaled_breaks <- 2 * sec_breaks
ggplot(data = dat, aes(x = x, y = y)) +
geom_line(size = 1) +
scale_y_continuous(trans = magnify_trans_log(interval_low = 0.5,
interval_high = 1,
reducer = 0.5,
reducer2 = 8),
breaks = c(0.001, 0.01, 0.1, 0.5, 0.6, 0.7, 0.8, 0.9, 1),
sec.axis = sec_axis(trans = ~.,
breaks = scaled_breaks,
labels = sprintf("%.3f", sec_breaks))) +
theme_bw() +
theme(axis.text.y=element_text(colour = "black", size=15))
结果图:
编辑 2
ggplot2-package 的当前开发版本确实解决了我在下面的问题中提到的错误。使用
安装开发版本devtools::install_github("tidyverse/ggplot2")
编辑
ggplot2 3.1.0 中 sec_axis
的错误行为似乎是一个错误。开发人员已认识到这一点,他们正在努力修复(请参阅 GitHub 上的 thread)。
目标
我有一个 y 轴范围从 0 到 1 的图形。我想添加一个范围从 0 到 0.5 的次要 y 轴(所以刚好是主要 y 轴值的一半) .目前没问题。
让事情变得复杂的是我有一个自定义的 y 轴变换,其中 y 轴的一部分以线性方式显示,其余部分以对数方式显示(请参见下面的代码示例)。有关参考,请参阅
问题
这在使用 ggplot2 版本 3.0.0 时效果很好,但在使用最新版本 (3.1.0) 时不再有效。请参见下面的示例。我不知道如何在最新版本中修复它。
来自changelog:
sec_axis() and dup_axis() now return appropriate breaks for the secondary axis when applied to log transformed scales
这个新功能似乎在混合变换 y 轴的情况下失效。
可重现的例子
这是一个使用最新版本 (3.1.0) 的 ggplot2 的示例:
library(ggplot2)
library(scales)
#-------------------------------------------------------------------------------------------------------
# Custom y-axis
#-------------------------------------------------------------------------------------------------------
magnify_trans_log <- function(interval_low = 0.05, interval_high = 1, reducer = 0.05, reducer2 = 8) {
trans <- Vectorize(function(x, i_low = interval_low, i_high = interval_high, r = reducer, r2 = reducer2) {
if(is.na(x) || (x >= i_low & x <= i_high)) {
x
} else if(x < i_low & !is.na(x)) {
(log10(x / r)/r2 + i_low)
} else {
log10((x - i_high) / r + i_high)/r2
}
})
inv <- Vectorize(function(x, i_low = interval_low, i_high = interval_high, r = reducer, r2 = reducer2) {
if(is.na(x) || (x >= i_low & x <= i_high)) {
x
} else if(x < i_low & !is.na(x)) {
10^(-(i_low - x)*r2)*r
} else {
i_high + 10^(x*r2)*r - i_high*r
}
})
trans_new(name = 'customlog', transform = trans, inverse = inv, domain = c(1e-16, Inf))
}
#-------------------------------------------------------------------------------------------------------
# Create data
#-------------------------------------------------------------------------------------------------------
x <- seq(-1, 1, length.out = 1000)
y <- c(x[x<0] + 1, -x[x>0] + 1)
dat <- data.frame(
x = x
, y = y
)
#-------------------------------------------------------------------------------------------------------
# Plot using ggplot2
#-------------------------------------------------------------------------------------------------------
theme_set(theme_bw())
ggplot(dat, aes(x = x, y = y)) +
geom_line(size = 1) +
scale_y_continuous(
, trans = magnify_trans_log(interval_low = 0.5, interval_high = 1, reducer = 0.5, reducer2 = 8)
, breaks = c(0.001, 0.01, 0.1, 0.5, 0.6, 0.7, 0.8, 0.9, 1)
, sec.axis = sec_axis(
trans = ~.*(1/2)
, breaks = c(0.001, 0.01, 0.1, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5)
)
) + theme(
axis.text.y=element_text(colour = "black", size=15)
)
这会产生以下情节:
辅助 y 轴的标记对于轴的对数部分(低于 0.5)是正确的,但对于轴的线性部分是错误的。
如果我使用
安装 ggplot2 3.0.0require(devtools)
install_version("ggplot2", version = "3.0.0", repos = "http://cran.us.r-project.org")
和运行与上面相同的代码,我得到如下图,这就是我想要的:
问题
- 有没有办法在最新版本的ggplot2 (3.1.0) 中解决这个问题?理想情况下,我想避免使用旧版本的 ggplot2(即 3.0.0)。
- 在这种情况下,是否有
sec_axis
的替代方案?
你能为不同的 y 轴范围创建两个单独的图,并将它们堆叠在一起吗?以下对我有用,在 ggplot2 3.1.0 上:
library(cowplot)
theme_set(theme_bw())
p.bottom <- ggplot(dat, aes(x = x, y = y)) +
geom_line(size = 1) +
scale_y_log10(breaks = c(0.001, 0.01, 0.1, 0.5),
expand = c(0, 0),
sec.axis = sec_axis(trans = ~ . * (1/2),
breaks = c(0.001, 0.01, 0.1, 0.25))) +
coord_cartesian(ylim = c(0.001, 0.5)) + # limit y-axis range
theme(axis.text.y=element_text(colour = "black", size=15),
axis.title.y = element_blank(),
axis.ticks.length = unit(0, "pt"),
plot.margin = unit(c(0, 5.5, 5.5, 5.5), "pt")) #remove any space above plot panel
p.top <- ggplot(dat, aes(x = x, y = y)) +
geom_line(size = 1) +
scale_y_continuous(breaks = c(0.6, 0.7, 0.8, 0.9, 1),
labels = function(y) sprintf("%.3f", y), #ensure same label format as p.bottom
expand = c(0, 0),
sec.axis = sec_axis(trans = ~ . * (1/2),
breaks = c(0.3, 0.35, 0.4, 0.45, 0.5),
labels = function(y) sprintf("%.3f", y))) +
coord_cartesian(ylim = c(0.5, 1)) + # limit y-axis range
theme(axis.text.y=element_text(colour = "black", size=15),
axis.text.x = element_blank(), # remove x-axis labels / ticks / title &
axis.ticks.x = element_blank(), # any space below the plot panel
axis.title.x = element_blank(),
axis.ticks.length = unit(0, "pt"),
plot.margin = unit(c(5.5, 5.5, 0, 5.5), "pt"))
plot_grid(p.top, p.bottom,
align = "v", ncol = 1)
这是一个使用 sec_axis()
与 ggplot2
版本 3.1.0 一起使用的解决方案,它只需要创建一个图。我们仍然像以前一样使用 sec_axis()
,但不是将次轴的变换缩放 1/2,而是 我们反向缩放次轴上的中断 .
在这种特殊情况下,我们相当容易,因为我们只需将所需的断点位置乘以 2。然后,对于图形的对数和线性部分,生成的断点将正确定位。之后,我们所要做的就是用所需的值重新标记中断。这避免了 ggplot2
在必须缩放混合变换时被中断位置混淆的问题,因为我们自己进行缩放。简陋,但有效。
不幸的是,目前似乎没有 sec_axis()
的任何其他替代方案(除了 dup_axis()
,这在这里几乎没有帮助)。不过,我很乐意在这一点上得到纠正!祝你好运,我希望这个解决方案对你有所帮助!
代码如下:
# Vector of desired breakpoints for secondary axis
sec_breaks <- c(0.001, 0.01, 0.1, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5)
# Vector of scaled breakpoints that we will actually add to the plot
scaled_breaks <- 2 * sec_breaks
ggplot(data = dat, aes(x = x, y = y)) +
geom_line(size = 1) +
scale_y_continuous(trans = magnify_trans_log(interval_low = 0.5,
interval_high = 1,
reducer = 0.5,
reducer2 = 8),
breaks = c(0.001, 0.01, 0.1, 0.5, 0.6, 0.7, 0.8, 0.9, 1),
sec.axis = sec_axis(trans = ~.,
breaks = scaled_breaks,
labels = sprintf("%.3f", sec_breaks))) +
theme_bw() +
theme(axis.text.y=element_text(colour = "black", size=15))
结果图: