ggplot第二坐标轴的Y轴限制

6

我需要使用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)  

GGplot1

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)  

GGplot2

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)  

GGplot3


我认为你关于a的方程式应该是ylim.prim[1] - b*ylim.sec[1]。如果我使用这个方程式代替你的定义,两个比例尺之间的重新映射似乎可以正常工作,并且两个轴的限制与你的定义相匹配。 - aosmith
3个回答

6
根据代码,你在两个刻度之间的转换过于简单。为了达到你想要的结果,需要对温度数据进行归一化处理(这样你就可以变化其分布和平均值,使其适合你的主y轴),然后计算次要y轴的反向归一化处理。
所谓归一化是指:(Temp-mean(TEMP))/ sd(TEMP),其中TEMP是所有值的数组,而Temp是要绘制的特定值。
编辑:由于ggplot2仅允许对原始比例进行转换,而且没有设置次要轴唯一限制的选项,因此没有轻松设置次要y轴的ylim选项。 然而,有一种方法可以做到这一点,虽然它很hacky,但我将在这里展示。
通过使用简单的线性模型(或任何其他求解方法)来解决两个边界的归一化问题,可以使次要y轴的变换与唯一的限制相匹配。 我使用的线性变换引入了两个变量ass是一个缩放因子,结果乘以该因子,从而允许根据主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"))

temperature vs precipitation plot with secondary y axis scaled to fit


1
你在 ggplot() 函数中哪里应用了 ylim.sec? - Darwin PC
@DarwinPC 很好的问题,感谢您指出!我修改了我的答案,使用了 ylim.sec 值,这使得转换步骤有点复杂,但至少对于线性转换来说是可行的。不用说,这相当地 hacky,并且违背了 ggplot2 的哲学... - an outpost

2
这个怎么样:
  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)   

让我知道是否需要进一步解释。 在此输入图片描述

0
一个简单的解决方案,展示了直接代数变换。对于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"))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接