ggplot2::sec_axis对数转换错误,如何复刻car包双轴密度图?
ggplot2复刻次轴刻度错误的解决方法
问题背景
想用{ggplot2}包复刻Fox, J., & Weisberg, S. (2018)《An R Companion to Applied Regression(第3版)》中的图3.15,原代码使用{car}和{carData}包实现:
graphics::par(mfrow = c(1, 2), mar = c(5, 4, 6, 2) + 0.1) car::densityPlot( ~ assets, data = carData::Ornstein, xlab = "assets", main = "(a)" ) car::densityPlot( ~ base::log10(assets), data = carData::Ornstein, adjust = 0.65, xlab = base::expression(log[10] ~ "(assets)"), main = "(b)" ) car::basicPowerAxis( 0, base = 10, side = "above", at = 10 ^ (2:5), axis.title = "" )
自行编写的ggplot2复刻代码如下:
gg1 <- carData::Ornstein |> ggplot2::ggplot( ggplot2::aes(x = assets) ) + ggplot2::geom_density() + ggplot2::ylab("Density") + ggplot2::geom_rug() + ggplot2::ggtitle("(a)") + ggplot2::theme_bw() gg2 <- carData::Ornstein |> ggplot2::ggplot( ggplot2::aes(x = base::log10(assets)) ) + ggplot2::geom_density( adjust = 0.65 ) + ggplot2::labs( y = "Density", x = base::expression(log[10](assets)) ) + ggplot2::geom_rug() + ggplot2::scale_x_continuous( sec.axis = ggplot2::sec_axis( transform = ~. ^10, breaks = c(100, 1000, 1e+5) ) ) + ggplot2::theme_bw() gridExtra::grid.arrange(gg1, gg2, ncol = 2)
出现的问题:第二个图的次轴显示错误,log10刻度的2应对应1e+02,5应对应1e+05,但当前代码无法实现正确映射。
问题根源
次轴的映射逻辑搞反了:
- 主轴是
log10(assets)的数值(如2、3、4、5),次轴需要对应显示原始assets值(100、1000、10000、100000)。 - 原代码中
sec_axis的breaks参数传入的是原始assets值,但transform是将主轴值转换为原始值,两者不匹配,导致刻度对应错误。
修正后的代码
以下提供两种可行的修正方案:
方案一:通过主轴刻度生成次轴标签
gg1 <- carData::Ornstein |> ggplot2::ggplot(ggplot2::aes(x = assets)) + ggplot2::geom_density() + ggplot2::ylab("Density") + ggplot2::geom_rug() + ggplot2::ggtitle("(a)") + ggplot2::theme_bw() gg2 <- carData::Ornstein |> ggplot2::ggplot(ggplot2::aes(x = base::log10(assets))) + ggplot2::geom_density(adjust = 0.65) + ggplot2::labs( y = "Density", x = base::expression(log[10](assets)) ) + ggplot2::geom_rug() + ggplot2::scale_x_continuous( # 主轴设置log10(assets)的刻度为2到5 breaks = 2:5, sec.axis = ggplot2::sec_axis( transform = ~ .x, breaks = 2:5, # 将主轴刻度转换为10的幂次作为次轴标签 labels = function(x) 10^x ) ) + ggplot2::theme_bw() gridExtra::grid.arrange(gg1, gg2, ncol = 2)
方案二:明确次轴转换规则与对应刻度
gg1 <- carData::Ornstein |> ggplot2::ggplot(ggplot2::aes(x = assets)) + ggplot2::geom_density() + ggplot2::ylab("Density") + ggplot2::geom_rug() + ggplot2::ggtitle("(a)") + ggplot2::theme_bw() gg2 <- carData::Ornstein |> ggplot2::ggplot(ggplot2::aes(x = base::log10(assets))) + ggplot2::geom_density(adjust = 0.65) + ggplot2::labs( y = "Density", x = base::expression(log[10](assets)) ) + ggplot2::geom_rug() + ggplot2::scale_x_continuous( sec.axis = ggplot2::sec_axis( # 次轴由主轴值计算原始assets(10的主轴值次方) transform = ~ 10^., # 主轴上的对应刻度点 breaks = 2:5, # 次轴显示的原始assets值标签 labels = c("100", "1000", "10000", "100000") ) ) + ggplot2::theme_bw() gridExtra::grid.arrange(gg1, gg2, ncol = 2)
效果说明
两种方案都能实现主轴(log10刻度)与次轴(原始assets刻度)的正确对应:主轴的2对应次轴100,5对应次轴100000,完全匹配原代码basicPowerAxis的效果。
内容的提问来源于stack exchange,提问作者petzi
相关产品推荐
相关产品推荐

