如何在ggplot中创建带有反向(第二个)y轴的geom_line图形?

4
我正在尝试绘制一个 geom_line 图,与一个条形图结合。条形图将具有反转的 y 轴。最终产品预计类似于下面的图。我不确定如何将反转的条形图与线图组合起来。
此外,如果我们可以为 A、B 和 C 添加图例,那就太好了。感谢任何帮助。
library(ggplot2)
Mydata = data.frame(ID=1:14, A=1:14, B=20:7, C=100:113)

ggplot(data = Mydata) + 
geom_line(aes(x=ID, y = A)) +
geom_line(aes(x=ID, y = B))

enter image description here

我已经尝试了以下代码。第二个y轴现在从90到130反转,但是条形图仍未反转。
ggplot(data = Mydata) + 
geom_line(aes(x=ID, y = A)) +
geom_line(aes(x=ID, y = B)) + 
geom_bar(stat="identity", aes(x=ID, y = (190-C)/5)) + 
scale_y_continuous(breaks=seq(0,30,5), sec.axis = sec_axis(trans=~190-.*5, breaks=seq(0,130,10)))

enter image description here

3个回答

2

更新

如果你想要左边的图表外观而不是右边的图表:

enter image description here enter image description here

那么你只需要在geom_col中使用rev()即可:

geom_col(aes(x = ID, y = rev(-(190-C)/5)))

如果你想让条形图向下移动,它们必须包含负值。当您添加第二个y轴时,实际上只是一个视觉效果;您的所有跟踪都没有真正遵循该轴。"技巧"在于转换。如果您希望条形图位于线条上方,就好像顶部有一个零轴用于条形图,底部有一个零轴用于线条,则需要进行一些更改。首先,条形图需要是负数才能向下;这驱动了其他所有内容。其次,线条的值应该小于条形图中的值,而不会失去纵横比(缺乏更好的术语)。将两个跟踪偏移以确保条形图不重叠的最简单方法是通过否定当前的y并将其偏移至出现在条形图中的最大值来完成。首先,找到该偏移值:
offset = max((190 - Mydata$C)/5)  # how much to offset the other traces

看看我们所做的,y 轴取反并偏移:

offset = max((190 - Mydata$C)/5)
ggplot(data = Mydata) + 
  geom_line(aes(x = ID, y = -A - offset)) +
  geom_line(aes(x = ID, y = -B - offset)) +
  geom_col(aes(x = ID, y = -(190-C)/5))

进入图像描述

接下来,我们需要修复两个 y 轴。从左侧的 y 轴开始,我们需要让它看起来好像我们没有把所有东西都取反。为了做到这一点,我们需要找到实际的负值,反映在 y 轴上的感知零点。

由于列 B 的值较大(为 20),我用它来确定底部位置。此外,由于 A 和 B 之间的最低值为 1,我们将添加 -1 来获得那条零线。要查找底部或我们想要的零线位置,请查找取反的 B 列的最小值 - 偏移量减去 1。

btm = min(-1 * Mydata$B - offset - 1) # because the min value is 1

看看我们目前的进展:

btm = min(-1 * Mydata$B - offset - 1) # because the min value of B is 1
offset = max((190 - Mydata$C)/5)
ggplot(data = Mydata) + 
  geom_line(aes(x = ID, y = -A - offset)) +
  geom_line(aes(x = ID, y = -B - offset)) +
  geom_col(aes(x = ID, y = -(190-C)/5)) +
  scale_y_continuous(breaks = seq(btm, by = 10, length.out = 5), 
                     labels = seq(0, 40, 10))

enter image description here

现在是添加第二个y轴的时候了。

我反转了变换的符号,然后看看数字在第二个y轴上落在哪里。

btm = min(-1 * Mydata$B - offset - 1) # because the min value of B is 1
offset = max((190 - Mydata$C)/5)
ggplot(data = Mydata) + 
  geom_line(aes(x = ID, y = -A - offset)) + 
  geom_line(aes(x = ID, y = -B - offset)) +
  geom_col(aes(x = ID, y = -(190-C)/5)) +
  scale_y_continuous(breaks = seq(btm, by = 10, length.out = 5), 
                     labels = seq(0, 40, 10),
                     sec.axis = sec_axis(~190 + . * 5))  # changed sign!

这里输入图片描述

接下来,我们需要确定辅助 y 轴上的零点在哪里。为了计算这个值,我们需要首先计算在图表中经过转换后的 C 列的最大值,然后加上 C 列的最大值、B 列的最大值以及之前计算出的偏移量。

top = max(((190 + (-(190 - Mydata$C)/5) * 5) + offset - btm + max(Mydata$B)))

请注意,在创建这个值的调用中:
条形图中的Y为:
-(190 - Mydata$C)/5
使用第二个轴转换后条形图中的Y为:
190 + (-(190 - Mydata$C)/5) * 5
好了!现在您可以根据自己的需要调整标签了。 (我猜你已经有想法了。)
btm = min(-1 * Mydata$B - offset - 1) # because the min value of B is 1
offset = max((190 - Mydata$C)/5)
top = max(((190 + (-(190 - Mydata$C)/5) * 5) + offset - btm + max(Mydata$B))) 

ggplot(data = Mydata) + 
  geom_line(aes(x = ID, y = -A - offset)) +
  geom_line(aes(x = ID, y = -B - offset)) +
  geom_col(aes(x = ID, y = -(190-C)/5)) +
  scale_y_continuous(breaks = seq(btm, by = 10, length.out = 5), 
                     labels = seq(0, 40, 10),
                     sec.axis = sec_axis(~190 + . * 5,  # changed sign!
                                         breaks = seq(top - 20 * 4, top, by = 20),
                                         labels = seq(100, 0, by = -25)))

enter image description here


嗨,Kat,非常感谢您详细的回答。在我的数据中,“C”从100增加到113,但是您的图表显示“C”正在减少。您能否请检查一下?此外,您能否说明如何为图表添加图例?谢谢。 - Yang Yang
我以为你想要条形图反转,所以这种外观是有意的。你的问题显示第二个y轴在下降,并且你说你想要倒置的条形图。你能解释一下你在寻找什么吗?至于图例,你想要什么?通常是一个关键字,比如红色代表这个,蓝色代表那个。现在,所有东西都是没有特点的。要为每个geom添加图例条目,请在每个aes中添加color =“您想在图例中看到的内容”。这将添加颜色。(对于条形图,请使用fill。这看起来比color好看。) - Kat
嗨Kat,感谢您的解释。在我的数据中,“C”从100开始增加到113(当“ID”=1,“C”=100)。然而,您的图表显示“C”从113开始减少,然后降至100(当“ID”=1,“C”=113)。这与数据不符。例如,随着第二个y轴从上到下从0增加到100,应该沿着x轴增加“C”的条形图长度。您能否请检查一下?谢谢。 - Yang Yang
我想我明白你的意思了。请查看我的更新答案。如果我还是误解了,请告诉我。 - Kat
嗨,Kat,感谢您的回复。我认为在原始数据中,“C”从100增加到113时,左侧的图更有意义。 - Yang Yang

1

你可以尝试使用基础图形,这样会变得非常简单。

## base plane with the lines
par(mar=c(5, 4, 4, 3) + .1)
matplot(Mydata[2:3], type='o', pch=c(0, 4), col=1, lty=1,
        ylim=c(min(Mydata[2:3]), max(Mydata[2:3]) + 10),
        yaxt='n', xlim=c(.5, nrow(Mydata) + .5),
        xlab='xlab', ylab='ylab', main='main')
axis(2, axTicks(2), axTicks(2), tck=.02, las=2)  ## make nicer west axis
ymax <- par()$usr[4]  ## store north coord
xseq <- seq_len(nrow(Mydata))  ## store x axis
fac <- 1/15  ## define reduction factor for upper barplot
## upper barplot using `rect`
rect(xseq - .25, ymax, xseq + .25, ymax - Mydata$C*fac, 
     col='#139FFF', border=NA)
axis(3, xseq, tck=.02, labels=FALSE)  ## north x axis
ats <- seq.int(0, max(Mydata$C), by=50)  ## east ats
axis(4, y - ats*fac, ats, tck=.02, las=2)  ## east axis
## legend
legend('bottomright', legend=c('foo', 'bar', 'baz'), lty=1,
       lwd=c(1, 1, 5), pch=c(0, 4, NA), col=c(1, 1, '#139FFF'),
       bty='n', horiz=TRUE)

enter image description here


Data:

Mydata <- structure(list(ID = 1:14, A = 1:14, B = 20:7, C = 100:113), class = "data.frame", row.names = c(NA, 
-14L))

嗨Jay,非常感谢你的帮助。这个图看起来很棒!能否使用ggplot绘制这个图?我很想知道为什么我的代码不能正常工作。谢谢! - Yang Yang
1
@YangYang 我不知道,我不使用它。显然,该图表也没有使用那个软件包制作,可能甚至不是在R中制作的。 - jay.sf

1

Hadley Wickham在这里回答(https://dev59.com/V3A75IYBdhLWcg3wy8br#3101876)说,ggplot不支持分离y轴比例尺,因为他认为这种方法从根本上是有缺陷的。

尽管如此,使用gridExtra包,您可以相当接近。基本上你需要单独制作两个图表,然后将它们附加到网格中。

以下是输出结果...

enter image description here

...这是输入内容:

library(tidyverse)
library(gridExtra)

Mydata = data.frame(ID=1:14, A=1:14, B=20:7, C=100:113)

lines<-Mydata%>%
  select(-C)%>%
  gather(key=Lines,value=vals,-ID)

bars<-Mydata%>%
  select(ID,C)


b<-ggplot()+
  geom_bar(data=bars,  aes(ID,C),stat="identity",fill='blue')+
  scale_y_reverse()+
  theme_minimal()+
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank())+
  labs(x="",y="Y Axis Label")+
  scale_x_continuous(limits = c(0,15))

l<-ggplot()+
    geom_line(data=lines,aes(ID,vals,color=Lines))+
  theme_minimal()+
  theme(legend.position="bottom")+
  scale_x_continuous(limits = c(0,15))+
  labs(y="Second Y axis Label")

grid.arrange(b,l,nrow=2)

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