ggplot2 two-line label with expression - r

I'd like to write an axis label over two lines with an expression() statement. However, plotmath and expression won't allow this (e.g. subscripted text appears on the far right). I found this discussion circa 2005 of a similar issue but the work around that they offer doesn't translate to my application in ggplot2. A recent question addressed a different permutation of multi-line expression statements, but again the work around provided doesn't apply here.
Example:
p <- ggplot(mtcars,aes(x=wt,y=mpg))+
geom_point()+
xlab(expression(paste("A long string of text goes here just for the purpose \n of illustrating my point Weight "[reported])))
try(ggsave(plot=p,filename=<some file>,height=4,width=6))
yields an image where subscript "reported" is kicked out to the right when I'd like it to sit next to the previous word.

I think this is a bug. (Or a consequence of the fact that "multi-line expressions are not supported", as stated in the conversation you linked to).
The workaround that Gavin Simpson alluded to is:
#For convenience redefine p as the unlabeled plot
p <- ggplot(mtcars,aes(x=wt,y=mpg))+geom_point()
#Use atop to fake a line break
p + xlab(expression(atop("A long string of text for the purpose", paste("of illustrating my point" [reported]))))
It is possible to use true line breaks with subscripts. In the short example below, which has the same form as your example, the subscript is correctly placed adjacent to the rest of the text but the two lines of text are not centered correctly:
p + xlab(expression(paste("line1 \n line2 a" [b])))
I think that in both cases, the subscript is placed wrong when the upper line of text is longer than the lower line of text. Compare
p + xlab(expression(paste("abc \n abcd" [reported])))
p + xlab(expression(paste("abc \n ab" [reported])))
The subscript always ends up aligned just to the right of the right end of the upper line.
p + xlab(expression(paste("abcdefghijklmnop \n ab" [reported])))

1) Solution with cowplot::draw_label()
One could also use the annotation function draw_label() from the package cowplot (suggested in this discussion). We could call cowplot::draw_label() as many lines of text we have. When cowplot::draw_label() is used in combination with cowplot::ggdraw(), it can annotate anywhere on the canvas/sheet with the coordinates ranging from 0 to 1 (relative to the entire canvas).
One needs to tweak the annotation position and make enough space for the custom axis title.
Note that the cowplot package currently alters the default ggplot theme, therefore, if needed, use theme_set() after loading the package as mentioned here.
Note also that the function cowplot::draw_label() uses ggplot2::annotation_custom() under the hood. I'll mention more about this in the second part below.
library(ggplot2)
library(cowplot)
#>
#> Attaching package: 'cowplot'
#> The following object is masked from 'package:ggplot2':
#>
#> ggsave
# If needed, revert to default theme (cowplot modifies the theme);
# theme_set(theme_grey())
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
# Make enough space for the custom two lines axis title
p <- p +
xlab("") + # empty label
# Tweak the margins (push the label down by forcing a wider top margin)
theme(axis.title.x = element_text(size = 10, # also adjust text size if needed
margin = margin(t = 10, r = 0, b = 0, l = 0,
unit = "mm")))
# The two lines we wish on the plot
line_1 <- "A long string of text for the purpose"
line_2 <- expression(paste("of illustrating my point" [reported]))
# Or avoid paste() (is not actually needed)
# line_2 <- expression("of illustrating my point" [reported])
# Call cowplot::draw_label two times to plot two lines of text
ggdraw(p) +
draw_label(line_1, x = 0.55, y = 0.075) + # use relative coordinates for positioning
draw_label(line_2, x = 0.55, y = 0.025)
Note that, cowplot::draw_label() can also be used in combination with setting the clipping off, coord_cartesian(clip = "off"), which allows plotting anywhere on the canvas. This time we do not use the relative coordinates anymore, but the ones from the plot/data (the absolute coordinates):
# Other two expressions
line_1b <- expression(bolditalic('First line'))
line_2b <- expression(integral(f(x)*dx, a, b))
p + coord_cartesian(clip = "off") + # allows plotting anywhere on the canvas
draw_label(line_1b, x = 3.5, y = 8.2) + # use absolute coordinates for positioning
draw_label(line_2b, x = 3.5, y = 6)
Created on 2019-01-14 by the reprex package (v0.2.1)
2) Solution with ggplot2::annotation_custom()
As mentioned, cowplot::draw_label() is a wrapper of ggplot2::annotation_custom(). So, instead of cowplot::draw_label(), we could use directly ggplot2::annotation_custom() in combination with setting the clipping off - coord_cartesian(clip = "off"), which became available with merging this pull request.
However, this approach is more verbose, with more coordinate arguments and we need to employ grid::textGrob().
# Some other two lines we wish on the plot as OX axis title
line_1c <- expression("Various fonts:" ~ bolditalic("bolditalic") ~ bold("bold") ~ italic("italic"))
line_2c <- expression("this" ~~ sqrt(x, y) ~~ "or this" ~~ sum(x[i], i==1, n) ~~ "math expression")
# the ~~ ads a bit more space than ~ between the expression's components
p + coord_cartesian(clip = "off") +
annotation_custom(grid::textGrob(line_1c), xmin = 3.5, xmax = 3.5, ymin = 7.3, ymax = 7.3) +
annotation_custom(grid::textGrob(line_2c), xmin = 3.5, xmax = 3.5, ymin = 5.5, ymax = 5.5)
Created on 2019-01-14 by the reprex package (v0.2.1)

The package ggtext offers a different option, by allowing for HTML tags to format/customise labels and text.
library(ggtext)
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
xlab("A long string of text goes here just for the purpose<br>of illustrating my point Weight<sub>reported</sub>") +
theme(axis.title.x = element_markdown())

You could use this trick,
library(gridExtra)
library(grid)
element_custom <- function() {
structure(list(), class = c("element_custom", "element_text"))
}
element_grob.element_custom <- function(element, label="", ...) {
mytheme <- ttheme_minimal(core = list(fg_params = list(parse=TRUE,
hjust=0, x=0.1)))
disect <- strsplit(label, "\\n")[[1]]
tableGrob(as.matrix(disect), theme=mytheme)
}
# default method is unreliable
heightDetails.gtable <- function(x) sum(x$heights)
ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_line() +
labs(x= "First~line \n italic('and a second') \n integral(f(x)*dx, a, b)")+
(theme_grey() %+replace% theme(axis.title.x = element_custom()))

Related

Add Logo to ggplot footer with `+` operator

So, I want to add a logo footer to my ggplot, but I want to do so via a function that I can use the + operator with, so I can do: qplot(1:10, 1:10) + add_mylogo()
I can get what I want with this:
library(magick)
library(ggplot2)
mylogo <- image_scale(image_read("https://upload.wikimedia.org/wikipedia/commons/f/f7/Stack_Overflow_logo.png"), "180")
qplot(1:10, 1:10) + labs(caption="")
grid::grid.raster(mylogo, x = .97, y = .02, just = c('right', 'bottom'), width = unit(1.2, 'inches'))
Which produces:
My problem is that I want to use + to add it to the plot.
So then I tried this:
library(cowplot)
library(magick)
library(ggplot2)
mylogo <- image_scale(image_read("https://upload.wikimedia.org/wikipedia/commons/f/f7/Stack_Overflow_logo.png"), "380")
qplot(1:3, 1:3)+ labs(caption="") +
draw_image(mylogo, x=3, y = .2, hjust=.7, vjust=0, scale = .5, clip=TRUE)
Which produces:
So now I have it working with a + operator, but I can't figure out how to use any sort of relative positioning. I saw this post about relative positioning using annotate and tried it, but it doesn't work
qplot(1:3, 1:3)+ labs(caption="") +
draw_image(mylogo, x = -Inf, y = Inf, hjust=.7, vjust=0, scale = .5, clip=TRUE)
Error in if (rasterRatio > vpRatio) { :
missing value where TRUE/FALSE needed
So the first option using grid works with relative positioning, but not with the + operator, and the second option using cowplot works with the + operator, but not relative positioning. Is there any way to get both?
You could consider using ggtext:
library(ggplot2)
library(ggtext)
qplot(1:3, 1:3) +
labs(caption = "<img src='https://upload.wikimedia.org/wikipedia/commons/f/f7/Stack_Overflow_logo.png' width='100'/>") +
theme(plot.caption = element_markdown())
The way you are using draw_image is intended to use the plot coordinates, not relative positioning, as you can see in the draw_image help examples. For using relative positioning you have to change a little bit your code, adding first the image, and then the plot inside a draw_plot call:
ggdraw() +
draw_image(mylogo, scale = 0.2, x = 0.38, y = -0.45) +
draw_plot(
qplot(1:3, 1:3)+ labs(caption="") +
theme(panel.background = element_blank(), plot.background = element_blank())
)
In my tests, positioning seems to be relative to the center of the plot (x = 0, y = 0), so you need negative values to go down in y or left in x.
Another "problem" is that the logo is added before, so you must convert the plot or panel background to transparent or remove them.

ggplot: place image in corner of panel

I'm trying to find a way to insert an image into the corner of a ggplot panel, without specifying the coordinates manually each time.
In this instance, I'm attempting to place a graphic in the top right.
library(magick)
library(ggplot2)
library(datasets)
homer <- magick::image_read("http://icons.iconarchive.com/icons/jonathan-rey/simpsons/128/Homer-Simpson-04-Happy-icon.png")
g <- ggplot(mpg, aes(class)) +
geom_bar() +
labs(
title = "Count of Auto by Class",
subtitle = "Text to Create More Space")
g + annotation_custom(rasterGrob(homer, interpolate = TRUE),
xmax = Inf, ymax = Inf) +
coord_cartesian(clip = "off")
I have found some examples that come close to solving this:
Inserting an image to ggplot outside the chart area
Corner Labels in ggplot2
But neither quite get there. Specifying the exact location at which to place the image seems to require quite a bit of trial-and-error on each plot created, especially when x is categorical.
I would also like to maintain the size of my original image; the code I've used above seems to stretch it across the plot.
Thanks in advance...much appreciated.
try this
library(grid)
a <- rasterGrob(homer, interpolate = TRUE,
width=unit(1,'cm'),
x = unit(1,"npc"), y = unit(1,"npc"),
hjust = 1, vjust=1)
g + annotation_custom(grob = a)

Using math symbols in ggplot's annotate function

I am using ggplot to plot some data across 5 facets and I want to put some text that says "Delta = #" where Delta is the upper case math delta symbol and # is 1,2,3,4, or 5 based on which facet it is. Here is what I have:
annotate("text",x="baseline",y=75,label=paste(expression(Delta),"=",1:5))
My line of code works but it spells out Delta rather than giving me the Delta symbol. How can I get the math symbol?
Try this
df <- mtcars[2:6,]
ggplot(df, aes(mpg, disp))+
geom_point()+
annotate("text",df$mpg,df$disp,label=paste(("Delta * '=' *"), 1:5),
parse=TRUE, hjust = 1.1)
annotate() will give you the same annotation on each facet, you should use geom_text() instead, with a suitable data.frame to provide the mapping.
library(ggplot2)
ggplot(data.frame(f=1:2, lab = sprintf("Delta == %i", 1:2))) + facet_wrap(~f) +
geom_text(aes(label=lab), x=0, y=0, parse=TRUE)

Multi line title in ggplot 2 with multiple italicized words

I am trying to create a plot title manually formatted on two lines which includes two italicized words, I have done some searching
on Stack Exchange but have not found a good solution to this seemingly simple problem.
The scientific names of the two species are fairly long, and thus the need for a multi-line title (ggplot2 doesn't format this).
Objective:
..........First Line of Title with Species
Second line words anotherItalicSpecies the end
ggplot(mtcars,aes(x=wt,y=mpg))+
geom_point()+
labs(title= expression(paste(atop("First line of title with ", atop((italic("Species")))),"
secondline words", italic("anotherSpecies"), "the end")))
Which yields the following mangled title:
Using a combination of atop, paste, italic and scriptstyle:
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
labs(title = ~ atop(paste('First line of title with ',italic("Species")),
paste(scriptstyle(italic("Species")),
scriptstyle(" secondline words "),
scriptstyle(italic("anotherSpecies")),
scriptstyle(" the end"))))
gives you the desired result:
Using scriptstyle is not a necessity, but imho it is nicer to have your subtitle in a smaller font than the main title.
See also ?plotmath for other usefull customizations.
As an alternative to inserting line breaks in title, you may use title together with subtitle (available from ggplot 2.2.0). Possibly this makes the plothmathing slightly more straightforward.
p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
labs(title = expression("First line: "*italic("Honorificabilitudinitatibus")),
subtitle = expression("Second line: "*italic("Honorificabilitudinitatibus praelongus")*" and more"))
p
If you wish the font size to be the same on both lines, set the desired size in theme.
p + theme(plot.title = element_text(size = 12),
plot.subtitle = element_text(size = 12))
Note that both title and subtitle are left-aligned by default in ggplot2 2.2.0. The text can be centered by adding hjust = 0.5 to element_text above.
We could also call cowplot::draw_label() two times (inspired from this discussion). However, we need to tweak a bit the position and make enough space for the custom title. I gave more explanations about this approach and also using ggplot2::annotation_custom() in ggplot2 two-line label with expression.
library(ggplot2)
library(cowplot)
#>
#> Attaching package: 'cowplot'
#> The following object is masked from 'package:ggplot2':
#>
#> ggsave
# The two lines we wish on the plot. The ~~ creates extra space between the
# expression's components, might be needed here.
line_1 <- expression("First Line of Title with" ~~ italic("Species"))
line_2 <- expression(italic("Species") ~~ "second line words" ~~ italic("anotherSpecies") ~~ "the end")
# Make enough space for the custom two lines title
p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
labs(title = "") + # empty title
# Force a wider top margin to make enough space
theme(plot.title = element_text(size = 10, # also adjust text size if needed
margin = margin(t = 10, r = 0, b = 0, l = 0,
unit = "mm")))
# Call cowplot::draw_label two times to plot the two lines of text
ggdraw(p) +
draw_label(line_1, x = 0.55, y = 0.97) +
draw_label(line_2, x = 0.55, y = 0.93)
Created on 2019-01-14 by the reprex package (v0.2.1)

Whisker plots to compare mean and variance between clusters [duplicate]

I am trying to recreate a figure from a GGplot2 seminar http://dl.dropbox.com/u/42707925/ggplot2/ggplot2slides.pdf.
In this case, I am trying to generate Example 5, with jittered data points subject to a dodge. When I run the code, the points are centered around the correct line, but have no jitter.
Here is the code directly from the presentation.
set.seed(12345)
hillest<-c(rep(1.1,100*4*3)+rnorm(100*4*3,sd=0.2),
rep(1.9,100*4*3)+rnorm(100*4*3,sd=0.2))
rep<-rep(1:100,4*3*2)
process<-rep(rep(c("Process 1","Process 2","Process 3","Process 4"),each=100),3*2)
memorypar<-rep(rep(c("0.1","0.2","0.3"),each=4*100),2)
tailindex<-rep(c("1.1","1.9"),each=3*4*100)
ex5<-data.frame(hillest=hillest,rep=rep,process=process,memorypar=memorypar, tailindex=tailindex)
stat_sum_df <- function(fun, geom="crossbar", ...) {stat_summary(fun.data=fun, geom=geom, ...) }
dodge <- position_dodge(width=0.9)
p<- ggplot(ex5,aes(x=tailindex ,y=hillest,color=memorypar))
p<- p + facet_wrap(~process,nrow=2) + geom_jitter(position=dodge) +geom_boxplot(position=dodge)
p
In ggplot2 version 1.0.0 there is new position named position_jitterdodge() that is made for such situation. This postion should be used inside the geom_point() and there should be fill= used inside the aes() to show by which variable to dodge your data. To control the width of dodging argument dodge.width= should be used.
ggplot(ex5, aes(x=tailindex, y=hillest, color=memorypar, fill=memorypar)) +
facet_wrap(~process, nrow=2) +
geom_point(position=position_jitterdodge(dodge.width=0.9)) +
geom_boxplot(fill="white", outlier.colour=NA, position=position_dodge(width=0.9))
EDIT: There is a better solution with ggplot2 version 1.0.0 using position_jitterdodge. See #Didzis Elferts' answer. Note that dodge.width controls the width of the dodging and jitter.width controls the width of the jittering.
I'm not sure how the code produced the graph in the pdf.
But does something like this get you close to what you're after?
I convert tailindex and memorypar to numeric; add them together; and the result is the x coordinate for the geom_jitter layer. There's probably a more effective way to do it. Also, I'd like to see how dodging geom_boxplot and geom_jitter, and with no jittering, will produce the graph in the pdf.
library(ggplot2)
dodge <- position_dodge(width = 0.9)
ex5$memorypar2 <- as.numeric(ex5$tailindex) +
3 * (as.numeric(as.character(ex5$memorypar)) - 0.2)
p <- ggplot(ex5,aes(x=tailindex , y=hillest)) +
scale_x_discrete() +
geom_jitter(aes(colour = memorypar, x = memorypar2),
position = position_jitter(width = .05), alpha = 0.5) +
geom_boxplot(aes(colour = memorypar), outlier.colour = NA, position = dodge) +
facet_wrap(~ process, nrow = 2)
p

Resources