【问题标题】:Plot a legend outside of the plotting area in base graphics?在基本图形的绘图区域之外绘制图例?
【发布时间】:2015-11-10 17:02:57
【问题描述】:

正如标题所说:如何在使用基础图形时在绘图区域之外绘制图例?

我曾想过摆弄layout 并生成一个只包含图例的空图,但我会对仅使用基本图形工具的方式感兴趣,例如par(mar = ) 在右侧获得一些空间传说的情节。


这里是一个例子:

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")
legend(1,-1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

产生:

但如前所述,我希望图例位于绘图区域之外(例如,在图形/绘图的右侧。

【问题讨论】:

  • ...您也可以使用虚拟容器来破解图例,不时简单且非常方便。类似问题here.
  • @hhh 链接失效了。您可以使用这种方法更新它或发布答案吗?

标签: r plot legend


【解决方案1】:

除了已经提到的解决方案(使用layoutpar(xpd=TRUE))之外,另一种解决方案是在整个设备上用透明图覆盖您的图,然后在其中添加图例。

诀窍是在整个绘图区域上覆盖一个(空)图形,并在其中添加图例。我们可以使用par(fig=...) 选项。首先我们指示 R 在整个绘图设备上创建一个新绘图:

par(fig=c(0, 1, 0, 1), oma=c(0, 0, 0, 0), mar=c(0, 0, 0, 0), new=TRUE)

设置omamar 是必需的,因为我们希望绘图的内部覆盖整个设备。需要new=TRUE 以防止 R 启动新设备。然后我们可以添加空图:

plot(0, 0, type='n', bty='n', xaxt='n', yaxt='n')

我们已经准备好添加图例了:

legend("bottomright", ...)

将在设备的右下角添加一个图例。同样,我们可以将图例添加到顶部或右边距。我们唯一需要确保的是原始情节的边距足够大以容纳图例。

将所有这些都放入一个函数中;

add_legend <- function(...) {
  opar <- par(fig=c(0, 1, 0, 1), oma=c(0, 0, 0, 0), 
    mar=c(0, 0, 0, 0), new=TRUE)
  on.exit(par(opar))
  plot(0, 0, type='n', bty='n', xaxt='n', yaxt='n')
  legend(...)
}

还有一个例子。首先创建绘图,确保底部有足够的空间来添加图例:

par(mar = c(5, 4, 1.4, 0.2))
plot(rnorm(50), rnorm(50), col=c("steelblue", "indianred"), pch=20)

然后添加图例

add_legend("topright", legend=c("Foo", "Bar"), pch=20, 
   col=c("steelblue", "indianred"),
   horiz=TRUE, bty='n', cex=0.8)

导致:

【讨论】:

  • 这里是列表的重要补充。在图形here 中有一个关于如何使用多个绘图进行这项工作的解释。
  • Jan,有没有办法增加图例中的字体大小,而不会剪切一些文本?例如,我有一个图形 4 种不同类型的标签,但它们之间有很多空白。
  • 我写了一个更详细的问题stackoverflow.com/questions/42707308/…
【解决方案2】:

添加另一个我认为非常优雅的简单替代方案。

你的情节:

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

传说:

legend("bottomright", c("group A", "group B"), pch=c(1,2), lty=c(1,2),
       inset=c(0,1), xpd=TRUE, horiz=TRUE, bty="n"
       )

结果:

这里只有图例的第二行被添加到您的示例中。反过来:

  • inset=c(0,1) - 在 (x,y) 方向上按绘图区域的分数移动图例。在这种情况下,图例位于"bottomright" 位置。它在 x 方向移动 0 个绘图区域(因此保持在“右侧”),在 y 方向移动 1 个绘图区域(从下到上)。碰巧它出现在情节的正上方。
  • xpd=TRUE - 让图例出现在绘图区域之外。
  • horiz=TRUE - 指示生成水平图例。
  • bty="n" - 摆脱图例边界框的样式细节。

在侧面添加图例时同样适用:

par(mar=c(5,4,2,6))
plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

legend("topleft", c("group A", "group B"), pch=c(1,2), lty=c(1,2),
       inset=c(1,0), xpd=TRUE, bty="n"
       )

这里我们简单地调整了图例的位置,并在图的右侧添加了额外的边距空间。结果:

【讨论】:

  • 试过了,效果很好。简单得多。
【解决方案3】:

最近我发现了一个非常简单有趣的功能,可以在你想要的绘图区域之外打印图例。

在绘图的右侧制作外边距。

par(xpd=T, mar=par()$mar+c(0,0,0,5))

创建一个情节

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

添加图例并使用 locator(1) 函数,如下所示。然后,您只需在加载以下脚本后单击所需位置即可。

legend(locator(1),c("group A", "group B"), pch = c(1,2), lty = c(1,2))

试试看

【讨论】:

    【解决方案4】:

    我喜欢这样做:

    par(oma=c(0, 0, 0, 5))
    plot(1:3, rnorm(3), pch=1, lty=1, type="o", ylim=c(-2,2))
    lines(1:3, rnorm(3), pch=2, lty=2, type="o")
    legend(par('usr')[2], par('usr')[4], bty='n', xpd=NA,
           c("group A", "group B"), pch=c(1, 2), lty=c(1,2))
    

    唯一需要的调整是将右边距设置为足够宽以容纳图例。

    不过,这也可以自动化:

    dev.off() # to reset the graphics pars to defaults
    par(mar=c(par('mar')[1:3], 0)) # optional, removes extraneous right inner margin space
    plot.new()
    l <- legend(0, 0, bty='n', c("group A", "group B"), 
                plot=FALSE, pch=c(1, 2), lty=c(1, 2))
    # calculate right margin width in ndc
    w <- grconvertX(l$rect$w, to='ndc') - grconvertX(0, to='ndc')
    par(omd=c(0, 1-w, 0, 1))
    plot(1:3, rnorm(3), pch=1, lty=1, type="o", ylim=c(-2, 2))
    lines(1:3, rnorm(3), pch=2, lty=2, type="o")
    legend(par('usr')[2], par('usr')[4], bty='n', xpd=NA,
           c("group A", "group B"), pch=c(1, 2), lty=c(1, 2))
    

    【讨论】:

    • 使用 xpd=T 或 xpd=NA 不会阻止我的“主”(标题)在被拉伸以尝试使用添加有较宽右边距的区域时被剪裁。
    • @PhilGoetz 你确定你在情节区域内绘制 main 吗?有没有可能你没有足够的边距线来绘制?
    【解决方案5】:

    您可以使用Plotly R API、代码或从 GUI 中通过将图例拖动到所需位置来执行此操作。

    这是一个例子。图和代码也是here

    x = c(0,1,2,3,4,5,6,7,8) 
    y = c(0,3,6,4,5,2,3,5,4) 
    x2 = c(0,1,2,3,4,5,6,7,8) 
    y2 = c(0,4,7,8,3,6,3,3,4)
    

    您可以通过将 x 和 y 值之一指定为 100 或 -100 来将图例定位在图表之外。

    legendstyle = list("x"=100, "y"=1)
    layoutstyle = list(legend=legendstyle)
    

    以下是其他选项:

    • list("x" = 100, "y" = 0) 右下外侧
    • list("x" = 100, "y"= 1) 右上外
    • list("x" = 100, "y" = .5)外右中
    • list("x" = 0, "y" = -100)左下
    • list("x" = 0.5, "y" = -100)中下
    • list("x" = 1, "y" = -100)右下

    然后是响应。

    response = p$plotly(x,y,x2,y2, kwargs=list(layout=layoutstyle));

    当您拨打电话时,Plotly 会在您的图表中返回一个 URL。您可以通过调用browseURL(response$url) 更快地访问它,以便它会在您的浏览器中为您打开图表。

    url = response$url
    filename = response$filename
    

    这给了我们这个图表。您还可以从 GUI 中移动图例,然后图形将相应地缩放。全面披露:我是 Plotly 团队的一员。

    【讨论】:

      【解决方案6】:

      也许您需要的是par(xpd=TRUE) 以使事物能够在绘图区域之外绘制。因此,如果您使用bty='L' 进行主要情节,您将在右侧有一些空间用于图例。通常这会被剪裁到绘图区域,但请执行par(xpd=TRUE) 并稍作调整,您可以获得尽可能远的图例:

       set.seed(1) # just to get the same random numbers
       par(xpd=FALSE) # this is usually the default
      
       plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2), bty='L')
       # this legend gets clipped:
       legend(2.8,0,c("group A", "group B"), pch = c(1,2), lty = c(1,2))
      
       # so turn off clipping:
       par(xpd=TRUE)
       legend(2.8,-1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))
      

      【讨论】:

      • 请注意,您可以将 xpd 直接传递给图例,因此您无需担心事后重置 par。另请参阅 grconvertX 和 Y,了解以不依赖于您绘制的数据限制的方式指定图例位置的方法。
      • 由于这个问题和答案仍然很受欢迎,par(xpd=NA) 更强大(即绘制到更多区域)。
      • +1。我们应该提到,在图例之前有一个单独的par 调用是有意义的。在我的情节中,我在其他几个场合使用了par(new=T),只是想在同一个调用中添加xpd 参数,这会导致麻烦。
      • 即使在第二个示例中,图例也会从屏幕上略微剪掉。即使您尝试调整图表大小,图例框仍部分保留在屏幕之外。有没有办法让它留在屏幕内?
      【解决方案7】:

      没有人提到对legend 使用负的inset 值。这是一个示例,图例位于图的右侧,与顶部对齐(使用关键字"topright")。

      # Random data to plot:
      A <- data.frame(x=rnorm(100, 20, 2), y=rnorm(100, 20, 2))
      B <- data.frame(x=rnorm(100, 21, 1), y=rnorm(100, 21, 1))
      
      # Add extra space to right of plot area; change clipping to figure
      par(mar=c(5.1, 4.1, 4.1, 8.1), xpd=TRUE)
      
      # Plot both groups
      plot(y ~ x, A, ylim=range(c(A$y, B$y)), xlim=range(c(A$x, B$x)), pch=1,
                     main="Scatter plot of two groups")
      points(y ~ x, B, pch=3)
      
      # Add legend to top right, outside plot region
      legend("topright", inset=c(-0.2,0), legend=c("A","B"), pch=c(1,3), title="Group")
      

      inset=c(-0.2,0) 的第一个值可能需要根据图例的宽度进行调整。

      【讨论】:

      • @Henrik 不,如果没有 xpd=TRUE,它就无法工作。另请注意,最好将 xpd=TRUE 设置为 legend() 函数的参数。
      • 有时必须将 xpd 设置为 TRUE 才能使负插图起作用。但有时不是。使用barplot(... 中的命令args.legend=list(x="bottom", horiz=TRUE, inset=-0.2),它似乎不需要xpd=TRUE,但只需legend(x="bottom", horiz=TRUE, inset=-0.2),它似乎确实需要xpd=TRUE。有什么见解吗?我只是在传递我的论点时感到困惑?
      【解决方案8】:

      很抱歉复活了一个旧线程,但我今天遇到了同样的问题。我发现的最简单的方法如下:

      # Expand right side of clipping rect to make room for the legend
      par(xpd=T, mar=par()$mar+c(0,0,0,6))
      
      # Plot graph normally
      plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
      lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")
      
      # Plot legend where you want
      legend(3.2,1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))
      
      # Restore default clipping rect
      par(mar=c(5, 4, 4, 2) + 0.1)
      

      在这里找到:http://www.harding.edu/fmccown/R/

      【讨论】:

      • oldpar 更好
      • 这个解决方案更好,因为图例的空间是固定的,与图例字符串的长度无关
      【解决方案9】:

      我只能提供已经指出的布局解决方案的一个示例。

      layout(matrix(c(1,2), nrow = 1), widths = c(0.7, 0.3))
      par(mar = c(5, 4, 4, 2) + 0.1)
      plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
      lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")
      par(mar = c(5, 0, 4, 2) + 0.1)
      plot(1:3, rnorm(3), pch = 1, lty = 1, ylim=c(-2,2), type = "n", axes = FALSE, ann = FALSE)
      legend(1, 1, c("group A", "group B"), pch = c(1,2), lty = c(1,2))
      

      【讨论】:

        【解决方案10】:

        试试layout(),我过去曾用过它,只需在下面创建一个空图,适当缩放大约 1/4 左右,然后手动将图例部分放入其中。

        这里有一些关于legend() 的老问题,应该可以帮助您入门。

        【讨论】:

        • 正如问题中已经说过的,这也是我的想法。但如果有另一种方式,那将是理想的。不知何故,我想没有。
        猜你喜欢
        • 2012-07-10
        • 2023-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多