Display only a given angular range in a circular histogram - r

The following code using R/ggplot
set.seed(123)
require(ggplot2)
n <- 60
df <- data.frame(theta=sample(180,n,replace=TRUE),
gp=sample(c("A","B"),n,replace=TRUE ))
p <- ggplot(df)
p <- p + geom_histogram(aes(x=theta,fill=gp),
binwidth=5)
p <- p + scale_x_continuous(breaks=seq(0,360,30),
limits=c(0,360))
p <- p + coord_polar(theta="x", start=3*pi/2, direction=-1)
p <- p + theme_bw()
print(p)
generates the figure below
I just want to display the angular range [0,180] and exclude entirely the range (180,360), so the figure would basically be the upper semi-circle rather than a full circle.
Changing the limits in scale_x_continuous does not do this.
Is there a way?
Thanks.
EDIT
There's a similar problem but with a different package here
Creating half a polar plot (rose diagram) with circular package

It is some kind of a hack, but based on this answer here and adding some code to your ggplot call as well as to the grid, I was able to come close to a solution. Please give it a try. Depending on your desired output format / resolution you might need to adjust the x, y, height and width arguments in the last line which basically recreates the black border around the plot which I deleted from the bw theme. Maybe someone with more profound knowledge of grobs can come up with something better.
library(ggplot2)
library(reshape2)
library(grid)
set.seed(123)
require(ggplot2)
n <- 60
df <- data.frame(theta=sample(180,n,replace=TRUE),
gp=sample(c("A","B"),n,replace=TRUE ))
p <- ggplot(df) + geom_histogram(aes(x = theta, fill = gp),
binwidth = 5) +
scale_x_continuous(
expand = c(0, 0),
breaks = seq(180, 0, -30),
limits = c(0, 360)
) +
coord_polar(theta = "x",
start = 3 * pi / 2,
direction = -1) +
theme_bw() +
theme(
panel.border = element_blank(),
axis.title.y = element_text(hjust = 0.75, vjust = 3),
legend.position = "top"
)
g = ggplotGrob(p)
grid.newpage()
pushViewport(viewport(height = 1, width = 1, clip="on"))
grid.draw(g)
grid.rect(x = 0, y = -0.05, height = 1, width = 2, gp = gpar(col="white"))
grid.rect(x = .5, y = .7, width = .6, height = .55, gp = gpar(lwd = 1, col = "black", fill = NA))

Related

Aligning and scaling ggplots with patchwork

I'm trying to make a combine some plots into a single plot using the patchwork package, but I'm unfamiliar with it and am having trouble figuring out to how to properly scale the size of the plots. There's one core plot, the scatter plot, and then a boxplot for each continuous variable to some the distributions as well. Here's a reproducible example below:
library(dplyr)
library(ggplot2)
library(patchwork)
set.seed(100)
dat1 <- tibble(x = runif(1000, 0, 10),
y = runif(1000, 0, 20),
group1 = sample(rep(letters[1:5], each = 200)),
group2 = sample(rep(letters[-22:-1], each = 250)))
plot1 <- ggplot(data = dat1) +
geom_point(aes(x = x, y = y, color = group1)) +
facet_wrap(~group2) +
theme(legend.position = 'bottom')
xbox <- ggplot(data = dat1) +
geom_boxplot(aes(x = x, y = group1, fill = group1, color = group1)) +
scale_x_continuous(position = 'top') +
theme(legend.position = 'none',
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
title = element_blank())
ybox <- ggplot(data = dat1) +
geom_boxplot(aes(x = group1, y = y, fill = group1, color = group1)) +
scale_y_continuous(position = 'right') +
theme(legend.position = 'none',
axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
title = element_blank())
(xbox + plot_spacer()) / (plot1 + ybox)
This is close to what I'm attempting, but the boxplots need to be rescaled such that the top one is approximately 20% of its shown height and the righthand one is approximately 20% of its shown width. I've tried using patchwork::plot_layout to handle the scaling, but I keep getting unexpected results. I inserted the patchwork::plot_spacer() so that the righthand boxplot wouldn't stretch the whole height of picture but maybe there is a better way to do that. The boxplots are supposed to appear more like axes, so that upper right whitespace shouldn't really be there.
I'm not certain you'll get a plot that makes inferential sense in the margins, but you can change the widths and heights of the marginal plots in plot_layout using either the heights and widths arguments or the design argument. Note that plots in parenthesis are considered "subplots" themselves and is given their own "area" when using plot_layout. As such I've added all plots together rather than arranging them using other patchwork operators.
xbox + plot_spacer() + plot1 + ybox + plot_layout(design = c(area(t = 1, b = 1, l = 1, r = 6), # <== top column
area(t = 1, b = 1, l = 7, r = 7), # <== top right
area(t = 2, b = 7, l = 1, r = 6), # <== bottom left
area(t = 2, b = 7, l = 7, r = 7) # <== bottom right
))
# Alternative:
xbox + plot_spacer() + plot1 + ybox + plot_layout(ncol = 2, nrow = 2,
heights = c(1, 6),
widths = c(6, 1))

Several colors for the same tick/label

My data :
dat <- data.frame(x = c(1,2,3,4,5,6), y = c(2,3,4,6,2,3))
Breaks and labels of my plot :
breaks <- c(3,5)
labels <- c(paste(3,"(0.3)"), paste(5,"(0.5)"))
And my plot :
library(ggplot2)
ggplot() +
geom_point(data = dat, aes(x = x, y = y)) +
scale_y_continuous(breaks = breaks, labels = labels)
I wish to colour the same labels differently. For instance, I wish to colour the "3" with a different colour than the one of "(0.3)".
Here's a way to stick 2 plots together with patchwork, which is a package similar to cowplot but with a little more flexibility. I split the labels into 2 vectors, one with the integers and one with the decimals in parentheses. Then make 2 plots, one for the outer labels with no other markings, and one for the main plot.
After doing one round of trying to build this, I started adjusting the margins in each theme, realizing I needed to set the top and bottom margins the same, but making no margin on the right side of the left plot and the left side of the right plot, so there's very little space between them. There's definitely still ways to tweak this, but I'd start with some of the spacing.
library(tidyverse)
library(patchwork)
lbl_int <- str_extract(labels, "^\\d+")
lbl_frac <- str_extract(labels, "\\(.+\\)")
The main plot is fairly straightforward, just removing elements from the left side in the theme.
(main_plot <- ggplot(dat, aes(x = x, y = y)) +
geom_point() +
scale_y_continuous(breaks = breaks, labels = lbl_frac) +
theme(axis.text.y.left = element_text(color = "gray"),
axis.title.y.left = element_blank(),
plot.margin = margin(1, 1, 1, 0, "mm")))
The plot for the outer labels has most theme elements removed, but has the y-axis title and labels.
(int_plot <- ggplot(dat, aes(x = 0, y = y)) +
scale_y_continuous(breaks = breaks, labels = lbl_int) +
theme(axis.text.y.left = element_text(color = "black"),
axis.title.y.left = element_text(color = "black"),
axis.title.x = element_blank(),
panel.grid = element_blank(),
axis.ticks = element_blank(),
axis.text.x = element_blank(),
panel.background = element_blank(),
plot.margin = margin(1, 0, 1, 1, "mm")))
Then patchwork makes it easy to just add plots together—literally with +—and then set the widths. Again, here's something you can adjust as you need, but I made the left plot very very skinny compared to the right one.
int_plot + main_plot +
plot_layout(ncol = 2, widths = c(1e-3, 1))
Created on 2018-12-21 by the reprex package (v0.2.1)
This is something to get you going (credit to this answer which I adapted).
We use annotate to plot your labels, on two different x-axis coords, this will function as our labels (so we need to shut off the actual labelling in the theme).
First we create two vectors of the exact labels that we want in different colors.
dat <- data.frame(x = c(1,2,3,4,5,6), y = c(2,3,4,6,2,3))
breaks <- c(3,5)
labels_new1 <- c(NA, NA, 3, NA, 5, NA) # NA in order to skip that annotation
labels_new2 <- c(NA, NA, "(0.3)", NA, "(0.5)", NA)
Important parts:
coord_cartesian(xlim = c(0, 6), expand = FALSE) + # this will cut our plot properly
plot.margin = unit(c(1, 1, 1, 5), "lines") # this will give us some space on the left
Note that in coord_cartesian defined like that we are actually cutting off the two annotations (notice that the two x values you see in the next part (-1, -0.5) are outside the xlim range).
Plot object:
g1 <- ggplot() +
geom_point(data = dat, aes(x = x, y = y)) +
annotate(geom = "text", y = seq_len(nrow(dat)), x = -1, label = labels_new1, size = 4) +
#first the number add color = "blue" for example
annotate(geom = "text", y = seq_len(nrow(dat)), x = -0.5, label = labels_new2, size = 4, color = "red") +
#second the parenthesis (colored in red)
coord_cartesian(xlim = c(0, 6), expand = FALSE) +
scale_y_continuous(breaks = breaks) +
#now lets shut off the labels and give us some left space in the plot
theme(plot.margin = unit(c(1, 1, 1, 5), "lines"),
axis.title.y = element_blank(),
axis.text.y = element_blank())
Finally:
g2 <- ggplot_gtable(ggplot_build(g1)) # convert to grob
g2$layout$clip[g2$layout$name == "panel"] <- "off" # clipping of the axes
# this will show the two annotations that we left off before
grid::grid.draw(g2)
Remarks:
You can play around with x=-1 and x=-0.5 to move the two annotations, and with the last value in c(1, 1, 1, 5) to give you more space on the left side.
labels_new1 and labels_new2 are very important, they are doing all the heavy work of what and where you want to show something.

R: add calibrated axes to PCA biplot in ggplot2

I am working on an ordination package using ggplot2. Right now I am constructing biplots in the traditional way, with loadings being represented with arrows. I would also be interested though to use calibrated axes and represent the loading axes as lines through the origin, and with loading labels being shown outside the plot region. In base R this is implemented in
library(OpenRepGrid)
biplot2d(boeker)
but I am looking for a ggplot2 solution. Would anybody have any thoughts how to achieve something like this in ggplot2? Adding the variable names outside the plot region could be done like here I suppose, but how could the line segments outside the plot region be plotted?
Currently what I have is
install.packages("devtools")
library(devtools)
install_github("fawda123/ggord")
library(ggord)
data(iris)
ord <- prcomp(iris[,1:4],scale=TRUE)
ggord(ord, iris$Species)
The loadings are in ord$rotation
PC1 PC2 PC3 PC4
Sepal.Length 0.5210659 -0.37741762 0.7195664 0.2612863
Sepal.Width -0.2693474 -0.92329566 -0.2443818 -0.1235096
Petal.Length 0.5804131 -0.02449161 -0.1421264 -0.8014492
Petal.Width 0.5648565 -0.06694199 -0.6342727 0.5235971
How could I add the lines through the origin, the outside ticks and the labels outside the axis region (plossibly including the cool jittering that is applied above for overlapping labels)?
NB I do not want to turn off clipping, since some of my plot elements could sometimes go outside the bounding box
EDIT: Someone else apparently asked a similar question before, though the question is still without an answer. It points out that to do something like this in base R (though in an ugly way) one can do e.g.
plot(-1:1, -1:1, asp = 1, type = "n", xaxt = "n", yaxt = "n", xlab = "", ylab = "")
abline(a = 0, b = -0.75)
abline(a = 0, b = 0.25)
abline(a = 0, b = 2)
mtext("V1", side = 4, at = -0.75*par("usr")[2])
mtext("V2", side = 2, at = 0.25*par("usr")[1])
mtext("V3", side = 3, at = par("usr")[4]/2)
Minimal workable example in ggplot2 would be
library(ggplot2)
df <- data.frame(x = -1:1, y = -1:1)
dfLabs <- data.frame(x = c(1, -1, 1/2), y = c(-0.75, -0.25, 1), labels = paste0("V", 1:3))
p <- ggplot(data = df, aes(x = x, y = y)) + geom_blank() +
geom_abline(intercept = rep(0, 3), slope = c(-0.75, 0.25, 2)) +
theme_bw() + coord_cartesian(xlim = c(-1, 1), ylim = c(-1, 1)) +
theme(axis.title = element_blank(), axis.text = element_blank(), axis.ticks = element_blank(),
panel.grid = element_blank())
p + geom_text(data = dfLabs, mapping = aes(label = labels))
but as you can see no luck with the labels, and I am looking for a solution that does not require one to turn off clipping.
EDIT2: bit of a related question is how I could add custom breaks/tick marks and labels, say in red, at the top of the X axis and right of the Y axis, to show the coordinate system of the factor loadings? (in case I would scale it relative to the factor scores to make the arrows clearer, typically combined with a unit circle)
Maybe as an alternative, you could remove the default panel box and axes altogether, and draw a smaller rectangle in the plot region instead. Clipping the lines not to clash with the text labels is a bit tricky, but this might work.
df <- data.frame(x = -1:1, y = -1:1)
dfLabs <- data.frame(x = c(1, -1, 1/2), y = c(-0.75, -0.25, 1),
labels = paste0("V", 1:3))
p <- ggplot(data = df, aes(x = x, y = y)) +
geom_blank() +
geom_blank(data=dfLabs, aes(x = x, y = y)) +
geom_text(data = dfLabs, mapping = aes(label = labels)) +
geom_abline(intercept = rep(0, 3), slope = c(-0.75, 0.25, 2)) +
theme_grey() +
theme(axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
panel.grid = element_blank()) +
theme()
library(grid)
element_grob.element_custom <- function(element, ...) {
rectGrob(0.5,0.5, 0.8, 0.8, gp=gpar(fill="grey95"))
}
panel_custom <- function(...){ # dummy wrapper
structure(
list(...),
class = c("element_custom","element_blank", "element")
)
}
p <- p + theme(panel.background=panel_custom())
clip_layer <- function(g, layer="segment", width=1, height=1){
id <- grep(layer, names(g$grobs[[4]][["children"]]))
newvp <- viewport(width=unit(width, "npc"),
height=unit(height, "npc"), clip=TRUE)
g$grobs[[4]][["children"]][[id]][["vp"]] <- newvp
g
}
g <- ggplotGrob(p)
g <- clip_layer(g, "segment", 0.85, 0.85)
grid.newpage()
grid.draw(g)
What about this:
use the following code.
If you want the labels also on top and on the right have a look at:
http://rpubs.com/kohske/dual_axis_in_ggplot2
require(ggplot2)
data(iris)
ord <- prcomp(iris[,1:4],scale=TRUE)
slope <- ord$rotation[,2]/ord$rotation[,1]
p <- ggplot() +
geom_point(data = as.data.frame(ord$x), aes(x = PC1, y = PC2)) +
geom_abline(data = as.data.frame(slope), aes(slope=slope))
info <- ggplot_build(p)
x <- info$panel$ranges[[1]]$x.range[1]
y <- info$panel$ranges[[1]]$y.range[1]
p +
scale_x_continuous(breaks=y/slope, labels=names(slope)) +
scale_y_continuous(breaks=x*slope, labels=names(slope)) +
theme(axis.text.x = element_text(angle=90, vjust=0.5),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank())

How can I add a methodological note to a plot ? [duplicate]

I am trying to display some information about the data below the plot created in ggplot2. I would like to plot the N variable using the X axis coordinate of the plot but the Y coordinate needs to be 10% from the bottom of the screen . In fact, the desired Y coordinates are already in the data frame as y_pos variable.
I can think of 3 approaches using ggplot2:
1) Create an empty plot below the actual plot, use the same scale and then use geom_text to plot the data over the blank plot. This approach sort of works but is extremely complicated.
2) Use geom_text to plot the data but somehow use y coordinate as percent of the screen (10%). This would force the numbers to be displayed below the plot. I can't figure out the proper syntax.
3) Use grid.text to display the text. I can easily set it at the 10% from the bottom of the screen but I can't figure how set the X coordindate to match the plot. I tried to use grconvert to capture the initial X position but could not get that to work as well.
Below is the basic plot with the dummy data:
graphics.off() # close graphics windows
library(car)
library(ggplot2) #load ggplot
library(gridExtra) #load Grid
library(RGraphics) # support of the "R graphics" book, on CRAN
#create dummy data
test= data.frame(
Group = c("A", "B", "A","B", "A", "B"),
x = c(1 ,1,2,2,3,3 ),
y = c(33,25,27,36,43,25),
n=c(71,55,65,58,65,58),
y_pos=c(9,6,9,6,9,6)
)
#create ggplot
p1 <- qplot(x, y, data=test, colour=Group) +
ylab("Mean change from baseline") +
geom_line()+
scale_x_continuous("Weeks", breaks=seq(-1,3, by = 1) ) +
opts(
legend.position=c(.1,0.9))
#display plot
p1
The modified gplot below displays numbers of subjects, however they are displayed WITHIN the plot. They force the Y scale to be extended. I would like to display these numbers BELOW the plot.
p1 <- qplot(x, y, data=test, colour=Group) +
ylab("Mean change from baseline") +
geom_line()+
scale_x_continuous("Weeks", breaks=seq(-1,3, by = 1) ) +
opts( plot.margin = unit(c(0,2,2,1), "lines"),
legend.position=c(.1,0.9))+
geom_text(data = test,aes(x=x,y=y_pos,label=n))
p1
A different approach of displaying the numbers involves creating a dummy plot below the actual plot. Here is the code:
graphics.off() # close graphics windows
library(car)
library(ggplot2) #load ggplot
library(gridExtra) #load Grid
library(RGraphics) # support of the "R graphics" book, on CRAN
#create dummy data
test= data.frame(
group = c("A", "B", "A","B", "A", "B"),
x = c(1 ,1,2,2,3,3 ),
y = c(33,25,27,36,43,25),
n=c(71,55,65,58,65,58),
y_pos=c(15,6,15,6,15,6)
)
p1 <- qplot(x, y, data=test, colour=group) +
ylab("Mean change from baseline") +
opts(plot.margin = unit(c(1,2,-1,1), "lines")) +
geom_line()+
scale_x_continuous("Weeks", breaks=seq(-1,3, by = 1) ) +
opts(legend.position="bottom",
legend.title=theme_blank(),
title.text="Line plot using GGPLOT")
p1
p2 <- qplot(x, y, data=test, geom="blank")+
ylab(" ")+
opts( plot.margin = unit(c(0,2,-2,1), "lines"),
axis.line = theme_blank(),
axis.ticks = theme_segment(colour = "white"),
axis.text.x=theme_text(angle=-90,colour="white"),
axis.text.y=theme_text(angle=-90,colour="white"),
panel.background = theme_rect(fill = "transparent",colour = NA),
panel.grid.minor = theme_blank(),
panel.grid.major = theme_blank()
)+
geom_text(data = test,aes(x=x,y=y_pos,label=n))
p2
grid.arrange(p1, p2, heights = c(8.5, 1.5), nrow=2 )
However, that is very complicated and would be hard to modify for different data. Ideally, I'd like to be able to pass Y coordinates as percent of the screen.
The current version (>2.1) has a + labs(caption = "text"), which displays an annotation below the plot. This is themeable (font properties,... left/right aligned). See https://github.com/hadley/ggplot2/pull/1582 for examples.
Edited opts has been deprecated, replaced by theme; element_blank has replaced theme_blank; and ggtitle() is used in place of opts(title = ...
Sandy- thank you so much!!!! This does exactly what I want. I do wish we could control the clipping in geom.text or geom.annotate.
I put together the following program if anybody else is interested.
rm(list = ls()) # clear objects
graphics.off() # close graphics windows
library(ggplot2)
library(gridExtra)
#create dummy data
test= data.frame(
group = c("Group 1", "Group 1", "Group 1","Group 2", "Group 2", "Group 2"),
x = c(1 ,2,3,1,2,3 ),
y = c(33,25,27,36,23,25),
n=c(71,55,65,58,65,58),
ypos=c(18,18,18,17,17,17)
)
p1 <- qplot(x=x, y=y, data=test, colour=group) +
ylab("Mean change from baseline") +
theme(plot.margin = unit(c(1,3,8,1), "lines")) +
geom_line()+
scale_x_continuous("Visits", breaks=seq(-1,3) ) +
theme(legend.position="bottom",
legend.title=element_blank())+
ggtitle("Line plot")
# Create the textGrobs
for (ii in 1:nrow(test))
{
#display numbers at each visit
p1=p1+ annotation_custom(grob = textGrob(test$n[ii]),
xmin = test$x[ii],
xmax = test$x[ii],
ymin = test$ypos[ii],
ymax = test$ypos[ii])
#display group text
if (ii %in% c(1,4)) #there is probably a better way
{
p1=p1+ annotation_custom(grob = textGrob(test$group[ii]),
xmin = 0.85,
xmax = 0.85,
ymin = test$ypos[ii],
ymax = test$ypos[ii])
}
}
# Code to override clipping
gt <- ggplot_gtable(ggplot_build(p1))
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)
Updated opts() has been replaced with theme()
In the code below, a base plot is drawn, with a wider margin at the bottom of the plot. The textGrob is created, then inserted into the plot using annotation_custom(). Except the text is not visible because it is outside the plot panel - the output is clipped to the panel. But using baptiste's code from here, the clipping can be overrridden. The position is in terms of data units, and both text labels are centred.
library(ggplot2)
library(grid)
# Base plot
df = data.frame(x=seq(1:10), y = seq(1:10))
p = ggplot(data = df, aes(x = x, y = y)) + geom_point() + ylim(0,10) +
theme(plot.margin = unit(c(1,1,3,1), "cm"))
p
# Create the textGrobs
Text1 = textGrob(paste("Largest x-value is", round(max(df$x), 2), sep = " "))
Text2 = textGrob(paste("Mean = ", mean(df$x), sep = ""))
p1 = p + annotation_custom(grob = Text1, xmin = 4, xmax = 4, ymin = -3, ymax = -3) +
annotation_custom(grob = Text2, xmin = 8, xmax = 8, ymin = -3, ymax = -3)
p1
# Code to override clipping
gt <- ggplotGrob(p1)
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)
Or, using grid functions to create and position the label.
p
grid.text((paste("Largest x-value is", max(df$x), sep = " ")),
x = unit(.2, "npc"), y = unit(.1, "npc"), just = c("left", "bottom"),
gp = gpar(fontface = "bold", fontsize = 18, col = "blue"))
Edit
Or, add text grob using gtable functions.
library(ggplot2)
library(grid)
library(gtable)
# Base plot
df = data.frame(x=seq(1:10), y = seq(1:10))
p = ggplot(data = df, aes(x = x, y = y)) + geom_point() + ylim(0,10)
# Construct the text grob
lab = textGrob((paste("Largest x-value is", max(df$x), sep = " ")),
x = unit(.1, "npc"), just = c("left"),
gp = gpar(fontface = "bold", fontsize = 18, col = "blue"))
gp = ggplotGrob(p)
# Add a row below the 2nd from the bottom
gp = gtable_add_rows(gp, unit(2, "grobheight", lab), -2)
# Add 'lab' grob to that row, under the plot panel
gp = gtable_add_grob(gp, lab, t = -2, l = gp$layout[gp$layout$name == "panel",]$l)
grid.newpage()
grid.draw(gp)
Actually the best answer and easiest solution is to use the cowplot package.
Version 0.5.0 of the cowplot package (on CRAN) handles ggplot2 subtitles using the add_sub function.
Use it like so:
diamondsCubed <-ggplot(aes(carat, price), data = diamonds) +
geom_point() +
scale_x_continuous(trans = cuberoot_trans(), limits = c(0.2, 3),
breaks = c(0.2, 0.5, 1, 2, 3)) +
scale_y_continuous(trans = log10_trans(), limits = c(350, 15000),
breaks = c(350, 1000, 5000, 10000, 15000)) +
ggtitle('Price log10 by Cube-Root of Carat') +
theme_xkcd()
ggdraw(add_sub(diamondsCubed, "This is an annotation.\nAnnotations can span multiple lines."))

Displaying text below the plot generated by ggplot2

I am trying to display some information about the data below the plot created in ggplot2. I would like to plot the N variable using the X axis coordinate of the plot but the Y coordinate needs to be 10% from the bottom of the screen . In fact, the desired Y coordinates are already in the data frame as y_pos variable.
I can think of 3 approaches using ggplot2:
1) Create an empty plot below the actual plot, use the same scale and then use geom_text to plot the data over the blank plot. This approach sort of works but is extremely complicated.
2) Use geom_text to plot the data but somehow use y coordinate as percent of the screen (10%). This would force the numbers to be displayed below the plot. I can't figure out the proper syntax.
3) Use grid.text to display the text. I can easily set it at the 10% from the bottom of the screen but I can't figure how set the X coordindate to match the plot. I tried to use grconvert to capture the initial X position but could not get that to work as well.
Below is the basic plot with the dummy data:
graphics.off() # close graphics windows
library(car)
library(ggplot2) #load ggplot
library(gridExtra) #load Grid
library(RGraphics) # support of the "R graphics" book, on CRAN
#create dummy data
test= data.frame(
Group = c("A", "B", "A","B", "A", "B"),
x = c(1 ,1,2,2,3,3 ),
y = c(33,25,27,36,43,25),
n=c(71,55,65,58,65,58),
y_pos=c(9,6,9,6,9,6)
)
#create ggplot
p1 <- qplot(x, y, data=test, colour=Group) +
ylab("Mean change from baseline") +
geom_line()+
scale_x_continuous("Weeks", breaks=seq(-1,3, by = 1) ) +
opts(
legend.position=c(.1,0.9))
#display plot
p1
The modified gplot below displays numbers of subjects, however they are displayed WITHIN the plot. They force the Y scale to be extended. I would like to display these numbers BELOW the plot.
p1 <- qplot(x, y, data=test, colour=Group) +
ylab("Mean change from baseline") +
geom_line()+
scale_x_continuous("Weeks", breaks=seq(-1,3, by = 1) ) +
opts( plot.margin = unit(c(0,2,2,1), "lines"),
legend.position=c(.1,0.9))+
geom_text(data = test,aes(x=x,y=y_pos,label=n))
p1
A different approach of displaying the numbers involves creating a dummy plot below the actual plot. Here is the code:
graphics.off() # close graphics windows
library(car)
library(ggplot2) #load ggplot
library(gridExtra) #load Grid
library(RGraphics) # support of the "R graphics" book, on CRAN
#create dummy data
test= data.frame(
group = c("A", "B", "A","B", "A", "B"),
x = c(1 ,1,2,2,3,3 ),
y = c(33,25,27,36,43,25),
n=c(71,55,65,58,65,58),
y_pos=c(15,6,15,6,15,6)
)
p1 <- qplot(x, y, data=test, colour=group) +
ylab("Mean change from baseline") +
opts(plot.margin = unit(c(1,2,-1,1), "lines")) +
geom_line()+
scale_x_continuous("Weeks", breaks=seq(-1,3, by = 1) ) +
opts(legend.position="bottom",
legend.title=theme_blank(),
title.text="Line plot using GGPLOT")
p1
p2 <- qplot(x, y, data=test, geom="blank")+
ylab(" ")+
opts( plot.margin = unit(c(0,2,-2,1), "lines"),
axis.line = theme_blank(),
axis.ticks = theme_segment(colour = "white"),
axis.text.x=theme_text(angle=-90,colour="white"),
axis.text.y=theme_text(angle=-90,colour="white"),
panel.background = theme_rect(fill = "transparent",colour = NA),
panel.grid.minor = theme_blank(),
panel.grid.major = theme_blank()
)+
geom_text(data = test,aes(x=x,y=y_pos,label=n))
p2
grid.arrange(p1, p2, heights = c(8.5, 1.5), nrow=2 )
However, that is very complicated and would be hard to modify for different data. Ideally, I'd like to be able to pass Y coordinates as percent of the screen.
The current version (>2.1) has a + labs(caption = "text"), which displays an annotation below the plot. This is themeable (font properties,... left/right aligned). See https://github.com/hadley/ggplot2/pull/1582 for examples.
Edited opts has been deprecated, replaced by theme; element_blank has replaced theme_blank; and ggtitle() is used in place of opts(title = ...
Sandy- thank you so much!!!! This does exactly what I want. I do wish we could control the clipping in geom.text or geom.annotate.
I put together the following program if anybody else is interested.
rm(list = ls()) # clear objects
graphics.off() # close graphics windows
library(ggplot2)
library(gridExtra)
#create dummy data
test= data.frame(
group = c("Group 1", "Group 1", "Group 1","Group 2", "Group 2", "Group 2"),
x = c(1 ,2,3,1,2,3 ),
y = c(33,25,27,36,23,25),
n=c(71,55,65,58,65,58),
ypos=c(18,18,18,17,17,17)
)
p1 <- qplot(x=x, y=y, data=test, colour=group) +
ylab("Mean change from baseline") +
theme(plot.margin = unit(c(1,3,8,1), "lines")) +
geom_line()+
scale_x_continuous("Visits", breaks=seq(-1,3) ) +
theme(legend.position="bottom",
legend.title=element_blank())+
ggtitle("Line plot")
# Create the textGrobs
for (ii in 1:nrow(test))
{
#display numbers at each visit
p1=p1+ annotation_custom(grob = textGrob(test$n[ii]),
xmin = test$x[ii],
xmax = test$x[ii],
ymin = test$ypos[ii],
ymax = test$ypos[ii])
#display group text
if (ii %in% c(1,4)) #there is probably a better way
{
p1=p1+ annotation_custom(grob = textGrob(test$group[ii]),
xmin = 0.85,
xmax = 0.85,
ymin = test$ypos[ii],
ymax = test$ypos[ii])
}
}
# Code to override clipping
gt <- ggplot_gtable(ggplot_build(p1))
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)
Updated opts() has been replaced with theme()
In the code below, a base plot is drawn, with a wider margin at the bottom of the plot. The textGrob is created, then inserted into the plot using annotation_custom(). Except the text is not visible because it is outside the plot panel - the output is clipped to the panel. But using baptiste's code from here, the clipping can be overrridden. The position is in terms of data units, and both text labels are centred.
library(ggplot2)
library(grid)
# Base plot
df = data.frame(x=seq(1:10), y = seq(1:10))
p = ggplot(data = df, aes(x = x, y = y)) + geom_point() + ylim(0,10) +
theme(plot.margin = unit(c(1,1,3,1), "cm"))
p
# Create the textGrobs
Text1 = textGrob(paste("Largest x-value is", round(max(df$x), 2), sep = " "))
Text2 = textGrob(paste("Mean = ", mean(df$x), sep = ""))
p1 = p + annotation_custom(grob = Text1, xmin = 4, xmax = 4, ymin = -3, ymax = -3) +
annotation_custom(grob = Text2, xmin = 8, xmax = 8, ymin = -3, ymax = -3)
p1
# Code to override clipping
gt <- ggplotGrob(p1)
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)
Or, using grid functions to create and position the label.
p
grid.text((paste("Largest x-value is", max(df$x), sep = " ")),
x = unit(.2, "npc"), y = unit(.1, "npc"), just = c("left", "bottom"),
gp = gpar(fontface = "bold", fontsize = 18, col = "blue"))
Edit
Or, add text grob using gtable functions.
library(ggplot2)
library(grid)
library(gtable)
# Base plot
df = data.frame(x=seq(1:10), y = seq(1:10))
p = ggplot(data = df, aes(x = x, y = y)) + geom_point() + ylim(0,10)
# Construct the text grob
lab = textGrob((paste("Largest x-value is", max(df$x), sep = " ")),
x = unit(.1, "npc"), just = c("left"),
gp = gpar(fontface = "bold", fontsize = 18, col = "blue"))
gp = ggplotGrob(p)
# Add a row below the 2nd from the bottom
gp = gtable_add_rows(gp, unit(2, "grobheight", lab), -2)
# Add 'lab' grob to that row, under the plot panel
gp = gtable_add_grob(gp, lab, t = -2, l = gp$layout[gp$layout$name == "panel",]$l)
grid.newpage()
grid.draw(gp)
Actually the best answer and easiest solution is to use the cowplot package.
Version 0.5.0 of the cowplot package (on CRAN) handles ggplot2 subtitles using the add_sub function.
Use it like so:
diamondsCubed <-ggplot(aes(carat, price), data = diamonds) +
geom_point() +
scale_x_continuous(trans = cuberoot_trans(), limits = c(0.2, 3),
breaks = c(0.2, 0.5, 1, 2, 3)) +
scale_y_continuous(trans = log10_trans(), limits = c(350, 15000),
breaks = c(350, 1000, 5000, 10000, 15000)) +
ggtitle('Price log10 by Cube-Root of Carat') +
theme_xkcd()
ggdraw(add_sub(diamondsCubed, "This is an annotation.\nAnnotations can span multiple lines."))

Resources