You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 14:35:59