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)
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")
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...
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")
## go vertical:
the_plot
grid.brackets(235, 200, 235, 300, lwd=2, col="red")
grid.brackets(445, 125, 445, 25, lwd=2, col="red")
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)
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))
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")
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
Output in R, Avoid Writing "[1]"
R: Extracting "Clean" Utf-8 Text from a Web Page Scraped with Rcurl
Why the Built-In Lm Function Is So Slow in R
How to Use R Plotly Library in R Script Visual of Power Bi
How to Use Cast or Another Function to Create a Binary Table in R
Paste All Combinations of a Vector in R
How to Add Elements to a List in R (Loop)
Left-Adjust Title in Ggplot2, or Absolute Position for Ggtitle
How to Use R to Download a Zipped File from a Ssl Page That Requires Cookies
How to Get the Number of Rows in a CSV File Without Opening It
In Ggplot2, Coord_Flip and Free Scales Don't Work Together
Ggplot2: Reorder Bars from Highest to Lowest in Each Facet
How to Convert Dd/Mm/Yy to Yyyy-Mm-Dd in R