英文:
Rotate legend keys of geom_abline
问题
我想将geom_abline
的图例键旋转为水平图例键,或者如果可能的话,根据它们的斜率旋转。以下是可复现的代码(dput
df如下):
library(ggplot2)
p = ggplot(df, mapping = aes(x = x, y = y)) +
geom_point() +
geom_abline(df, mapping = aes(slope = slope, intercept = 0, color = factor(group))) +
coord_cartesian(xlim = c(0, 3), ylim = c(0, 3))
p
我尝试使用@Sandy Muspratt在这里给出的答案,使用grid
函数,但是这也会旋转ablines和图例键。所以我想知道有没有人知道如何旋转geom_abline
的图例键?
英文:
I would like to rotate the legends keys of geom_abline
to a horizontal legend key or if possible based on their slope. Here is some reproducible code (dput
df below):
library(ggplot2)
p = ggplot(df, mapping = aes(x = x, y = y)) +
geom_point() +
geom_abline(df, mapping = aes(slope = slope, intercept = 0, color = factor(group))) +
coord_cartesian(xlim = c(0, 3), ylim = c(0, 3))
p
<!-- -->
<sup>Created on 2023-04-19 with reprex v2.0.2</sup>
I tried using @Sandy Muspratt his answer here with grid
functions, but that also rotates the ablines and the legend keys. So I was wondering if anyone knows how to rotate the legend keys of geom_abline
?
dput
df:
df<-structure(list(x = c(0, 0, 0), y = c(0, 0, 0), slope = c(0.5,
1, 0.75), group = c("A", "B", "C")), class = "data.frame", row.names = c(NA,
-3L))
答案1
得分: 5
以下是您要翻译的内容:
这里的解决方案在于定义一个新的绘图关键字。然而,正如Tjebo在评论中指出的那样,通过这种方式定制斜率很困难,因为传递给draw_key_*
函数的数据并不包含所有的美学数据,只包含使标准绘图关键字(线宽、颜色、填充等)的必要数据。
一个解决方法是利用ggplots是使用ggproto
系统构建的,它本身是基于嵌套环境的。可以编写一个自定义的draw_key_*
函数,该函数查找适当的祖先框架以找到整个美学数据集:
draw_key_custom <- function (data, params, size) {
colour <- data$colour
datalist <- get("data", parent.frame((10)))
i <- which(sapply(datalist, function(x) "slope" %in% names(x)))[1]
data <- datalist[[i]]
data <- data[data$colour == colour,]
slope <- data$slope
intercept <- (1 - slope)/2
y1 <- ifelse(abs(slope) > 1, (sign(slope) + 1)/2, intercept)
y2 <- ifelse(abs(slope) > 1, 1 - (sign(slope) + 1)/2, intercept + slope)
x1 <- ifelse(abs(slope) > 1, (y1 - intercept)/slope, 0)
x2 <- ifelse(abs(slope) > 1, (y2 - intercept)/slope, 1)
grid::segmentsGrob(x1, y1, x2, y2,
gp = grid::gpar(col = data$colour,
lwd = data$linewidth * 2))
}
这允许:
ggplot(df, mapping = aes(x = x, y = y)) +
geom_point() +
geom_abline(aes(slope = slope, intercept = 0, color = factor(group)),
key_glyph = draw_key_custom) +
coord_cartesian(xlim = c(0, 3), ylim = c(0, 3))
一些需要指出的事情是:
- 在图例中改变斜率是不寻常且不必要的。
- 结果中的斜率不一定与图中的斜率匹配,除非图例键的宽高比与绘图面板的宽高比相匹配。除非在
theme
中指定了宽高比,否则在调整大小时,此比率不稳定。 - 上述实现对
ggplot
中的更改很脆弱,如果多条线具有相同的颜色,则可能无法正常工作。
不过,了解它可以做到还是不错的。
英文:
The solution here lies in defining a new draw key. However, as Tjebo points out in the comments, it is difficult to customize the slopes this way because the data passed to the draw_key_*
functions does not contain all the aesthetic data - only that necessary to make the standard draw keys (linewidth, colour, fill, etc).
One way round this is to take advantage of the fact that ggplots are built using the ggproto
system, which itself is based on nested environments. A custom draw_key_*
function can be written that looks up the appropriate ancestor frame to find the whole aesthetic data set:
draw_key_custom <- function (data, params, size) {
colour <- data$colour
datalist <- get("data", parent.frame((10)))
i <- which(sapply(datalist, function(x) "slope" %in% names(x)))[1]
data <- datalist[[i]]
data <- data[data$colour == colour,]
slope <- data$slope
intercept <- (1 - slope)/2
y1 <- ifelse(abs(slope) > 1, (sign(slope) + 1)/2, intercept)
y2 <- ifelse(abs(slope) > 1, 1 - (sign(slope) + 1)/2, intercept + slope)
x1 <- ifelse(abs(slope) > 1, (y1 - intercept)/slope, 0)
x2 <- ifelse(abs(slope) > 1, (y2 - intercept)/slope, 1)
grid::segmentsGrob(x1, y1, x2, y2,
gp = grid::gpar(col = data$colour,
lwd = data$linewidth * 2))
}
This allows:
ggplot(df, mapping = aes(x = x, y = y)) +
geom_point() +
geom_abline(aes(slope = slope, intercept = 0, color = factor(group)),
key_glyph = draw_key_custom) +
coord_cartesian(xlim = c(0, 3), ylim = c(0, 3))
A couple of things I would point out are that:
- It is unusual and unnecessary to change slopes in a legend
- The resulting slopes will not necessarily match the slopes in the plot unless the aspect ratios of the legend keys fit the aspect ratio of the plotting panel. This ratio is not stable under plot resizing unless the aspect ratio is specified in
theme
- The above implementation is fragile to changes within
ggplot
, and may not work if multiple lines have the same colour.
Still, nice to know it can be done I suppose...
答案2
得分: 3
以下是您要翻译的内容:
不是对问题的真正回答,我理解这更多是关于概念验证。想到由此而来的所有可能性真的令人惊叹。然而,我认为提供一种替代方案非常重要,特别是对于未来可能有奇特传说想法的读者,尽管更可行的解决方案可能是:
没有传说。
你可以通过直接标注来完全避免传说,这基本上是在改进 Allan 的答案,并使用 Allan 自己的 geomtextpath 包。
library(geomtextpath)
ggplot(df, mapping = aes(x = x, y = y)) +
geom_point() +
geom_textabline(aes(label = group, slope = slope, intercept = 0, color = group), hjust = .8) +
coord_cartesian(xlim = c(0, 3), ylim = c(0, 3)) +
theme(legend.position = "none")
<!-- -->
英文:
Not a real answer to the question, and I understand that this was also more about proof of concept. And it is absolutely amazing to think of all the possibilities that arise from it. However, I believe it is important to offer an alternative especially for future readers who might have a fancy legend in mind although the arguably better solution might be:
No legend.
You could avoid a legend altogether by simply direct labelling, here basically improving on Allan's answer with Allan's very own geomtextpath package.
library(geomtextpath)
ggplot(df, mapping = aes(x = x, y = y)) +
geom_point() +
geom_textabline(aes(label = group, slope = slope, intercept = 0, color = group), hjust = .8) +
coord_cartesian(xlim = c(0, 3), ylim = c(0, 3)) +
theme(legend.position = "none")
<!-- -->
答案3
得分: 1
在同一个网站上有@user20650的答案和评论。根据他在这里的回答<https://stackoverflow.com/questions/35703983/how-to-change-angle-of-line-in-customized-legend-in-ggplot2>和阅读这个<https://ggplot2.tidyverse.org/reference/draw_key.html>,我们可以这样做:
要使其工作,我们必须将lwd = data$size
替换为lwd = 0.5
。
library(ggplot2)
library(grid)
df<-structure(list(x = c(0, 0, 0), y = c(0, 0, 0), slope = c(0.5,
1, 0.75), group = c("A", "B", "C")), class = "data.frame", row.names = c(NA,
-3L))
GeomAbline$draw_key <- function(data, params, size)
{
segmentsGrob(0, 0.5, 1, 0.5,
gp = gpar(col = alpha(data$colour,
data$alpha),
lwd = 0.5* .pt, lty = data$linetype,
lineend = "butt"))
}
ggplot(df, mapping = aes(x = x, y = y)) +
geom_point() +
geom_abline(df, mapping = aes(slope = slope, intercept = 0, color = factor(group))) +
coord_cartesian(xlim = c(0, 3), ylim = c(0, 3))
英文:
On the same site there is an answer and comment of @user20650. According to his answer here <https://stackoverflow.com/questions/35703983/how-to-change-angle-of-line-in-customized-legend-in-ggplot2> and reading this <https://ggplot2.tidyverse.org/reference/draw_key.html> we could do it this way:
To make it work we have to replace lwd = data$size
by lwd = 0.5
library(ggplot2)
library(grid)
library(ggplot2)
library(grid)
df<-structure(list(x = c(0, 0, 0), y = c(0, 0, 0), slope = c(0.5,
1, 0.75), group = c("A", "B", "C")), class = "data.frame", row.names = c(NA,
-3L))
GeomAbline$draw_key <- function(data, params, size)
{
segmentsGrob(0, 0.5, 1, 0.5,
gp = gpar(col = alpha(data$colour,
data$alpha),
lwd = 0.5* .pt, lty = data$linetype,
lineend = "butt"))
}
ggplot(df, mapping = aes(x = x, y = y)) +
geom_point() +
geom_abline(df, mapping = aes(slope = slope, intercept = 0, color = factor(group))) +
coord_cartesian(xlim = c(0, 3), ylim = c(0, 3))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论