ggplot2中次要y轴的更好转换

4

我正在尝试为ggplot2的一些数据添加一个次要y轴。我的数据p看起来像这样:

  aspect  yhat Count
    <dbl> <dbl> <dbl>
 1   1.37 0.329   144
 2  16.9  0.329     5
 3  32.3  0.330     5
 4  47.8  0.331     0
 5  63.3  0.333    57
 6  78.8  0.333    67
 7  94.3  0.332    13
 8 110.   0.332     0
 9 125.   0.332     0
10 141.   0.331     0

我试图按照以下方式绘制它:

 #get the information to for using a secondary y axis
    ylim.prim <- c(min(p$yhat), max(p$yhat))
    ylim.sec <- c(min(p$Count, na.rm = T), max(p$Count, na.rm = T))

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

    #now make plot
    p1 = ggplot(p, aes(x = get(col), y = yhat)) +
      geom_line() +
      labs(x = col) +
      geom_line(aes(y = a + Count*b), color = "red", linetype = 2) +
      scale_y_continuous(name = "Mean Response", sec.axis = sec_axis(~ (. - a)/b, name = "Number of Pixels")) +
      theme_light() +
      theme(axis.line.y.right = element_line(color = "red"),
            axis.ticks.y.right = element_line(color = "red"),
            axis.text.y.right = element_text(color = "red"),
            axis.title.y.right = element_text(color = "red")
      ) +
      theme(legend.title = element_blank()) +
      theme(text=element_text(size=22))

这条命令生成了如下的图形:

enter image description here

我的问题在于黑线和红线之间存在很多未使用的空白空间,这使得解读数值有些困难,我想知道是否可以使用更好的转换方法将线条拉近,甚至重叠在一起,但我无法想出如何实现。

我认为这可能与相同的yhat值可以具有不同的count值有关。如果是这样,是否有办法解决?


也许可以使用主要限制“ylim.prim”来缩放“Count”,然后绘制两条线而无需辅助轴? - zx8754
2
离题:我认为最好使用分面。否则,您的图表可能会误导读者有关两个系列之间关系的理解(黑线在红线上方,这是否意味着黑线>红线?)。 - markus
不,它并不意味着黑色在红色之上,本质上,“Count”是指在“aspect”的区间内有多少个观测值,因此,“Count”为144意味着在1.37-16.9的范围内有144个“aspect”的观测值。因为这个原因,我认为将“Count”进行缩放也会产生误导。也许唯一的方法是使用分面,但我希望避免这种情况。 - Stefano Potter
2
离题:使用散点图会更好吗?“计数”可以是点的颜色或大小。 - zx8754
那是一个好主意,谢谢,我会试着操作一下的。 - Stefano Potter
1个回答

3

总体而言,我同意这个前提:多个(不同的)轴很容易让观众感到困惑。使用颜色和不同的对象(点与线)有助于区分两个值,但这只是一种缓解措施。

以下是使用点进行尝试:

count_slope_intercept <- c(m = diff(range(p$Count)), b = min(p$Count))
yhat_slope_intercept <- c(m = diff(range(p$yhat)), b = min(p$yhat))

p$Count2 <- yhat_slope_intercept["b"] +
  yhat_slope_intercept["m"] * (p$Count - count_slope_intercept["b"]) / count_slope_intercept["m"]

ggplot(p, aes(x = aspect, y = yhat)) +
  geom_line() +
  labs(x = "aspect") +
  geom_point(aes(y = Count2), color = "red") +
  scale_y_continuous(
    name = "Mean Response",
    sec.axis = sec_axis(~ count_slope_intercept["b"] + count_slope_intercept["m"] *
                          (. - yhat_slope_intercept["b"]) / yhat_slope_intercept["m"])
  ) +
  theme_light() +
  theme(axis.line.y.right = element_line(color = "red"),
        axis.ticks.y.right = element_line(color = "red"),
        axis.text.y.right = element_text(color = "red"),
        axis.title.y.right = element_text(color = "red")
        ) +
  theme(legend.title = element_blank()) +
  theme(text=element_text(size=22))

使用两个y轴的ggplot2

(可能有更平滑的方法,可以使用scales或类似工具实现。)


编辑: 是的,scales也是同样的操作:

p$Count3 <- scales::rescale(p$Count, to = range(p$yhat), from = range(p$Count))

ggplot(p, aes(x = aspect, y = yhat)) +
  geom_line() +
  labs(x = "aspect") +
  geom_point(aes(y = Count3), color = "red") +
  scale_y_continuous(
    name = "Mean Response",
    sec.axis = sec_axis(~ scales::rescale(., to = range(p$Count), from = range(p$yhat)))
  ) +
  theme_light() +
  theme(axis.line.y.right = element_line(color = "red"),
        axis.ticks.y.right = element_line(color = "red"),
        axis.text.y.right = element_text(color = "red"),
        axis.title.y.right = element_text(color = "red")
        ) +
  theme(legend.title = element_blank()) +
  theme(text=element_text(size=22))

(虽然上面我写得很明确,包括to=from=这两个参数,但是from默认为range(x),所以代码可以缩短一些。但对于这个例子来说,我认为更好的做法是明确表示fromto的范围。)

1
你的修改中,y = Count2 应该改为 y = Count3,对吗? - Tung

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