How to Add Braces to a Graph

How to add braces to a graph?

A little Googling turn up some grid code from a thread on the R help mailing list here. At the very least it gives you something to work with. Here's the code from that post:

library(grid)

# function to draw curly braces in red
# x1...y2 are the ends of the brace
# for upside down braces, x1 > x2 and y1 > y2
Brack <- function(x1,y1,x2,y2,h)
{
x2 <- x2-x1; y2 <- y2-y1
v1 <- viewport(x=x1,y=y1,width=sqrt(x2^2+y2^2),
height=h,angle=180*atan2(y2,x2)/pi,
just=c("left","bottom"),gp=gpar(col="red"))
pushViewport(v1)
grid.curve(x2=0,y2=0,x1=.125,y1=.5,curvature=.5)
grid.move.to(.125,.5)
grid.line.to(.375,.5)
grid.curve(x1=.375,y1=.5,x2=.5,y2=1,curvature=.5)
grid.curve(x2=1,y2=0,x1=.875,y1=.5,curvature=-.5)
grid.move.to(.875,.5)
grid.line.to(.625,.5)
grid.curve(x2=.625,y2=.5,x1=.5,y1=1,curvature=.5)
popViewport()}

Add curly brackets for axis area in a plot

All credits to this answer, I only did some messy adjustments. Adding xpd=NA allows you to draw outside of the plot:

# function to draw curly braces
# x, y position where to put the braces
# range is the length of the brace
# position: 1 vertical, 2 horizontal
# direction: 1 left/down, 2 right/up
# depth controls width of the shape

CurlyBraces <- function(x0, x1, y0, y1, pos = 1, direction = 1, depth = 1) {

a=c(1,2,3,48,50) # set flexion point for spline
b=c(0,.2,.28,.7,.8) # set depth for spline flexion point

curve = spline(a, b, n = 50, method = "natural")$y * depth

curve = c(curve,rev(curve))

if (pos == 1){
a_sequence = seq(x0,x1,length=100)
b_sequence = seq(y0,y1,length=100)
}
if (pos == 2){
b_sequence = seq(x0,x1,length=100)
a_sequence = seq(y0,y1,length=100)
}

# direction
if(direction==1)
a_sequence = a_sequence+curve
if(direction==2)
a_sequence = a_sequence-curve

# pos
if(pos==1)
lines(a_sequence,b_sequence, lwd=1.5, xpd=NA) # vertical
if(pos==2)
lines(b_sequence,a_sequence, lwd=1.5, xpd=NA) # horizontal

}

windows(width=5, height=5)
par(oma=c(2,0,0,2))
plot(c(),c(), xlim=c(0,11), ylim=c(0,11), xlab='')
# horizontal brace
CurlyBraces(x0=2, x1=8, y0=-3, y1=-3, pos = 2, direction = 2, depth=1.5)
# vertical brace
CurlyBraces(x0=12, x1=12, y0=2, y1=6, pos = 1, direction = 1, depth=0.5)

Sample Image

ggplot2: Curly braces on an axis?

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")

Sample Image

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...

Sample Image

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")

Sample Image

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

Sample Image

Add curly braces to ggplot2 and then use ggsave

I don't understand the logic used in grid.brackets but it would help if there was a bracketsGrob function that would simply return a grob without drawing it. Perhaps contact the maintainer with a feature request?

Anyway, assuming such a function was available, it can be fed to annotation_custom making it compatible with ggsave.

bracketsGrob <- function(...){
l <- list(...)
e <- new.env()
e$l <- l
grid:::recordGrob( {
do.call(grid.brackets, l)
}, e)
}

# note that units here are "npc", the only unit (besides physical units) that makes sense
# when annotating the plot panel in ggplot2 (since we have no access to
# native units)

b1 <- bracketsGrob(0.33, 0.05, 0, 0.05, h=0.05, lwd=2, col="red")
b2 <- bracketsGrob(1, 0.05, 0.66, 0.05, h=0.05, lwd=2, col="red")

p <- the_plot +
annotation_custom(b1)+
annotation_custom(b2) +
scale_y_continuous(expand=c(0.11,0))
p

ggsave("test.png", p, width = 4, height = 2.5)

Sample Image

Drawing brackets over plot

One way to get what you want (or something very close to it) is to use matplotlib's annotate function which allows you to customize the arrow function quite extensively (see the official tutorial). Here is a sample with some made up plot data to show how to use it according to your label requirements:

import matplotlib.pyplot as plt
import numpy as np

# Make some fake data and a sample plot
x = np.arange(1,100)
y = x**2 * 1.0e6
ax = plt.axes()
ax.plot(x,y)

# Adjust the fontsizes on tick labels for this plot
fs = 14.0
[t.set_fontsize(fs) for t in ax.xaxis.get_majorticklabels()]
[t.set_fontsize(fs) for t in ax.yaxis.get_majorticklabels()]
ax.yaxis.get_offset_text().set_fontsize(fs)

# Here is the label and arrow code of interest
ax.annotate('SDL', xy=(0.5, 0.90), xytext=(0.5, 1.00), xycoords='axes fraction',
fontsize=fs*1.5, ha='center', va='bottom',
bbox=dict(boxstyle='square', fc='white'),
arrowprops=dict(arrowstyle='-[, widthB=7.0, lengthB=1.5', lw=2.0))

Sample Image

The annotate function contains the text label you desire as well as arguments for positioning for the arrow (xy) and the text itself (xycoords) and some general positioning and fontsize commands.

What is probably most of interest are the bbox and arrowprops arguments. The bbox argument draws a square around the label with a white background. The arrowprops are more involved--the arrowstyle key sets the head of the arrow (a bracket in this case) as well as the width and length of the head. Note that this is all one string under arrowstyle. Finally, the linewidth of the arrow is increased slightly.

You might have to adjust xy, xytext, widthB, lengthB, and the arrow's lw to get everything as you want it.

One thing that's not immediately clear (or at least wasn't to me when I figured this out) was that when annotate contains an arrowstyle argument, matplotlib uses the FancyArrowPatch properties, but when arrowstyle is missing, it uses the YAArrow properties. There is obviously more detail (as well as this distinction) noted in the documentation for annotate.

How to use double curly brackets in R with ggplot and dates?

The double curly braces work with unquoted variables names. With variables names as character strings, use aes_string, see also here.

suppressPackageStartupMessages({
library(dplyr)
library(ggplot2)
library(lubridate)
})

#Sample data
test <- tibble(Week = seq(as.Date("2014/09/04"), by = "week", length.out = 8),
Month = ymd(rep('2014-09-01', 4), rep('2014-10-01', 4)),
Week_score = c(2, 3, 4, 6, 5, 7, 8, 9),
Month_score = c(15, NA, NA, NA, 29, NA, NA, NA))

#My function
make_time_plot <- function(data, time_period = c("Week", "Month")) {

time_period <- match.arg(time_period)
time_period_score <- paste0(time_period, "_score")

data %>%
ggplot(aes_string(x = time_period, y = time_period_score)) +
geom_line()
}

#make_time_plot(data = test, time_period = "Month")
make_time_plot(data = test, time_period = "Week")

Sample Image

Created on 2022-04-02 by the reprex package (v2.0.1)

Extending ggplot2 properly?

ggplot2 is gradually becoming more and more extensible. The development version, https://github.com/hadley/ggplot2/tree/develop, uses roxygen2 (instead of two separate homegrown systems), and has begun the switch from proto to simpler S3 classes (currently complete for coords and scales). These two changes should hopefully make the source code easier to understand, and hence easier for others to extend (backup by the fact that pull request for ggplot2 are increasing).

Another big improvement that will be included in the next version is Kohske Takahashi's improvements to the guide system (https://github.com/kohske/ggplot2/tree/feature/new-guides-with-gtable). As well as improving the default guides (e.g. with elegant continuous colour bars), his changes also make it easier to override the defaults with your own custom legends and axes. This would make it possible to draw the curly braces in the axes, where they probably belong.

The next big round of changes (which I probably won't be able to tackle until summer 2012) will include a rewrite of geoms, stats and position adjustments, along the lines of the sketch in the layers package (https://github.com/hadley/layers). This should make geoms, stats and position adjustments much easier to write, and will hopefully foster more community contributions, such as a geom_tufteboxplot.



Related Topics



Leave a reply



Submit