【问题标题】:Y limits for ggplot with sec.axis带有 sec.axis 的 ggplot 的 Y 限制
【发布时间】:2020-03-05 13:36:43
【问题描述】:

我需要使用 sec.axis 创建一个双 y 图,但无法正确缩放两个轴。

我一直在遵循此线程中的说明:ggplot with 2 y axes on each side and different scales

但每次我将 ylim.prim 中的下限更改为 0 以外的任何值时,都会弄乱整个情节。出于可视化原因,我需要两个轴的非常具体的 y 限制。另外,当我将 geom_col 更改为 geom_line 时,它​​也会弄乱辅助轴的限制。

climate <- tibble(
  Month = 1:12,
  Temp = c(23,23,24,24,24,23,23,23,23,23,23,23),
  Precip = c(101,105,100,101,102, 112, 101, 121, 107, 114, 108, 120)
  )

ylim.prim <- c(0, 125)   # in this example, precipitation
ylim.sec <- c(15, 30)    # in this example, temperature

b <- diff(ylim.prim)/diff(ylim.sec)
a <- b*(ylim.prim[1] - ylim.sec[1])

ggplot(climate, aes(Month, Precip)) +
  geom_col() +
  geom_line(aes(y = a + Temp*b), color = "red") +
  scale_y_continuous("Precipitation", sec.axis = sec_axis(~ (. - a)/b, name = "Temperature"),) +
  scale_x_continuous("Month", breaks = 1:12)  

ylim.prim <- c(0, 125)   # in this example, precipitation
ylim.sec <- c(15, 30)    # in this example, temperature

b <- diff(ylim.prim)/diff(ylim.sec)
a <- b*(ylim.prim[1] - ylim.sec[1])

ggplot(climate, aes(Month, Precip)) +
  geom_line() +
  geom_line(aes(y = a + Temp*b), color = "red") +
  scale_y_continuous("Precipitation", sec.axis = sec_axis(~ (. - a)/b, name = "Temperature"),) +
  scale_x_continuous("Month", breaks = 1:12)  

ylim.prim <- c(95, 125)   # in this example, precipitation
ylim.sec <- c(15, 30)    # in this example, temperature

b <- diff(ylim.prim)/diff(ylim.sec)
a <- b*(ylim.prim[1] - ylim.sec[1])

ggplot(climate, aes(Month, Precip)) +
  geom_line() +
  geom_line(aes(y = a + Temp*b), color = "red") +
  scale_y_continuous("Precipitation", sec.axis = sec_axis(~ (. - a)/b, name = "Temperature"),) +
  scale_x_continuous("Month", breaks = 1:12)  

【问题讨论】:

  • 我认为a 的等式应该是ylim.prim[1] - b*ylim.sec[1]。如果我使用它而不是您的定义,则两个比例之间的重新映射似乎有效,并且两个轴的限制与您的定义相匹配。

标签: r ggplot2


【解决方案1】:

显示直接代数变换的简单解决方案。对两个 y 轴变量使用全轴范围。

library(ggplot2)
dat <- data.frame(Date = seq.Date(as.Date("2010-01-01"), by = "day", length.out = 61),
                  a = c(50:-10),
                  b = c(-100:-40)
                  )

a.diff <- max(dat$a) - min(dat$a)
b.diff <- max(dat$b) - min(dat$b)
a.min <- min(dat$a)
b.min <- min(dat$b)

ggplot(dat, aes(Date, a)) +
    geom_line(color = "blue") +
    geom_line(aes(y = (b - b.min) / b.diff * a.diff + a.min), color = "red") +
    scale_y_continuous(sec.axis = sec_axis(trans = ~((. -a.min) * b.diff / a.diff) + b.min,
                                           name = "b")) +
    theme(
        axis.title.y.left = element_text(color = "blue"),
        axis.text.y.left = element_text(color = "blue"),
        axis.title.y.right = element_text(color = "red"),
        axis.text.y.right = element_text(color = "red"))

【讨论】:

    【解决方案2】:

    从我在代码中看到的,你在两个尺度之间的转换有点太简单了。

    为了获得我认为您想要的结果,有必要对温度数据进行标准化(这样您可以改变分布和均值,并使其适合您的主要 y 尺度),然后计算次要 y 轴的反向归一化。

    归一化是指:(Temp - mean(TEMP))/sd(TEMP),其中TEMP 是所有值的数组,Temp 是要绘制的特定值。

    EDIT: 由于 ggplot2 只允许转换原始比例,并且没有为辅助轴设置唯一限制的选项,因此没有简单的选项来设置辅助 y 轴的 ylim。但是,有一种方法可以做到,这很 hacky,但我会在这里展示。

    通过使用简单的线性模型(或任何其他求解方法)求解两个边界的归一化,可以使辅助 y 轴的变换匹配唯一限制。 我使用的线性变换引入了as 这两个变量。 s 是结果乘以的比例因子,它允许改变绘制数据相对于主 y 轴的分布。变量 a 沿 y 轴移动生成的变换。

    变换是:

    y = a + (Temp - mean(TEMP))/sd(TEMP)) * s
    

    计算如下:

    climate <- tibble(
      Month = 1:12,
      Temp = c(23,23,24,24,24,23,23,23,23,23,23,23),
      Precip = c(101,105,100,101,102, 112, 101, 121, 107, 114, 108, 120)
      )
    
    ylim.prim <- c(95, 125)   # in this example, precipitation
    ylim.sec <- c(15, 30)    # in this example, temperature
    
    TEMP <- climate$Temp #needed for coherent normalisation
    
    # This is quite hacky, but it works if you want to set a boundary for the secondary y-axis
    fit = lm(b ~ . + 0, 
       tibble::tribble(
         ~a, ~s,  ~b,
          1,  (ylim.sec[1] - mean(TEMP))/sd(TEMP),  ylim.prim[1],
          1,  (ylim.sec[2] - mean(TEMP))/sd(TEMP), ylim.prim[2]))
    
    a <- fit$coefficients['a']
    s <- fit$coefficients['s']
    
    

    要将辅助 y 轴刻度调整回您的值,只需反向执行计算中的每一步。

    这样您就可以很好地调整两个时间序列的叠加:

    ggplot(climate, aes(Month, Precip)) +
      geom_line() + 
      geom_line(aes(y = (a + ((Temp - mean(TEMP))/sd(TEMP)) * s) ), color = "red") +
      scale_y_continuous("Precipitation", 
                         limits=ylim.prim,
                         sec.axis = sec_axis(~ (. - a) / s * sd(TEMP) + mean(TEMP), name = "Temperature"),) +
      scale_x_continuous("Month", breaks = 1:12) +
      theme(axis.title.y.right = element_text(colour = "red"))
    

    【讨论】:

    • 你在哪里应用了 ggplot( ) 函数中的 ylim.sec ?
    • @DarwinPC 好问题,感谢您指出!我修改了我的答案以使用 ylim.sec 值,这使转换的步骤有点复杂,但至少对于线性转换它确实像这样工作。不用说,这很 hacky 并且违背了 ggplot2 的哲学......
    【解决方案3】:

    这个怎么样:

      ggplot(climate, aes(Month, Precip)) +
        geom_line() +
        geom_line(aes(y = 4.626*Temp), color = "red") +
        scale_y_continuous("Precipitation", sec.axis = sec_axis(~ ./4.626, name = "Temperature"),) +
        scale_x_continuous("Month", breaks = 1:12)   
    

    如果您需要进一步解释,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-27
      • 2014-06-06
      • 2018-06-05
      • 1970-01-01
      • 2015-10-06
      • 1970-01-01
      • 2015-01-11
      相关资源
      最近更新 更多