Multiple graphs in one plot

517 views
Skip to first unread message

Sharon Dabach

unread,
Sep 2, 2015, 3:58:22 PM9/2/15
to Davis R Users' Group
Hi,
I have a data frame with multiple variables which I measured (n2o, wc, temp). - Attached
I want each graph to show one of these variables as a function of the distance and separated by treatment.
And I want to show the three graphs in one column (vertically) with the x-axis being the distance but only once at the bottom.
I also attached the code for making the n2o graph I want.
I tried the facets_grid but it doesn't work

Thanks for any advice
plot_waf.R
WAF1 March.rar

Brandon Hurr

unread,
Sep 2, 2015, 4:22:13 PM9/2/15
to davi...@googlegroups.com
Like this? 

waf <- structure(list(treat = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L), .Label = c("AGP", "HFLN", "PnF"
), class = "factor"), dist = c(0L, 75L, 150L, 225L, 300L, 0L, 
75L, 150L, 225L, 300L, 0L, 75L, 150L, 225L, 300L), n2o = c(3.37602672947042, 
0.560250603441995, 2.29055246201388, 2.29046459268188, 1.24776283713497, 
0.447905091642349, 0.495622885445164, 1.70959332470739, 1.41465268652102, 
0.981972629239121, 1.93191902222998, 2.01581254568787, 2.79872244912326, 
1.85986369783461, 1.10616943240497), wc = c(14.175, 14.275, 17.1, 
17.35, 16.75, 12.375, 11.15, 17.7, 15.025, 13.45, 14.85, 13.3, 
16.6, 15.2, 8.7), temp = c(20.05, 20.45, 20.525, 20.325, 20.475, 
19.975, 19.95, 20.1, 19.9, 20.175, 20.425, 20.65, 20.85, 20.875, 
21.025), n2osd = c(2.75117186826924, 0.559742275253513, 1.88883823242755, 
1.80468008039187, 0.893188468428236, 0.485503916219586, 0.469167600329146, 
1.13034644506959, 0.507864573134513, 1.19924537453657, 2.11144838831799, 
2.46409660756591, 2.00349564424744, 1.02911658546296, 0.314168128882309
), wcsd = c(2.36414184571626, 1.35246688191122, 1.99833263830958, 
1.28192563486863, 2.34449710314742, 1.80623918681884, 3.38476980999693, 
0.983192080250174, 3.97942625680302, 3.81007436497855, 2.63881286440197, 
3.0539591789457, 1.29871731591854, 4.65474668125631, 0.864098759787715
), tempsd = c(3.97869325281555, 3.85097390279394, 3.6754818278243, 
3.58457342882897, 3.69357550349252, 3.60959369828055, 2.37557010701291, 
2.14009345590327, 2.47251558808703, 2.85467452902545, 3.6609425015971, 
3.54635963583316, 3.6428011200174, 3.88876587107015, 3.86382798098811
), collar_area = c(15000L, 30000L, 30000L, 30000L, 19000L, 15000L, 
30000L, 30000L, 30000L, 19000L, 15000L, 30000L, 30000L, 30000L, 
19000L)), .Names = c("treat", "dist", "n2o", "wc", "temp", "n2osd", 
"wcsd", "tempsd", "collar_area"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", 
"14", "15"))

ggplot (waf, aes (dist, n2o, shape = treat)) + #x is distance from emitter, y is n2o flux and different shaps for the different treatments
    geom_point (size=4) + #size of symbols is 4 mm
    geom_errorbar (aes (ymin = n2o-n2osd, ymax = n2o+n2osd), width=5) +
    facet_grid(treat ~. )

--
Check out our R resources at http://www.noamross.net/davis-r-users-group.html
---
You received this message because you are subscribed to the Google Groups "Davis R Users' Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to davis-rug+...@googlegroups.com.
Visit this group at http://groups.google.com/group/davis-rug.
For more options, visit https://groups.google.com/d/optout.

Sharon Dabach

unread,
Sep 2, 2015, 4:53:53 PM9/2/15
to Davis R Users' Group
No.
I want to create 3 graphs:

ggplot (waf, aes (dist, n2o, shape = treat))
ggplot (waf, aes (dist, wc, shape = treat))
ggplot (waf, aes (dist, temp, shape = treat))
and place the in one column with the x-axis title being distance in the bottom of the column and each with it's own y-axis title.

Guillaume Théroux Rancourt

unread,
Sep 2, 2015, 5:16:02 PM9/2/15
to davi...@googlegroups.com
Hi Sharon,

You would need to melt your data:

waf.m = melt(waf, id=c('dist','treat'), measure=c('n2o','wc','temp'))
waf.sd = melt(waf, id=c('dist','treat'), measure=c('n2osd','wcsd','tempsd'))

waf. = cbind(waf.m,waf.sd[,'value']) #add only the sd to your data frame
colnames(waf.) = c(colnames(waf.m),'sd') 

ggplot (waf., aes(dist, value, ymin=(value-sd), ymax=(value+sd), shape=treat)) + 
geom_point (size=4) + 
geom_errorbar (width=5) +
facet_grid(variable ~. ,scale="free_y") 

Guillaume

Sharon Dabach

unread,
Sep 2, 2015, 5:48:46 PM9/2/15
to Davis R Users' Group
Thanks
It works but I can't add different text for the different y-axis.
Is there a way other than ggplot?

Erica Rettig

unread,
Sep 2, 2015, 6:10:32 PM9/2/15
to davi...@googlegroups.com
Pretty sure you can do this in base by splitting the window, turning off axis labels (and possibly adding the y-labels back in as freestanding text, i can't remember if you can turn off only one axis label), but ggplot is usually your best option for complicated and pretty things, at least in my experience. It's just a matter of playing with it for a bit (ok, like a day realistically) to figure out the exact parameters you need.

Brandon Hurr

unread,
Sep 2, 2015, 6:12:13 PM9/2/15
to davi...@googlegroups.com
Alignment is always tough, but you could plot each subplot separately and use gridExtra::grid.arrange to assemble everything. I'm unsure how you build a legend, but it is possible to extract it and reuse it. 

Alternatively, I wonder if you could get the spacing right to use grid or grid.arrange to just add labels. That might be possible, and programmatic. I'm unsure about parsing your expressions into labels, but this seems possible. 

These are a big step away from ggplot though and it's "ease". 

This gist is along those lines:


Michael Koontz

unread,
Sep 3, 2015, 2:13:59 AM9/3/15
to davi...@googlegroups.com
Hi Sharon,

Here’s a base R solution. I’m happy to help tweak it if it isn’t quite what you are looking for!

Mike

Dabach multi facet plot.R
PastedGraphic-1.tiff

Michael Koontz

unread,
Sep 3, 2015, 2:14:01 AM9/3/15
to davi...@googlegroups.com
Dabach multi facet plot.R
PastedGraphic-1.tiff

Joel Schwartz

unread,
Sep 3, 2015, 11:24:46 AM9/3/15
to Davis R Users' Group
By coincidence, I was just working on a similar ggplot alignment problem last week. Here's a ggplot option, though it requires alot of tweaking to get the alignment right. I've provided links to a couple of Stackoverflow answers that helped me with my alignment issues.

library(ggplot2)
library(grid)
library(gridExtra)

# Create the three plots. Keep legend only for one of them (we'll extract it later) and keep the x-axis values and title only for the last one. I used plot.margin in order to reduce the amount of vertical blank space between the three graphs once we lay them out with grid.arrange; adjust as needed.
p1=ggplot(waf, aes (dist, n2o, shape = treat)) +
  geom_point() +
  theme(legend.position="none",
        axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        plot.margin=unit(c(0.2,0,0,0),"line"))

p2=ggplot(waf, aes (dist, wc, shape = treat)) +
  geom_point() +
  theme(legend.position="none",
        axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        plot.margin=unit(c(0.2,0,0,0),"line"))

p3=ggplot(waf, aes (dist, temp, shape = treat)) +
  geom_point() +
  theme(plot.margin=unit(c(0.2,0,0,0),"line"))

## Extract legend

# Function to extract legend
# http://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot
g_legend<-function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  legend
}

# Extract legend from p3 then get rid of p3's legend
leg = g_legend(p3)
p3 = p3 + theme(legend.position="none")


## Left align graph edges (we need to do this because the y-values have different numbers of characters in different graphs)
# http://stackoverflow.com/a/13295880/496488
gp1 <- ggplotGrob(p1)
gp2 <- ggplotGrob(p2)
gp3 <- ggplotGrob(p3)

maxWidth = grid::unit.pmax(gp1$widths[2:5], gp2$widths[2:5], gp3$widths[2:5])

gp1$widths[2:5] <- as.list(maxWidth)
gp2$widths[2:5] <- as.list(maxWidth)
gp3$widths[2:5] <- as.list(maxWidth)

## Adjust heights of plot areas so that they are the same (we need to do this because the bottom plot gets slightly vertically compressed because it has x-axis labels and an x-axis title, that have to fit in the same vertical space as the other two plots)
maxHeight = grid::unit.pmax(gp1$heights[2:5], gp2$heights[2:5], gp3$heights[2:5])

gp1$heights[2:5] <- as.list(maxHeight)
gp2$heights[2:5] <- as.list(maxHeight)
gp3$heights[2:5] <- as.list(maxHeight)

## Arrange the three graphs plus the legend
grid.arrange(
  arrangeGrob(gp1,gp2,gp3, ncol=1),
  arrangeGrob(leg, ncol=1),
  widths=c(0.8, 0.2), ncol=2)

Sharon Dabach

unread,
Sep 3, 2015, 12:42:18 PM9/3/15
to Davis R Users' Group
Thanks for the great help.
Playing with all of it now to see which one works best for me.
I didn't realize it was possible with base R 

Noam Ross

unread,
Sep 3, 2015, 12:54:09 PM9/3/15
to Davis R Users' Group

One quick addendum: The newish cowplot package is a really helpful add-on to ggplot for arranging multiple plots, annotating them, and doing things like axis alignment. It has a number of helpful vignettes: https://cran.rstudio.com/web/packages/cowplot/index.html


Daniel Fulop

unread,
Sep 3, 2015, 1:01:45 PM9/3/15
to davi...@googlegroups.com
I totally agree with Noam.  cowplot is great for easily and rapidly getting a whole set of ggplots into a a final multi-panel figure.
--
Daniel Fulop, Ph.D.
Postdoctoral Scholar
Dept. Plant Biology, UC Davis
Maloof Lab, Rm. 2220
Life Sciences Addition, One Shields Ave.
Davis, CA 95616

510-253-7462
dfu...@ucdavis.edu

Brandon Hurr

unread,
Sep 3, 2015, 1:06:02 PM9/3/15
to davi...@googlegroups.com
It's disappointing some of those features are part of ggplot2 itself, perhaps Hadley is working on that with the new version, but I've not seen anything in the development version that suggests that is the case. 

ggplot2 is great for simple things, but like many parts of the Hadleyverse, if you want to do something more complex, the difficulty ramps up very quickly. I've started using standard evaluation with dplyr sometimes and it's a PITA to switch between NSE and SE (mentally). 

Sharon Dabach

unread,
Sep 3, 2015, 5:07:43 PM9/3/15
to Davis R Users' Group
I like this cowplot, he's right about the clean appearance of plots and having to take elements away from the ggplot

Sharon Dabach

unread,
Sep 4, 2015, 7:13:58 PM9/4/15
to Davis R Users' Group
So I edited the code to arrange the graphs as Joel suggested (code attached)
But for some reason the y-axis legend is not aligned, and the y-axis of the third plot is not aligned.
I looked at the stackoverflow post but couldn't find any solution/
Any suggestions?

plot_waf.R

Joel Schwartz

unread,
Sep 4, 2015, 9:58:07 PM9/4/15
to Davis R Users' Group
Sharon,

I looked at your code and it looks like "theme(plot.margin = unit(c(0.2,0,0,0),"line"))" is missing from the third plot (p3). That took care of the y-axis alignment issue with the third plot. I didn't see any issue with the legend, except that it was a bit too close to the plots. You can fix this be giving the legend more space in the widths argument to grid.arrange.

Joel

Joel Schwartz

unread,
Sep 4, 2015, 10:10:38 PM9/4/15
to Davis R Users' Group
One more thing I forgot to mention: When I ran your original code, I got the following error: " Error in FUN(X[[i]], ...) : Theme element 'text' has NULL property: margin". I'm not sure what's causing the error, but it goes away if I don't load cowplot. Could the legend issue you're seeing be associated with cowplot? It didn't seem like cowplot was necessary for the function to work properly.

Joel

Sharon Dabach

unread,
Sep 8, 2015, 6:11:29 PM9/8/15
to Davis R Users' Group
Thanks, I missed the plot margin completely.
I use the cowplot because of the default plot, it's what I like to use.
But you're right when I don't load it the y-axis legend appears fine.
Reply all
Reply to author
Forward
0 new messages