Automagic line breaks in graph annotation? Or better subtitle support?

638 views
Skip to first unread message

Brandon Hurr

unread,
Mar 18, 2010, 11:27:27 AM3/18/10
to ggplot2
Hello, 

I'm putting together a function that plots gene expression data over a series of time points for a collection of genotypes. I would like to create a subtitle for the graph that prints known information about the gene being plotted, but I've searched around and there doesn't seem to be an implementation for this in ggplot. So I tried using +annotate()

qplot(1:10, 1:10)+annotate("text", x=5, y=5, label ="asdf asdf asdf adsf asdf adsf adsf asdf asdf adsf asdf adsf asdf asdf asdfasdfasdf asdfasdfasd fadsfads fadsfads fasdf adsasdf adsf asdfasdfasd fasdfasd fasdfasdfasd fasdfasdfasd fasdfasdfadsfasd fasdf adsfasdf asdf asdfasdf asdfasdfasdf asdfasdf asdfasdfa sdfasdfasd fasdfasdfasd fasdfadsfasdf asdfasdf adfs", color="black", size=3)

The issue is that my "subtitles" range from a few words to a large paragraph (gene description information). What happens is that the annotation is printed on one line, which truncates most of the long descriptions. I'm hoping to find a way to put these descriptions to the top or bottom of the graphs, and have them line break within a predefined width (% of graph width or similar). 

Is there a better way to do what I want?

Thanks, 

Brandon

P.S. Actual ggplot code I'm using...

#lots of stuff before this

for (x in 3:namelength){
shortchip[[x]]<-as.numeric(shortchip[[x]])
dataset.avg<-ddply(shortchip, c("genotype", "time"), function(df) return(c(dataset.avg=mean(df[[x]],na.rm=TRUE), dataset.se=std.error(df[[x]]), na.rm=TRUE)))
p<-x-2
#creates vertical bar plot from mean with SEM errorbars and puts into list for storage 
#(units for graphs could be adapted to read a vector from a new file) 
#(shape=genotype only works for 6 or less lines)
plotname <- paste(onamae[x]) 
avg.plot<-qplot(x=time, dataset.avg, data=dataset.avg, geom=c("line", "point"), ylab="Expression", main=onamae[x], color=genotype)
plotlist[[plotname]] <- avg.plot+geom_errorbar(aes(ymax=dataset.avg+dataset.se, ymin=dataset.avg-dataset.se), width=1)+theme_bw()+opts(axis.text.x=theme_text(angle=-45, hjust=0, size=9)) + annotate("text", x = 30, y = 1, label = selectednames[p,2], color = "black",size = 2)
}
#saving plots to png files go here
label = selectednames[p,2]
selectednames is a dataframe which contains gene name in column 1 and gene description in column 2
nolinebreak.png

baptiste auguie

unread,
Mar 18, 2010, 11:45:01 AM3/18/10
to Brandon Hurr, ggplot2
Hi,

The book R graphics provides an example of automatic splitting of a
long paragraph to fit in a rectangle of fixed width. It should be
quite easy to adapt it into a new geom for ggplot2.

library(RGraphics)
grid.draw(splitTextGrob("asdf asdf asdf adsf asdf adsf adsf asdf asdf


adsf asdf adsf asdf asdf asdfasdfasdf asdfasdfasd fadsfads fadsfads
fasdf adsasdf adsf asdfasdfasd fasdfasd fasdfasdfasd fasdfasdfasd
fasdfasdfadsfasd fasdf adsfasdf asdf asdfasdf asdfasdfasdf asdfasdf

asdfasdfa sdfasdfasd fasdfasdfasd fasdfadsfasdf asdfasdf adfs"))

(try resizing the window)

With ggplot2 it would be necessary to specify the width of the
annotation box too.

Let me know if this sounds like a good approach and I'll try to write
the geom later if time permits.

HTH,

baptiste

PS / wish: Would it be possible to restrict the use of a particular
geom to annotate() only with ggplot2? I have a few examples where it
makes no sense at all to define a layer with aesthetic mapping and it
would be nice to throw an error if someone inadvertently tried...

> --
> You received this message because you are subscribed to the ggplot2 mailing
> list.
> To post to this group, send email to ggp...@googlegroups.com
> To unsubscribe from this group, send email to
> ggplot2+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/ggplot2

Brandon Hurr

unread,
Mar 18, 2010, 12:30:22 PM3/18/10
to baptiste auguie, ggplot2
Baptiste,

That's certainly better than what I had. Is there any way to specify the location of the annotation outside of the bounds of the graph? I'd hate to have such a long gene description that you can no longer see the lines and error bars.This is why I would prefer some sort of subtitle option rather than annotating the graph itself.   

Thanks, 

Brandon

P.S. I guess it doesn't like blank descriptions though

Error in strsplit(text, " ") : non-character argument

baptiste auguie

unread,
Mar 18, 2010, 5:29:58 PM3/18/10
to Brandon Hurr, ggplot2
Hi,

You could define your own title (or subtitle for that matter),


splitString <- function (text, width)
{
if(is.null(text)) return(NULL)

strings <- strsplit(text, " ")[[1]]
newstring <- strings[1]
linewidth <- stringWidth(newstring)
gapwidth <- stringWidth(" ")
availwidth <- convertWidth(width, "inches", valueOnly = TRUE)
for (i in 2:length(strings)) {
width <- stringWidth(strings[i])
if (convertWidth(linewidth + gapwidth + width, "inches",
valueOnly = TRUE) < availwidth) {
sep <- " "
linewidth <- linewidth + gapwidth + width
}
else {
sep <- "\n"
linewidth <- width
}
newstring <- paste(newstring, strings[i], sep = sep)
}
newstring
}


my.title <- function(label, ...)
textGrob(label=splitString("asdf asdf asdf adsf asdf adsf adsf asdf asdf


adsf asdf adsf asdf asdf asdfasdfasdf asdfasdfasd fadsfads fadsfads
fasdf adsasdf adsf asdfasdfasd fasdfasd fasdfasdfasd fasdfasdfasd
fasdfasdfadsfasd fasdf adsfasdf asdf asdfasdf asdfasdfasdf asdfasdf
asdfasdfa sdfasdfasd fasdfasdfasd fasdfadsfasdf asdfasdf adfs",

width=unit(10, "cm")), ...)


qplot(1:10, 1:10) + opts(plot.title = my.title)

HTH,

baptiste

Brandon Hurr

unread,
Mar 19, 2010, 6:50:49 AM3/19/10
to baptiste auguie, ggplot2
Baptiste, 

As is, that works pretty well, but when I try to incorporate it into my function I either get errors or I get no title at all (without errors though). 

I'm trying to get it to pull the title from a dataframe, which means the my.title function is probably just superfluous, so I pulled it's guts and used them in the plot.title call (see below).
------------------------------------------------------------------------------
title<-"Marker1 :  Similar to AAA80501.1 Lycopersicon esculentum; unknown; Solanum lycopersicum unknown oxidase-like protein mRNA, complete cds; similar to hyoscyamine 6beta-hydroxylase: SwissProt Accession Number P24397, flavavone-3-hydroxylase: PIR Accession Number S36233, and ethylene forming enzyme: encoded by GenBank Accession number M90294 ;cl209ct403cn544 ;COMPLETE ;RESISTANCE(6), FLOWER(71), FRUIT(7)"

subtitledplot<-qplot(1:10, 1:10) + opts(plot.title = textGrob(splitString(title, width=unit(10,"cm"))))
print(subtitledplot)
-------------------------------------------------------------------------------
Except this doesn't work. 
------------------------------------------------------------------------------
Error in UseMethod("validGrob") : 
  no applicable method for 'validGrob' applied to an object of class "list"
In addition: Warning message:
In editThisGrob(grob, specs) : Slot 'vp' not found
------------------------------------------------------------------------------
I'm afraid these errors are over my head. I'm not sure where it is getting a list from and I can't see the difference between what I've done and what you came up with. Whatever it is, it's probably staring me in the face and you'll find it in 0.00012239859123 seconds.

With much appreciation, 

Brandon


baptiste auguie

unread,
Mar 19, 2010, 7:06:37 AM3/19/10
to Brandon Hurr, ggplot2
Hi,

I think opts(plot.title) expects a function that returns a grob, not
directly a grob. A cleaner version of what I proposed last night might
be,

splitString <- function (text, width)
{
if(is.null(text)) return(NULL)

strings <- strsplit(text, " ")[[1]]
newstring <- strings[1]
linewidth <- stringWidth(newstring)
gapwidth <- stringWidth(" ")
availwidth <- convertWidth(width, "inches", valueOnly = TRUE)
for (i in 2:length(strings)) {
width <- stringWidth(strings[i])
if (convertWidth(linewidth + gapwidth + width, "inches",
valueOnly = TRUE) < availwidth) {
sep <- " "
linewidth <- linewidth + gapwidth + width
}
else {
sep <- "\n"
linewidth <- width
}
newstring <- paste(newstring, strings[i], sep = sep)
}
newstring
}

title <- "Marker1 : Similar to AAA80501.1 Lycopersicon esculentum;
unknown; Solanum lycopersicum unknown oxidase-like protein mRNA,
complete cds; similar to hyoscyamine 6beta-hydroxylase: SwissProt
Accession Number P24397, flavavone-3-hydroxylase: PIR Accession Number
S36233, and ethylene forming enzyme: encoded by GenBank Accession
number M90294 ;cl209ct403cn544 ;COMPLETE ;RESISTANCE(6), FLOWER(71),
FRUIT(7)"

theme_title <-
function (width=unit(1, "npc"), family = "", face = "plain", colour =
"black", size = 10,
hjust = 0.5, vjust = 0.5, angle = 0, lineheight = 1.1)
{
vj <- vjust
hj <- hjust
structure(function(label, x = hj, y = vj, ..., vjust = vj,
hjust = hj, default.units = "npc") {
textGrob(splitString(label, width), x, y, hjust = hjust, vjust
= vjust, ...,
default.units = default.units, gp = gpar(fontsize = size,
col = colour, fontfamily = family, fontface = face,
lineheight = lineheight), rot = angle)
}, class = "theme", type = "title", call = match.call())
}


qplot(1:10, 1:10) +

opts(title=title, plot.title=theme_title(width=unit(10, "cm")))


I'm not sure how one is supposed to define theme_*** elements though;
perhaps the above is not supposed to have type="title" but "text", I
dunno.

HTH,

baptiste

Brandon Hurr

unread,
Mar 19, 2010, 7:32:35 AM3/19/10
to baptiste auguie, ggplot2
Baptiste, 

That does work. Thanks an absolute ton, you saved me a nervous breakdown this morning. :P

Brandon
Reply all
Reply to author
Forward
0 new messages