ggplot2:轴上的大括号?

发布于 2024-11-28 18:06:43 字数 1021 浏览 0 评论 0原文

回答最近的可视化问题中,我确实需要大括号来显示跨度在一个轴上,我不知道如何在 ggplot2 中做到这一点。这是情节:

exampleplot

我真的很喜欢 y 轴标签“第二个字母,而不是刻度线” -字母名称”有一个从 1 延伸到 10 的大括号(红色和蓝色第二个字母的垂直跨度)。但我不确定如何实现这一点。 x 轴可以从类似的处理中受益。

链接的 CrossValidated 问题中提供了代码(对于本示例来说不必要地复杂,因此我不会显示它)。相反,这里有一个最小的示例:

library(ggplot2)
x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)
qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") )

minimal example

在这种情况下,大括号来自 (0,1) 表示低类型,来自(2,3) 对于高类型来说是理想的选择,而不是刻度线。

我不想使用 geom_rect 因为:

  • 刻度线将保留
  • 我更喜欢大括号
  • 它将位于绘图内部而不是外部

我将如何实现这一点?完美的答案是:

  • 一个漂亮、光滑、细的大括号
  • 在绘图区域之外绘制
  • 通过高级参数指定(理想情况下是传递给scale_x_continuous中的breaks选项的范围类型对象) )

In answering a recent visualization question I really needed braces to show a span on an axis, and I can't figure out how to do it in ggplot2. Here's the plot:

example plot

Instead of a tick mark, I'd really like the y axis label "Second letter of two-letter names" to have a brace extending from 1 to 10 (the vertical span of the red and blue second letters). But I'm not sure how to make that happen. The x axis could benefit from similar treatment.

Code is available in the linked CrossValidated question (and unnecessarily complicated for this example, so I won't show it). Instead, here's a minimal example:

library(ggplot2)
x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)
qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") )

minimal example

In this case, a brace from (0,1) for low types and from (2,3) for the high types would be ideal instead of tick marks.

I'd rather not use geom_rect because:

  • The tick marks will remain
  • I'd prefer braces
  • It will be inside the plot instead of outside it

How would I accomplish this? The perfect answer would have:

  • A nice, smooth, thin curly brace
  • Drawn outside the plotting area
  • Specified via a high-level argument (ideally a range-type object passed to the breaks option in scale_x_continuous)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

三生一梦 2024-12-05 18:06:43

另一种解决方案使用绘制大括号的函数。

谢谢古尔!

curly <- function(N = 100, Tilt = 1, Long = 2, scale = 0.1, xcent = 0.5,
                  ycent = 0.5, theta = 0, col = 1, lwd = 1, grid = FALSE){

# N determines how many points in each curve
# Tilt is the ratio between the axis in the ellipse 
#  defining the curliness of each curve
# Long is the length of the straight line in the curly brackets 
#  in units of the projection of the curly brackets in this dimension
# 2*scale is the absolute size of the projection of the curly brackets 
#  in the y dimension (when theta=0)
# xcent is the location center of the x axis of the curly brackets
# ycent is the location center of the y axis of the curly brackets
# theta is the angle (in radians) of the curly brackets orientation
# col and lwd are passed to points/grid.lines

           ymin <- scale / Tilt
           y2 <- ymin * Long
           i <- seq(0, pi/2, length.out = N)

           x <- c(ymin * Tilt * (sin(i)-1),
                  seq(0,0, length.out = 2),
                  ymin * (Tilt * (1 - sin(rev(i)))),
                  ymin * (Tilt * (1 - sin(i))),
                  seq(0,0, length.out = 2),
                  ymin * Tilt * (sin(rev(i)) - 1))

           y <- c(-cos(i) * ymin,
                  c(0,y2),
                  y2 + (cos(rev(i))) * ymin,
                  y2 + (2 - cos(i)) * ymin,
                  c(y2 + 2 * ymin, 2 * y2 + 2 * ymin),
                  2 * y2 + 2 * ymin + cos(rev(i)) * ymin)

           x <- x + xcent
           y <- y + ycent - ymin - y2

           x1 <- cos(theta) * (x - xcent) - sin(theta) * (y - ycent) + xcent
           y1 <- cos(theta) * (y - ycent) + sin(theta) * (x - xcent) + ycent

           ##For grid library:
           if(grid){
              grid.lines(unit(x1,"npc"), unit(y1,"npc"),gp=gpar(col=col,lwd=lwd))
           }

           ##Uncomment for base graphics
           else{
              par(xpd=TRUE)
              points(x1,y1,type='l',col=col,lwd=lwd)
              par(xpd=FALSE)
           }

}


library(ggplot2)
x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)
qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") )

curly(N=100,Tilt=0.4,Long=0.3,scale=0.025,xcent=0.2525,
      ycent=par()$usr[3]+0.1,theta=-pi/2,col="red",lwd=2,grid=TRUE)
curly(N=100,Tilt=0.4,Long=0.3,scale=0.025,xcent=0.8,
      ycent=par()$usr[3]+0.1,theta=-pi/2,col="red",lwd=2,grid=TRUE)

结果图

Another solution using a function that draws a curly bracket.

Thanks Gur!

curly <- function(N = 100, Tilt = 1, Long = 2, scale = 0.1, xcent = 0.5,
                  ycent = 0.5, theta = 0, col = 1, lwd = 1, grid = FALSE){

# N determines how many points in each curve
# Tilt is the ratio between the axis in the ellipse 
#  defining the curliness of each curve
# Long is the length of the straight line in the curly brackets 
#  in units of the projection of the curly brackets in this dimension
# 2*scale is the absolute size of the projection of the curly brackets 
#  in the y dimension (when theta=0)
# xcent is the location center of the x axis of the curly brackets
# ycent is the location center of the y axis of the curly brackets
# theta is the angle (in radians) of the curly brackets orientation
# col and lwd are passed to points/grid.lines

           ymin <- scale / Tilt
           y2 <- ymin * Long
           i <- seq(0, pi/2, length.out = N)

           x <- c(ymin * Tilt * (sin(i)-1),
                  seq(0,0, length.out = 2),
                  ymin * (Tilt * (1 - sin(rev(i)))),
                  ymin * (Tilt * (1 - sin(i))),
                  seq(0,0, length.out = 2),
                  ymin * Tilt * (sin(rev(i)) - 1))

           y <- c(-cos(i) * ymin,
                  c(0,y2),
                  y2 + (cos(rev(i))) * ymin,
                  y2 + (2 - cos(i)) * ymin,
                  c(y2 + 2 * ymin, 2 * y2 + 2 * ymin),
                  2 * y2 + 2 * ymin + cos(rev(i)) * ymin)

           x <- x + xcent
           y <- y + ycent - ymin - y2

           x1 <- cos(theta) * (x - xcent) - sin(theta) * (y - ycent) + xcent
           y1 <- cos(theta) * (y - ycent) + sin(theta) * (x - xcent) + ycent

           ##For grid library:
           if(grid){
              grid.lines(unit(x1,"npc"), unit(y1,"npc"),gp=gpar(col=col,lwd=lwd))
           }

           ##Uncomment for base graphics
           else{
              par(xpd=TRUE)
              points(x1,y1,type='l',col=col,lwd=lwd)
              par(xpd=FALSE)
           }

}


library(ggplot2)
x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)
qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") )

curly(N=100,Tilt=0.4,Long=0.3,scale=0.025,xcent=0.2525,
      ycent=par()$usr[3]+0.1,theta=-pi/2,col="red",lwd=2,grid=TRUE)
curly(N=100,Tilt=0.4,Long=0.3,scale=0.025,xcent=0.8,
      ycent=par()$usr[3]+0.1,theta=-pi/2,col="red",lwd=2,grid=TRUE)

result plot

白昼 2024-12-05 18:06:43

更新:如果您需要保存绘图,请务必查看相关的 Stackoverflow 问答使用 ggsave() 并使括号保留在保存的图像中。


OP 要求支架不在图上。此解决方案结合使用 axis.ticks.length 和 axis.ticks = element_blank() 来允许大括号位于绘图区域之外。
这个答案建立在 @Pankil 和 @user697473 的基础上:我们将使用 pBrackets R 包 - 并包含图片!

library(ggplot2)
library(grid)
library(pBrackets) 
x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)
the_plot <- qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") ) +
  theme(axis.ticks = element_blank(),
        axis.ticks.length = unit(.85, "cm"))


#Run grid.locator a few times to get coordinates for the outer
#most points of the bracket, making sure the 
#bottom_y coordinate is just at the bottom of the gray area.
# to exit grid.locator hit esc; after setting coordinates
# in grid.bracket comment out grid.locator() line
the_plot
grid.locator(unit="native") 
bottom_y <- 284
grid.brackets(220, bottom_y,   80, bottom_y, lwd=2, col="red")
grid.brackets(600, bottom_y,  440, bottom_y, lwd=2, col="red")

输入图片描述这里

关于 @Pankil 答案的快速注释:

## Bracket coordinates depend on the size of the plot
## for instance,
## Pankil's suggested bracket coordinates do not work
## with the following sizing:
the_plot
grid.brackets(240, 440, 50, 440, lwd=2, col="red")
grid.brackets(570, 440, 381, 440, lwd=2, col="red")
## 440 seems to be off the graph...

在此处输入图像描述

还有更多内容来展示 pBrackets 的功能:

#note, if you reverse the x1 and x2, the bracket flips:
the_plot
grid.brackets( 80, bottom_y, 220, bottom_y, lwd=2, col="red")
grid.brackets(440, bottom_y, 600, bottom_y, lwd=2, col="red")

在此处输入图像描述 < /一>

## go vertical:
the_plot
grid.brackets(235, 200, 235, 300, lwd=2, col="red")
grid.brackets(445, 125, 445,  25, lwd=2, col="red")

<一href="https://i.sstatic.net/RBwRO.png" rel="noreferrer">在此处输入图像描述

Update: Be sure to see this related Stackoverflow Q&A if you need to save the plot with ggsave() and have the brackets persist in the saved image.


The OP requested the bracket be off the plot. This solution uses axis.ticks.length in combination with axis.ticks = element_blank() to allow the brace to be outside the plotting area.
This answer builds upon those of @Pankil and @user697473: we will use pBrackets R package -- and include pictures!

library(ggplot2)
library(grid)
library(pBrackets) 
x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)
the_plot <- qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") ) +
  theme(axis.ticks = element_blank(),
        axis.ticks.length = unit(.85, "cm"))


#Run grid.locator a few times to get coordinates for the outer
#most points of the bracket, making sure the 
#bottom_y coordinate is just at the bottom of the gray area.
# to exit grid.locator hit esc; after setting coordinates
# in grid.bracket comment out grid.locator() line
the_plot
grid.locator(unit="native") 
bottom_y <- 284
grid.brackets(220, bottom_y,   80, bottom_y, lwd=2, col="red")
grid.brackets(600, bottom_y,  440, bottom_y, lwd=2, col="red")

enter image description here

A quick note on @Pankil's answer:

## Bracket coordinates depend on the size of the plot
## for instance,
## Pankil's suggested bracket coordinates do not work
## with the following sizing:
the_plot
grid.brackets(240, 440, 50, 440, lwd=2, col="red")
grid.brackets(570, 440, 381, 440, lwd=2, col="red")
## 440 seems to be off the graph...

enter image description here

And a couple more to showcase functionality of pBrackets:

#note, if you reverse the x1 and x2, the bracket flips:
the_plot
grid.brackets( 80, bottom_y, 220, bottom_y, lwd=2, col="red")
grid.brackets(440, bottom_y, 600, bottom_y, lwd=2, col="red")

enter image description here

## go vertical:
the_plot
grid.brackets(235, 200, 235, 300, lwd=2, col="red")
grid.brackets(445, 125, 445,  25, lwd=2, col="red")

enter image description here

恏ㄋ傷疤忘ㄋ疼 2024-12-05 18:06:43

这是 ggplot 中的一个笨拙的解决方案,它构建了一个隐约类似于大括号的线条图。

构造一个函数,将大括号的位置和尺寸作为输入。该函数的作用是指定支架轮廓图的坐标,并使用一些数学缩放来使其达到所需的大小和位置。您可以利用这一原理并修改坐标以获得任何所需的形状。原则上,您可以使用相同的概念并添加曲线、椭圆等。

bracket <- function(x, width, y, height){
  data.frame(
      x=(c(0,1,4,5,6,9,10)/10-0.5)*(width) + x,
      y=c(0,1,1,2,1,1,0)/2*(height) + y
  )
}

将其传递给ggplot,特别是geom_line

qplot(x=x,y=y) +
    scale_x_continuous("",breaks=c(.5,2.5), labels=c("Low types","High types")) +
    geom_line(data=bracket(0.5,1,0,-0.2)) +
    geom_line(data=bracket(2.5,2,0,-0.2))

在此处输入图像描述

Here is kludgy solution in ggplot that constructs a line drawing that vaguely resembles a curly bracket.

Construct a function that takes as input the position and dimensions of a curly bracket. What this function does is to specify the co-ordinates of an outline drawing of a bracket and uses some math scaling to get it to the size and position desired. You can use this principle and modify the co-ordinates to give you any desired shape. In principle you can use the same concept and add curves, ellipses, etc.

bracket <- function(x, width, y, height){
  data.frame(
      x=(c(0,1,4,5,6,9,10)/10-0.5)*(width) + x,
      y=c(0,1,1,2,1,1,0)/2*(height) + y
  )
}

Pass that to ggplot and specifically geom_line

qplot(x=x,y=y) +
    scale_x_continuous("",breaks=c(.5,2.5), labels=c("Low types","High types")) +
    geom_line(data=bracket(0.5,1,0,-0.2)) +
    geom_line(data=bracket(2.5,2,0,-0.2))

enter image description here

浮云落日 2024-12-05 18:06:43

正如 @user697473 所建议的,pBrackets 是一个优雅的解决方案。

它与默认绘图命令配合使用效果最佳,但要使其与 GGplot2 配合使用,请使用 pBracket::grid.brackets 。我添加了代码,以便于尝试。

您的代码开始..

library(ggplot2)
x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)
qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") ) +
  theme(axis.ticks = element_blank())

最后一行删除您不需要的勾号。
现在,pBrackets

library(pBrackets)  # this will also load grid package
grid.locator(unit="native") 

现在使用光标识别图表上括号开始和结束的点。您将获得“本地”单位中的相应坐标。现在在下面的命令中输入它们

grid.brackets(240, 440, 50, 440, lwd=2, col="red")
grid.brackets(570, 440, 381, 440, lwd=2, col="red")

您可以在图表上的任何位置添加括号,甚至使用grid.text添加文本。

希望这有帮助!谢谢pBrackets

潘基尔!

as @user697473 suggested pBrackets is the elegant solution.

It works best with the default plotting commands but to make it work with GGplot2 use the pBracket::grid.brackets. I'm including the code to make it easy for try it out.

Starting with your code..

library(ggplot2)
x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)
qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") ) +
  theme(axis.ticks = element_blank())

the last line removes the ticks you didn't want.
Now the pBrackets

library(pBrackets)  # this will also load grid package
grid.locator(unit="native") 

now using your cursor identify the point on the graph where the brackets start and end. You will get the corresponding coordinates in the 'native' unit. now feed them in the command below

grid.brackets(240, 440, 50, 440, lwd=2, col="red")
grid.brackets(570, 440, 381, 440, lwd=2, col="red")

You can add the brackets anywhere on the graph or even add text using grid.text.

enter image description here

Hope this helps! Thank you pBrackets!

Pankil!

一个人的旅程 2024-12-05 18:06:43

#== 编辑: ggbrace 替换了 curlyBraces 包

这个答案确实很晚了,但是对于绘图区域内的大括号,我制作了一个小包 ggbrace 允许您使用带有指定坐标的大括号(易于使用):

基本上:

devtools::install_github("NicolasH2/ggbrace")
library(ggbrace)
library(ggplot2)

您可以自定义 x 和 y 坐标以及括号指向的位置。在这种特定情况下:

x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)

qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") ) + 
  geom_brace(aes(x=c(0,1), y=c(0,-.3)), inherit.data=F, rotate=180) + 
  geom_brace(aes(x=c(2,3), y=c(0,-.3)), inherit.data=F, rotate=180)

在此处输入图像描述

编辑:如果您希望大括号位于绘图区域之外,您可以设置一些 ggplot 参数。 ggbrace github 页面上有更详细的描述。

qplot(x=x,y=y) +
  geom_brace(aes(x=c(0,1), y=c(-.4,-.7), label = "Low types"), inherit.data=F, rotate=180, labelsize=5) + 
  geom_brace(aes(x=c(2,3), y=c(-.4,-.7), label = "High types"), inherit.data=F, rotate=180, labelsize=5) +
  coord_cartesian(y=range(y), clip = "off") +
  theme(plot.margin = unit(c(0.05, 0.05, 0.2, 0.05), units="npc"))

输入图片此处描述

#== EDIT: ggbrace replaced the curlyBraces package

This answer is really late, but for braces inside the plotting area I made a small package ggbrace that lets you have curly braces with the coordinates you specify (easy to use):

basically:

devtools::install_github("NicolasH2/ggbrace")
library(ggbrace)
library(ggplot2)

you can customize x and y coordinates and where the bracket points to. In this specific case:

x <- c(runif(10),runif(10)+2)
y <- c(runif(10),runif(10)+2)

qplot(x=x,y=y) +
  scale_x_continuous("",breaks=c(.5,2.5),labels=c("Low types","High types") ) + 
  geom_brace(aes(x=c(0,1), y=c(0,-.3)), inherit.data=F, rotate=180) + 
  geom_brace(aes(x=c(2,3), y=c(0,-.3)), inherit.data=F, rotate=180)

enter image description here

EDIT: If you want the braces outside of the plotting area, you can set some ggplot parameters. It is described in a little more detail on the ggbrace github page.

qplot(x=x,y=y) +
  geom_brace(aes(x=c(0,1), y=c(-.4,-.7), label = "Low types"), inherit.data=F, rotate=180, labelsize=5) + 
  geom_brace(aes(x=c(2,3), y=c(-.4,-.7), label = "High types"), inherit.data=F, rotate=180, labelsize=5) +
  coord_cartesian(y=range(y), clip = "off") +
  theme(plot.margin = unit(c(0.05, 0.05, 0.2, 0.05), units="npc"))

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文