Hi:
This situation is not common, but it's not exactly rare, either. In
situations like this, one typically leans on one or more of grid,
gtable and gridExtra to perform the necessary surgery. Your case
entails two adjustments:
(i) alignment of the plot regions so that the widths of each are the
same when stacked;
(ii) a single legend that should be positioned intermediate to the two plots.
The conventional way to go about this is to create a gtable object and
use that information to modify the graphics page. The approach below
plots ozone and wind separately, so starts with two melt() operations
that stack the observed and fitted responses. (You could also use
dplyr with tidyr::gather() as a replacement for melt().)
library(reshape2)
library(ggplot2)
library(grid)
library(gridExtra)
# Your initial data transformations - built-in data sets should always
# be left in their original state
airq <- transform(airquality, Ozone.model = Ozone +
rnorm(length(Ozone), 2),
Wind.model = Wind + rnorm(length(Wind)))
## Add time, see ?airquality
airq$dtm <- as.Date(paste("1973", airquality$Month, airquality$Day,
sep = "-"), format="%Y-%m-%d", tz="EST")
# Perform separate melt operations on ozone and wind
# and create a new variable type whose labels match the
# desired ones in the (common) legend
aq_ozone <- melt(airq, id.var = "dtm",
measure.var = c("Ozone", "Ozone.model"))
aq_ozone$type <- factor(aq_ozone$variable, labels = c("Measured", "Modeled"))
aq_wind <- melt(airq, id.var = "dtm",
measure.var = c("Wind", "Wind.model"))
aq_wind$type <- factor(aq_wind$variable, labels = c("Measured", "Modeled"))
######## Generate the two plots separately #########
# Get rid of the x-axis info in the top plot
A <- ggplot(aq_ozone, aes(x = dtm, y = value, color = type)) +
geom_line() + labs(x = NULL, y = "Ozone") +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank())
# Get a gtable of the grobs from A and extract the legend grob
gA <- ggplot_gtable(ggplot_build(A))
# Function to extract a legend from a gtable - adapted from
# http://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot/12539820#12539820
g_legend <- function(gt){
leg <- which(sapply(gt$grobs, function(x) x$name) == "guide-box")
gt$grobs[[leg]]
}
# Grab the legend box as a grob
legend <- g_legend(gA)
# Redo A to get rid of the legend in the final plot
A <- A + theme(legend.position = "none")
# Remove the default top margin spacing in the bottom plot
B <- ggplot(aq_wind, aes(x = dtm, y = value, color = type)) +
geom_line() + labs(y = "Wind") +
theme(plot.margin = unit(c(0, 1, 0.5, 0.5), "lines"))
#############
# Generate gtables in order to align the plot regions
gA <- ggplot_gtable(ggplot_build(A))
gB <- ggplot_gtable(ggplot_build(B))
########## Align the widths of the two plot regions ########
# Determine the maximum width of A and B
maxWidth = grid::unit.pmax(gA$widths[2:3], gB$widths[2:3])
# Reassign widths to maxWidth in each of gA and gB as lists
gA$widths[2:3] <- as.list(maxWidth)
gB$widths[2:3] <- as.list(maxWidth)
#################
# Generate a layout matrix for positioning the grobs
# Four 1's and 2's means that we want 80% of the width in
# each row to be devoted to the plots
lay <- rbind(c(1, 1, 1, 1, 3), c(2, 2, 2, 2, 3))
## use the grid.arrange() function from gridExtra
## to compose the final graphic
grid.arrange(gA, gB, legend, layout_matrix = lay)
Normally you can add to a ggplot in grid.arrange(), but in this case
we modified the gtables of A and B to align the horizontal plot
widths, so we need to use the gtables as the arguments in
grid.arrange() in this example. This is why I edited A after its
legend grob was extracted and redid its gtable. (If there's a more
efficient method, I'm happy to hear about it.)
To condense the (admittedly verbose) code, one could write a function
that takes the input data and response variable of interest as
arguments, along with a flag to keep or remove the legend, such that
it returns the result of ggplot_gtable(ggplot_build(plot)) as its
output, using the intermediate steps as its body.
Dennis
Post by BrianDear list,
a considerable limitation of ggplot2 in meteorological analysis for me
is the lack of ability to plot concurrent time series of different
library(reshape2)
## simulate a model study
airquality <- transform(airquality, Ozone.model = Ozone +
rnorm(length(Ozone), 2),
Wind.model = Wind + rnorm(length(Wind)))
## Add time, see ?airquality
airquality$dtm <- as.Date(paste("1973", airquality$Month, airquality$Day,
sep = "-"), format="%Y-%m-%d", tz="EST")
d.f <- melt(airquality, id.vars = "dtm", measure.vars = c("Wind",
"Wind.model", "Ozone", "Ozone.model"))
d.f$type <- ifelse(grepl("model", d.f$variable), "Modeled", "Measured")
d.f$Variables <- sub("\\.model", "", d.f$variable)
## The problem
(box <- ggplot(d.f, aes(dtm, value, color=type)) +
geom_line() +
facet_grid(Variables~., scales="free_y"))
## A solution, please note that =ggsave= will no longer work
move_facet_strip_y <- function(object, sane.defaults = TRUE)
{
http://stackoverflow.com/questions/23497682/alter-facet-grid-output-in-ggplot2
if (sane.defaults)
{
object <- object +
theme(strip.text.y = element_text(angle=90)) +
labs(y = "")
}
g <- ggplotGrob(object)
## Find bordering elements
adjustment.index <- g$layout[, "r"] == unique(g$layout[g$layout$name
== "strip-right", "r"]) - 1
## Pull them over
g$layout[adjustment.index, "r"] <- g$layout[adjustment.index , "r"] + 1
## Push panels over
g$layout[g$layout$name == "strip-right", c("l", "r")] <- 2
## change colors to sane defaults
if (sane.defaults)
{
index <- which(unlist(lapply(sapply(g$grobs, "[[", "childrenOrder"),
function(x)
any(grepl(pattern="strip.text.y", x)))))
## loop over panels
for(i in index) {
place <- grep("strip.background.rect",
g$grobs[[i]]$childrenOrder)
g$grobs[[i]]$children[[place]][["gp"]][["col"]] <- "transparent"
g$grobs[[i]]$children[[place]][["gp"]][["fill"]] <-
"transparent"
}
}
class(g) <- c("crap", class(g))
g
}
grid.draw(move_facet_strip_y(box))
It is a brutish fix to allow plotting of time series stacked on top of each
other. It will work for more than just time series. Probably violates
a few principles
of the grammar of graphics and usually requires the violation of tidy data
principles. It helps me immensely.
I hope that helps somebody else too.
Cheers,
Brian
--
--
You received this message because you are subscribed to the ggplot2 mailing list.
Please provide a reproducible example: https://github.com/hadley/devtools/wiki/Reproducibility
More options: http://groups.google.com/group/ggplot2
---
You received this message because you are subscribed to the Google Groups "ggplot2" group.
For more options, visit https://groups.google.com/d/optout.
--
--
You received this message because you are subscribed to the ggplot2 mailing list.
Please provide a reproducible example: https://github.com/hadley/devtools/wiki/Reproducibility
To post: email ***@googlegroups.com
To unsubscribe: email ggplot2+***@googlegroups.com
More options: http://groups.google.com/group/ggplot2
---
You received this message because you are subscribed to the Google Groups "ggplot2" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ggplot2+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.