英文:
ggplot legend with geom_tile and geom_line
问题
我试图制作一个带有两个垂直轴的ggplot图表,用户可以决定在每一侧使用哪种geom。选项有lines、points和bars。当在左侧添加bars和右侧添加lines时,我在图例中无法获得正确的形状。
使用线条和点正常工作
# 准备图例指南
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(NA, NA, 20, 20),
linetype = c(1, 1, 0, 0))))
# 准备geoms
gglines <- geom_line(aes(x = Date, y = Val1, color = ID, group = ID), na.rm = TRUE)
ggpoints <- geom_point(aes(x = Date, y = Val2_scl, color = ID, group = ID), na.rm = TRUE)
# 生成ggplot
ggplot(dfAll) + gglines + ggpoints +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
使用bars和点也正常工作
# 准备图例指南
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(15, 15, 20, 20),
linetype = c(0, 0, 0, 0))))
# 准备geoms
ggtiles <- geom_tile(aes(x = Date, y = Val1/2, height = Val1, fill = ID, group = ID),
na.rm = TRUE, stat = "identity", position = position_dodge(preserve = "single"),
show.legend = FALSE)
# 生成ggplot
ggplot(dfAll) + ggtiles + ggpoints +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
使用bars和lines时,在图例中未显示任何形状
# 准备图例指南
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(15, 15, NA, NA),
linetype = c(0, 0, 1, 1))))
# 准备geoms
gglines <- geom_line(aes(x = Date, y = Val2_scl, color = ID, group = ID), na.rm = TRUE)
# 生成ggplot
ggplot(dfAll) + ggtiles + gglines +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
我尝试了在guide_legend()
中使用不同的shape
值,但图表似乎忽略了该属性。我认为这与前两个示例使用了geom_point()
调用,而最后一个示例没有使用有关。shape
是geom_point()
对象的属性之一,但不是geom_line()
的属性。
如果有人想到使用geom_bar()
而不是geom_tile()
的解决方案,请注意,我在这里使用后者是基于这个SO答案,它帮助我在其中一个轴反转时绘制倒置的bars。
英文:
I'm trying to produce a ggplot chart with two vertical axes where the user can decide which geom to use at each side. The options are lines, points and bars. I'm having troubles to have the correct shape in the legend when adding bars at the left and lines at the right.
MRE
library(ggplot2)
library(dplyr)
library(plotly)
# prepare raw data
df1 <- data.frame(ID = c("A", "A", "A", "A", "B", "B", "B", "B"),
Date = structure(c(19078, 19085, 19092, 19099, 19078, 19085, 19092, 19099), class = "Date"),
Val = c(236, 221, 187, 136, 77, 100, 128, 180))
df2 <- data.frame(ID = c("J", "J", "J", "J", "K", "K", "K", "K"),
Date = structure(c(19078, 19085, 19092, 19099, 19078, 19085, 19092, 19099), class = "Date"),
Val = c(478, 500, 549, 479, 73, 5, 15, 74))
# prepare y2 scaled data
ylim1 <- range(df1$Val)
ylim2 <- range(df2$Val)
scale_y2.1 <- function(y, ylim1, ylim2) {
ylim1[1] + (ylim1[2] - ylim1[1]) *(y - ylim2[1])/(ylim2[2] - ylim2[1])
}
dfAll <- full_join(df1, df2, by = c("ID", "Date"), suffix = c("1", "2"))
y2.scl <- scale_y2.1(dfAll$Val2, ylim1, ylim2)
dfAll <- dfAll %>% mutate(Val2_scl = y2.scl)
# prepare y2 ticks and scaled breaks
labs2 <- pretty(ylim2)
brks2 <- scale_y2.1(labs2, ylim1, ylim2)
With lines and points it works fine
# prepare legend guides
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(NA, NA, 20, 20),
linetype = c(1, 1, 0, 0))))
# prepare geoms
gglines <- geom_line(aes(x = Date, y = Val1, color = ID, group = ID), na.rm = TRUE)
ggpoints <- geom_point(aes(x = Date, y = Val2_scl, color = ID, group = ID), na.rm = TRUE)
# generate ggplot
ggplot(dfAll) + gglines + ggpoints +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
With bars and points it works fine too
# prepare legend guides
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(15, 15, 20, 20),
linetype = c(0, 0, 0, 0))))
# prepare geoms
ggtiles <- geom_tile(aes(x = Date, y = Val1/2, height = Val1, fill = ID, group = ID),
na.rm = TRUE, stat = "identity", position = position_dodge(preserve = "single"),
show.legend = FALSE)
# generate ggplot
ggplot(dfAll) + ggtiles + ggpoints +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
With bars and lines it fails to show any shape for the bars in the legend
# prepare legend guides
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(15, 15, NA, NA),
linetype = c(0, 0, 1, 1))))
# prepare geoms
gglines <- geom_line(aes(x = Date, y = Val2_scl, color = ID, group = ID), na.rm = TRUE)
# generate ggplot
ggplot(dfAll) + ggtiles + gglines +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
I've tried with different shape
values in the guide_legend()
but the plot just seems to ignore that property. I think it has something to do with the fact that the two first examples use a geom_point()
call, while the last one doesn't. The shape
is one of the properties of a geom_point()
object, but not of a geom_line()
one.
I know I've read in a SO answer to a question about a legend that someone suggested to use a dummy geom_something()
call just to have a shape added to the legend. Maybe that could work here using geom_point()
, but I cannot get to find that thread or documentation on how to add an empty/dummy geom
.
In case someone thinks of a solution using geom_bar()
instead of geom_tile()
note that I'm using the later here based on this SO answer which helped me get the bars to be plotted inverted when one of the axes is reversed.
答案1
得分: 1
以下是翻译好的部分:
"我不知道是否有一个解决这个问题的方法,实际上并不是一种权宜之计,但至少我们可以像你提到的那样使用虚拟的 geom_point()
来创建图例。
因此,我们可以使用上面示例中的 ggpoints
的定义,并将其透明度设置为 alpha = 0.0
,以使点不再可见。请注意,我们还必须在 overwrite.aes()
中覆盖 alpha
,以使图例中的条形图可见。
编辑后的代码部分:
# 准备几何对象
ggpoints <-
geom_point(aes(
x = Date,
y = Val2_scl,
color = ID,
group = ID
),
na.rm = TRUE,
alpha = 0.0)
# 准备图例指南
legguides <- guides(color = guide_legend(
title.position = "top",
override.aes = list(
shape = c(15, 15, NA, NA),
linetype = c(0, 0, 1, 1),
alpha = 1.0
)
))
# 生成 ggplot 图
ggplot(dfAll) + ggtiles + gglines + ggpoints +
scale_y_continuous(sec.axis = dup_axis(
breaks = rev(brks2),
labels = rev(labs2),
name = "Val2"
)) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
英文:
I don't know whether there is a solution for this problem which does not in fact is a workaround, but we can at least create the legend using a dummy geom_point()
as already mentioned by you.
Therefore, we can use the definition of ggpoints
from the above examples and set its transparency to alpha = 0.0
such that the points are not visible any more. Note that we then additionally have to overwrite alpha
in overwrite.aes()
in order to make the bars visible in the legend.
Edited code parts:
# prepare geoms
ggpoints <-
geom_point(aes(
x = Date,
y = Val2_scl,
color = ID,
group = ID
),
na.rm = TRUE,
alpha = 0.0)
# prepare legend guides
legguides <- guides(color = guide_legend(
title.position = "top",
override.aes = list(
shape = c(15, 15, NA, NA),
linetype = c(0, 0, 1, 1),
alpha = 1.0
)
))
# generate ggplot
ggplot(dfAll) + ggtiles + gglines + ggpoints +
scale_y_continuous(sec.axis = dup_axis(
breaks = rev(brks2),
labels = rev(labs2),
name = "Val2"
)) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论