以编程方式在 R ggplot2 中标记多个 ablines

Programmatically label multiple ablines in R ggplot2

关于在 ggplot2 中标记单个 geom_abline() 的现有问题:

None 其中的一个用例是我想将多条参考线添加到散点图,目的是允许对斜率范围内的点进行轻松分类。这是该图的可重现示例:

library(ggplot2)

set.seed(123)
df <- data.frame(
  x = runif(100, 0, 1),
  y = runif(100, 0, 1))

lines <- data.frame(
  intercept = rep(0, 5),
  slope = c(0.1, 0.25, 0.5, 1, 2))

p <- ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  geom_abline(aes(intercept = intercept, slope = slope),
              linetype = "dashed", data = lines)
p

由于我发现无法通过其他问题以编程方式执行此操作,因此我通过数据框“缩放”了手动方法,使用反复试验找出合理的标签位置。

labels <- data.frame(
  x = c(rep(1, 3), 0.95, 0.47),
  y = c(0.12, 0.28, 0.53, 1, 1),
  label = lines$slope)

p + geom_text(aes(label = label), color = "red", data = labels)

有没有比反复试验更好的方法?虽然这不是 5 行坏,但我仍然不得不在导出时进一步重做我的调整,因为 R 会话中的原型制作与 R 会话中的原型制作之间的情节纵横比和间距不相同. 生成的图像。程序化标签将是一个巨大的帮助。

一些想法:

这是查看新 geomtextpath 的好机会,它看起来真的很酷。它有一堆 geoms 可以沿着不同类型的路径放置文本,因此您可以将标签投影到线条上。

但是,我想不出一种按照您想要的方式设置 hjust 参数的好方法:文本是根据绘图的范围而不是文本所在的路径对齐的。在这种情况下,默认的 hjust = 0.5 意味着标签位于 x = 0.5(因为 x 范围是 0 到 1;不同的范围会有不同的位置)。你可以做一些调整,但我很快就让标签离开了绘图的范围。如果在中间或中间附近没问题,那么这个选项看起来很不错。

library(ggplot2)
library(geomtextpath)
library(dplyr)

# identical setup from the question

p +
  geom_textabline(aes(intercept = intercept, slope = slope, label = as.character(slope)),
                  data = lines, gap = FALSE, offset = unit(0.2, "lines"), text_only = TRUE)

或者,由于您已经有了直线方程,您可以做一些代数运算来找到您的坐标。在 y 最大的地方求解 x,在 x 最大的地方求解 y;对于其中的每一个,使用 pmin 将它们限制在图表的范围内。例如斜率 = 0.5 的线在 x = 2 之前不会达到 y = 1,这在图表之外,因此将其限制为绘图的最大 x。你如何定义最大值可以不同:可以是数据中包含的最大值,你也可以从保存的绘图对象中提取(不确定是否存在这些不相同的情况),或者它可以从中提取面板布局或中断。或者在 How can I extract plot axes' ranges for a ggplot2 object? 上获得更多想法。由你决定。

# y = intercept + slope * x
xmax <- max(df$x) 
# or layer_scales(p)$x$get_limits()[2] for data range
# or ggplot_build(p)$layout$panel_params[[1]]$y.range[2] for panel range
ymax <- max(df$y)
lines_calc <- lines %>%
  mutate(xcalc = pmin((ymax - intercept) / slope, xmax),
         ycalc = pmin(intercept + slope * xmax, ymax))

p +
  geom_text(aes(x = xcalc, y = ycalc, label = as.character(slope)),
            data = lines_calc, vjust = 0, nudge_y = 0.02)

添加一个答案作为对卡米尔第一个答案的轻微改进,或者“只是不同”,这取决于您的观点。这不是我自己的,只是在 this comment.

中重新创建了 geomtextpath 包创建者的那个

到目前为止,总共有四个解决方案:

  • 我的试错手册方法
  • camille 使用 geom_textabline 和默认 hjust=0.5 位置
  • camille 正在寻找情节的极值,限制为 xmax 或 ymax,以先达到者为准
  • 这个,它使用 scale_hjust_manual 将所需的试验和错误减少至少一半(只需要一个值而不是让 x 和 y 都正确)
# same setup as in question
library(geomtextpath)

p + geom_textabline(aes(intercept = intercept, 
                        slope = slope,
                        label = as.character(slope),
                        hjust = as.character(slope)),
                    data = lines,
                    gap = FALSE,
                    text_only = TRUE,
                    offset = unit(0.2, "lines"),
                    color = "red") +
  scale_hjust_manual(values = c(0.65, 0.65, 0.65, 0.65, 0.5))

对于任何想要跟进的人来说,这听起来像是 there is hope 通过 geomtextpath 的“真正的解决方案”,这将最终让 hjust 在幕后“做正确的事”。