ggplot2:坐标轴上的花括号?

27

回答最近的可视化问题中,我真的需要括号来显示坐标轴上的一个跨度,但我不知道如何在ggplot2中实现。这是图表:

example plot

我希望y轴标签 "Second letter of two-letter names" 能够有一个大括号,延伸从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选项的范围类型对象)

未经测试,但您的问题似乎很熟悉:https://dev59.com/TW025IYBdhLWcg3wKSiP#6179288 - joran
@joran:希望有一个针对ggplot2的特定解决方案。我猜,由于您(非常酷)的答案是基于网格而不是基于格子的,因此应该能够采用。但我仍然不知道如何关闭刻度线、在绘图区域外绘制图形等。 - Ari B. Friedman
任何解决方案几乎肯定都涉及到对网格进行操作。那段其他的代码不是我写的(我在网格方面并不擅长),我只是觉得它可能会成为你或其他人的一个很好的起点。 - joran
这确实很方便。除了之前提到的问题,我担心的是它仍然有些笨拙。ggplot2在指定数据与呈现方面非常美观,然后我必须使用几乎像铅笔一样的方式绘制,才能使用您提供的花哨曲线得到我想要的效果。从概念上讲,似乎正确的做法是将跨度对象作为列表中的一个元素传递给scale_x_continuous()中的断点。 - Ari B. Friedman
6个回答

20

使用一个绘制花括号的函数来解决问题。

感谢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)

结果图


1
对于像我这样的非数学专业人士 - 如果您在内部使用 theta <- angle*pi/180 将角度转换为弧度,那么您可以将 angle 用作函数参数 - 我觉得这更容易。 - tjebo

17
更新:如果您需要使用ggsave()保存绘图并使括号保留在保存的图像中,请确保查看这个相关的Stackoverflow Q&A
提问者要求将括号从图中移除。这个解决方案使用了axis.ticks.lengthaxis.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")

输入图像描述


但是如何保存呢?ggsave 不会添加括号。 - SamanthaDS
不错的答案。但是如果能解释一下如何将图表导出为PDF格式会更好。我已经通过RStudio的导出按钮将其保存为PNG格式,但是使用通常的解决方案(如“ggsave(...)”、“pdf(...)...dev.off()”)失败了。也许“grid”包中的某个函数(如“gridArrange()”)是为此设计的,但我没有很快找到解决方案。 - PatrickT
哦等等:https://dev59.com/3lsV5IYBdhLWcg3w6yYh - PatrickT
PatrickT -- 我已经将你链接到答案的SamanthaDS帖子添加了进来。谢谢你们两位。 - swihart
grid.brackets() 似乎没有从 x1y1x2y2 参数所指定的位置开始和结束!(它们看起来很好,只是无法将它们放置在应该放置的位置) - stevec

12

这里有一个在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))

这里输入图片描述


1
不错。你可以结合这里的答案,将大括号靠近轴线:https://dev59.com/O1rUa4cB1Zd3GeqPghhY#7001873 - Ari B. Friedman

9

正如@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 添加文本。

enter image description here

希望这能帮到你!感谢 pBrackets

Pankil!


4

不错!它导入了“grid”,所以应该可以与ggplot2一起使用。 - Ari B. Friedman
一个好的项目,但使用起来并不是超级容易。请查看后续内容:https://dev59.com/3lsV5IYBdhLWcg3w6yYh - PatrickT

2

#== 编辑: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"))

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接