Functions that create ggplot2 plots

42 views
Skip to first unread message

hadley wickham

unread,
Feb 7, 2009, 6:03:43 PM2/7/09
to ggplot2
Hi all,

I'm currently working on section for the ggplot2 book about writing
functions that create ggplot2 plots. I'm having a bit of a hard time
thinking about what I should say (or even if it's useful enough to
include in the book), so I was wondering if you'd written a function
that creates ggplot2 plots, if you would mind sending it to me for
some inspiration.

Thanks!

Hadley

--
http://had.co.nz/

baptiste auguie

unread,
Feb 8, 2009, 6:42:26 AM2/8/09
to hadley wickham, ggplot2
Hi Hadley,

I've only tried this once, mostly because I am uneasy about the scoping and parsing rules when passing arguments to a plot from within a function. On the plus side, ggplot is probably the best approach I know of to return a plot object that you can subsequently modify by adding layers, etc.

The following function (package constants on r-forge/photonics) takes a data.frame, does some trivial conversion, and outputs a list containing a ggplot. A few bottlenecks I've encountered:

- in an interactive session, calling the result of this function will print the plot whether I want it or not. I'm not sure how to make it more invisible.

- the variable names in the ggplot call are all hard-wired because I don't know how to pass them as arguments. A few tips on using substitute, quote, bquote, eval, deparse ... would be very useful I think.

- a slight issue with plotmath type labels not working in strips, but this is not relevant here

material2plot <- function(material, xaxis="wavelength"){
    mdf <- with(material, {data.frame(
        lambda=lambda, energy = L2eV(lambda),
        epsr=Re(epsilon),  epsi=Im(epsilon),
        n=Re(nk),  k=Im(nk))
        })
   
    meltdf <- melt(mdf, id.var=c("lambda", "energy"))
   
    levels(meltdf$variable) <- c(expression(epsilon[r]), expression(epsilon[i]), expression(n), expression(k))
    mqplot <- ggplot(data = meltdf, mapping = aes(x = lambda, y = value)) + facet_wrap(~variable, scale="free") +
    layer(data=meltdf, mapping=aes(colour=variable),  geom = c( "line"), stat = "identity" )  +
    scale_y_continuous("") + 
    scale_colour_brewer(palette="Set1")
    if(xaxis == "wavelength"){
        mqplot <- mqplot +
        scale_x_continuous(expression("wavelength / "*mu*m))
    }else{
        mqplot <- mqplot +
            scale_x_continuous(expression("energy / eV"))
}
    invisible(list(mdf=mdf, meltdf=meltdf, qplot=mqplot))
}

# example
data(AuJChristy) # load some raw data
gold <- fit.material("AuJChristy", range=c(0.4, 1), draw=F) # create the data.frame used in material2plot (spline fit of the raw data)

material2plot(gold$fine)->testFine
material2plot(gold$raw)->testRaw

testFine # this prints the plot in an interactive session

testFine$qplot + # this possibility of adding layers is awesome!
layer(data=testRaw$meltdf, mapping=aes(colour=variable),  geom = c( "point"), stat = "identity" )






From: hadley wickham <h.wi...@gmail.com>
To: ggplot2 <ggp...@googlegroups.com>
Sent: Saturday, 7 February, 2009 23:03:43
Subject: Functions that create ggplot2 plots
testLayer.pdf

hadley wickham

unread,
Feb 8, 2009, 9:32:02 AM2/8/09
to baptiste auguie, ggplot2
> I've only tried this once, mostly because I am uneasy about the scoping and
> parsing rules when passing arguments to a plot from within a function. On
> the plus side, ggplot is probably the best approach I know of to return a
> plot object that you can subsequently modify by adding layers, etc.

You will always be ok provided all of the data you provide in a data
frame. If you use ggplot you should almost always be ok, unless you
are using local variables in an aes call (which is theoretically not
allowed but I can't figure out how to detect it so I can issue an
error)

> The following function (package constants on r-forge/photonics) takes a
> data.frame, does some trivial conversion, and outputs a list containing a
> ggplot. A few bottlenecks I've encountered:
>
> - in an interactive session, calling the result of this function will print
> the plot whether I want it or not. I'm not sure how to make it more
> invisible.

What I would do here is split your function into two pieces - one that
creates the data frame and one that produces the plot. This makes it
much easier for other people to build on top of your efforts if they
don't want to use any part of the plot you provide (at least they can
get the data separately from the plot in your function, which is
better than most of base R)

So I'd suggest material2plot just returns a list of the two data
frames with class = "material2plot". You then write a
plot.material2plot that produces the plot. Your example would then
become:

gold <- fit.material("AuJChristy", range=c(0.4, 1), draw=F)

material2plot(gold$fine)->testFine

plot(testFine)
plot(testFine) + geom_point(aes(colour = variable))

> - the variable names in the ggplot call are all hard-wired because I don't
> know how to pass them as arguments. A few tips on using substitute, quote,
> bquote, eval, deparse ... would be very useful I think.

Ok - aes_string is probably the way to go here. aes_string("x",
"myvar") is equivalent to aes(x, myvar), with the obvious difference
that it's much much much easier to work with strings than expressions.

> - a slight issue with plotmath type labels not working in strips, but this
> is not relevant here

On my to do list ;)

Hadley


--
http://had.co.nz/

hadley wickham

unread,
Feb 8, 2009, 11:31:18 AM2/8/09
to baptiste auguie, ggplot2
>> - in an interactive session, calling the result of this function will print
>> the plot whether I want it or not. I'm not sure how to make it more
>> invisible.
>
> What I would do here is split your function into two pieces - one that
> creates the data frame and one that produces the plot. This makes it
> much easier for other people to build on top of your efforts if they
> don't want to use any part of the plot you provide (at least they can
> get the data separately from the plot in your function, which is
> better than most of base R)

Having actually tried that out, I now think it's bad advice. Here's
what's currently going into the book:

As mentioned in the previous section, you definitely want to separate
your plotting code into a function that does any data transformations
and manipulations and a function that creates the plot. Generally,
your plotting function should do no data manipulation, just create a
plot. See the following example for one way to create a parallel
coordinates function.

% INTERWEAVE
%
% pcp_data <- function(df) {
% numeric <- laply(df, is.numeric)
% # Rescale numeric columns
% df[numeric] <- colwise(range01)(df[numeric])
% # Add row identified
% df$.row <- rownames(df)
% # Melt, using non-numeric variables as id vars
% dfm <- melt(df, id = c(".row", names(df)[!numeric]))
% # Add pcp to class of the data frame
% class(dfm) <- c("pcp", class(dfm))
% dfm
% }
% pcp <- function(df, ...) {
% df <- pcp_data(df)
% ggplot(df, aes(variable, value)) + geom_line(aes(group = .row))
% }
% pcp(mpg)
% pcp(mpg) + aes(colour = drv)

Hadley

--
http://had.co.nz/

baptiste auguie

unread,
Feb 8, 2009, 2:24:30 PM2/8/09
to hadley wickham, ggplot2
Hi Hadley,

Thanks for the useful advice on this. I'm afraid this wasn't much of an example on how to use ggplot2 in a function though! I agree on the good practice that is separating the data manipulation and the plotting, but it probably means that creating a ggplot in a function is not very common. In fact, ggplot2 is so high-level that it's hard to see how it could be abstracted much further in a wrap-up function. Rggobi is probably a good example of generating ggplot code automatically but you don't need input for this!

Two related problems that require more low-level user adaptations come to mind, but it's not directly answering your query,

1) create a custom layout using grid, and place several ggplots in it. That's an area where many tricky questions can arise (how to align two plots with respect to their x-axis, how to put a legend in another viewport, generalize the facet_wrap layout, how to add an inset inside a ggplot? ...)

2) create a new object that you haven't implemented yet and use it in a plot. I'm thinking of a new grob such as a secondary x-axis for instance, or a new geom, ...







From: hadley wickham <h.wi...@gmail.com>
To: baptiste auguie <bapt...@yahoo.fr>
Cc: ggplot2 <ggp...@googlegroups.com>
Sent: Sunday, 8 February, 2009 16:31:18
Subject: Re: Functions that create ggplot2 plots

hadley wickham

unread,
Feb 8, 2009, 2:36:49 PM2/8/09
to baptiste auguie, ggplot2
On Sun, Feb 8, 2009 at 1:24 PM, baptiste auguie <bapt...@yahoo.fr> wrote:
> Hi Hadley,
>
> Thanks for the useful advice on this. I'm afraid this wasn't much of an
> example on how to use ggplot2 in a function though! I agree on the good
> practice that is separating the data manipulation and the plotting, but it
> probably means that creating a ggplot in a function is not very common. In
> fact, ggplot2 is so high-level that it's hard to see how it could be
> abstracted much further in a wrap-up function. Rggobi is probably a good
> example of generating ggplot code automatically but you don't need input for
> this!
>
> Two related problems that require more low-level user adaptations come to
> mind, but it's not directly answering your query,
>
> 1) create a custom layout using grid, and place several ggplots in it.
> That's an area where many tricky questions can arise (how to align two plots
> with respect to their x-axis, how to put a legend in another viewport,
> generalize the facet_wrap layout, how to add an inset inside a ggplot? ...)

I now have a decent section on that issue at the end of the
development of the polishing plots for publication chapter.
Unfortunately there are some things that it's currently basically
impossible to do with ggplot2 (like line up two plots with different
tick label lengths), so that descriptions of that will have to wait
until I've figured how to actually do it.

> 2) create a new object that you haven't implemented yet and use it in a
> plot. I'm thinking of a new grob such as a secondary x-axis for instance, or
> a new geom, ...

Again, I have a draft chapter on the topic, but at this point it's
very unlikely to make it into the book because the book is already
overdue for copyediting, and I probably need to make some
architectural changes before it's worth even beginning to describe how
to write new geoms. And implementing a secondary y axis isn't
something that's obvious to me how to do.

Maybe for the 2nd edition of the book ;)

Hadley

--
http://had.co.nz/

John

unread,
Feb 9, 2009, 4:25:06 PM2/9/09
to ggplot2
I actually wanted to do a function using ggplot2 because I was doing
about 10 plots that were all variations on the same theme and each had
similar text annotations. Mostly what stopped me was that I didn't
understand how to deal with formulas in functions. (And "formula"
turns out to be an unhelpful search term because it covers so much.) I
needed to create a formula both for cast() and for qplot() such as
"year + group + class ~ amt". I am new enough to R that I don't
understand whether formula is a general class or whether a formula
like this is specific to ggplot2. What I wanted to do was pass some
of this to the function and then have the function built the formula.
Your comment to baptiste's message about using aes_string rather than
aes() is what I needed to know, I think. I'd have an analogous issue
with cast(), and it looks like I could try cast_parse_formula for
that.

-- John Goldin
> From: hadley wickham <h.wick...@gmail.com>
> To: ggplot2 <ggp...@googlegroups.com>
> Sent: Saturday, 7 February, 2009 23:03:43
> Subject: Functions that create ggplot2 plots
>
> Hi all,
>
> I'm currently working on section for the ggplot2 book about writing
> functions that create ggplot2 plots.  I'm having a bit of a hard time
> thinking about what I should say (or even if it's useful enough to
> include in the book), so I was wondering if you'd written a function
> that creates ggplot2 plots, if you would mind sending it to me for
> some inspiration.
>
> Thanks!
>
> Hadley
>
> --http://had.co.nz/
>
>  testLayer.pdf
> 507KViewDownload
Reply all
Reply to author
Forward
0 new messages