I am having problems with a vanishing legend in ggpairs.
I add a legend inside the top part of a lower triangular ggpairs plot as follows.
First I create a ggpairs plot without legend then I strip the legend I want from and ad hoc graph and place in the ggpairs plot it with putPlot. It works nicely until I try to modify the theme which makes the legend disappear.
# 1 produce graph without legend
library(GGally)
library(ggplot2)
plotwithoutlegend <-ggpairs(
iris,
columns=1:4,
switch="both",
upper="blank",
mapping=aes(color = Species,
shape= Species,
fill=Species,
alpha=0.5)
)
#2 grab the legend from a graph with the legend I want (without alpha).
auxplot <- ggplot(iris, aes(x=Petal.Length, y=Petal.Width,
color=Species,
shape=Species,
fill=Species)) + geom_point()
mylegend <- grab_legend(auxplot)
# 3 place the legend in the ggpairs grid with putPlot
graph1 <- putPlot(plotwithoutlegend,mylegend,3,4)
show(graph1)
This produces a graph with the legend in the desired place.
ggpairs graph with legend before changing theme:
However if I change some aspect of the theme the legend disappears.
graph2 <- graph1 +theme(strip.background =element_blank(), strip.placement = "outside")
show(graph2)
Legend vanishes after changing theme:
I had similar issue. I think you need to use library(grid). see my solution.
# plotwithoutlegend
plotwithoutlegend <- ggpairs(
iris,
columns=1:4,
switch="both",
upper="blank",
mapping=aes(color = Species,
shape= Species,
fill=Species,
alpha=0.5)
)+
theme(strip.background =element_blank(), strip.placement = "outside")
#2 grab the legend from a graph with the legend I want (without alpha).
auxplot <- ggplot(iris, aes(x=Petal.Length, y=Petal.Width,
color=Species,
shape=Species,
fill=Species)) + geom_point()
mylegend <- grab_legend(auxplot)
##### plot legend with plot
grid.newpage()
grid.draw(plotwithoutlegend)
vp = viewport(x=.9, y=.75, width=.35, height=.3) ## control legend position
pushViewport(vp)
grid.draw(mylegend)
upViewport()
Learned here: Legend using ggpairs
# first run your code until
graph2 <- graph1 +theme(strip.background =element_blank(), strip.placement = "outside")
# then run this code
colidx <- c(3,5,6,7)
for (i in 1:length(colidx)) {
# Address only the diagonal elements
# Get plot out of plot-matrix
inner <- getPlot(graph2, i, i);
# Add ggplot2 settings (here we remove gridlines)
inner <- inner + theme(panel.grid = element_blank()) +
theme(axis.text.x = element_blank())
# Put it back into the plot-matrix
graph2 <- putPlot(graph2, inner, i, i)
for (j in 1:length(colidx)){
if((i==1 & j==1)){
# Move the upper-left legend to the far right of the plot
inner <- getPlot(graph2, i, j)
inner <- inner + theme(legend.position=c(length(colidx)-0.25,0.50))
graph2 <- putPlot(graph2, inner, i, j)
}
else{
# Delete the other legends
inner <- getPlot(graph2, i, j)
inner <- inner + theme(legend.position="none")
graph2 <- putPlot(graph2, inner, i, j)
}
}
}
# then run this code
show(graph2)
Related
I'd like to vertically arrange my stacked geom_bar objects and display them with unbroken vertical lines (see concept below) and a single set of axes and legend. I'm using plot_grid now but should perhaps be using facet wrapping? I'm unsure whether that would allow me to place vertical lines. The code that generates my current plot is here.
my concept:
my current plot:
You could create your plots and disable the axis text, line and ticks. Then make the axis titles match the background color so they are not visible (but retain the same graph dimensions) and plot them with plot_grid() as you are doing. Then overlay a full sized plot with zero data, the axis titles and vertical lines over the top of it using draw_plot(). For the single legend, leverage the following SO answer:
Align multiple plots in ggplot2 when some have legends and others don't
The code:
#!/usr/bin/env Rscript
if (!require("pacman")) install.packages("pacman")
pacman::p_load(ggplot2, cowplot)
### Create some garbage data to plot
d0 <- data.frame(foo=c(0,0,0,0,0),bar=c("SX_RUNNYNOSE","SX_COUGH","SX_HEADACHE","SX_MALAISE","SX_MYALGIA"))
d1 <- data.frame(foo=c(1,2,3,4,5),bar=c("SX_RUNNYNOSE","SX_COUGH","SX_HEADACHE","SX_MALAISE","SX_MYALGIA"))
### Create a plot with 0 data but having the axis titles and vertical lines
p0 <- ggplot(d0, aes(x=seq(1,5), y=foo, fill=bar)) +
geom_bar(stat="identity") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.line.x=element_blank(),
axis.line.y=element_blank(),
axis.ticks.x=element_blank(),
axis.ticks.y=element_blank()
) +
theme(legend.position = "none") +
geom_segment(aes(x=2, y=0, xend=2, yend=4.9), color='red') +
geom_text(aes(x=2, y=max(d1$foo), label="T0")) +
geom_segment(aes(x=3, y=0, xend=3, yend=4.9), color='red') +
geom_text(aes(x=3, y=max(d1$foo), label="T24")) +
labs(y="Continued Symptom Count Among Samples", x="Time Elapsed Since Viral Challenge")
### A bar pot with the sample data and only the bars (no axis, etc)
### Make color of axis titles white to match the background color so they are not visible
p1 <- ggplot(d1, aes(x=seq(1,5), y=foo, fill=bar)) +
geom_bar(stat="identity") +
theme(axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.line.x=element_blank(),
axis.line.y=element_blank(),
axis.ticks.x=element_blank(),
axis.ticks.y=element_blank(),
axis.title.x = element_text(colour = "white"),
axis.title.y = element_text(colour = "white")
) +
theme(legend.title=element_blank())
### Arrange bar plots and legends in a grid and use draw_plot to
### overlay the single axis titles and vertical bars across all
### plots
g <- plot_grid(
plot_grid(
p1 + theme(legend.position = "none")
, p1 + theme(legend.position = "none")
, p1 + theme(legend.position = "none")
, ncol = 1
, align = "h"
, labels=c("Rhinovirus", "H3N2", "H1N1")
, hjust=c(-0.5,-1,-1)) +
draw_plot(p0, 0, 0, 1, 1, 1)
, plot_grid(
ggplot()
, get_legend(p1)
, ggplot()
, ncol =1)
, rel_widths = c(9,3)
)
g
The result:
I want to combine these two graphs :
p1 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
facet_wrap(~ Species)
p2 <- ggplot(iris, aes(Sepal.Length)) +
geom_density()
To combine, I do :
multiplot(p1, p2, cols = 2)
But it is not the desired shape.
I would like the graph p2 has the same dimensions than others and is situated just next to the last faceted graph.
Thanks for help
Not sure if this is applicable in you generic case, but with facet_grid instead of facet_wrap, you can use the margins argument:
library(ggplot2)
ggplot(iris, aes(Sepal.Length)) +
geom_density() +
facet_grid(. ~ Species, margins = T)
If you question is more generic the answer probably lies in grid.arrange.
Something like this could be a start:
library(gridExtra)
grid.arrange(arrangeGrob(p1, p2,
widths = c(3,1),
heights = c(1,20),
layout_matrix = matrix(c(1,1,NA,2),2)))
As you can see there are several problems (different axes, top strip), but working with grid could gets complicated quickly.
This code should work:
p1 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
ylim(limits = c(0, 1.25))+
facet_wrap(~ Species)
p2 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
ggtitle("") + # ad empty title as place holder
labs(y = "", x = "") + # hide axis labels
ylim(limits = c(0, 1.25)) + # y axis values should be fixed in both plots
coord_fixed(ratio=20/1) + # ratio of x- and y-axis to reduce width of plot
theme(axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.line.y = element_blank(),
plot.margin=unit(c(0,0,0.65,-10), "lines")) # margin between plots = "0.65"
I fiddled a bit and used just different styling options to produce this result. If you have more plots than this one I would recommend to use one theme for all.
You can use either the multiplot function that you are already using
multiplot(p1, p2, cols = 2)
or you install the packages gridExtra and grid and use that one:
grid.arrange(p1, p2, ncol=2)
Hope this helps!
I'm generating a simple chart:
data(iris); require(ggthemes)
ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width)) +
geom_point(aes(shape=Species, colour=Petal.Width)) +
scale_colour_gradient() +
theme_gdocs() +
labs(shape="Species label", colour="Petal width label")
I would like to draw a common border across those two legends:
Obviously the code theme(legend.background = element_rect(colour = 'black')) will generate two borders, each for each legend element.
Edit
As of version 2.2.0, ggplot allows a border for each individual legend (legend.background), and a border for the combined legend (legend.box.background). Set the legend.box.background to desired colors, fills, sizes, etc. But also set legend.background to element_blank().
ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width)) +
geom_point(aes(shape=Species, colour=Petal.Width)) +
scale_colour_gradient() +
theme_gdocs() +
labs(shape="Species label", colour="Petal width label") +
theme(legend.background = element_blank(),
legend.box.background = element_rect(size = 2))
You probably need to delve into the structure of the ggplot grob; something like this:
Minor edit: Updating to ggplot2 2.0.0 (and ggthemes 3.0.0)
# Load packages and data
library(ggplot2)
library(gtable)
library(grid)
data(iris)
# Small problem with theme_gdocs drawing a border around each legend.
# Fixed in the github development version
# library(devtools)
# install_github("jrnold/ggthemes")
library(ggthemes)
p <- ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width)) +
geom_point(aes(shape=Species, colour=Petal.Width)) +
scale_colour_gradient() +
theme_gdocs() +
labs(shape="Species label", colour="Petal width label")
# Get the ggplot grob
gt = ggplotGrob(p)
# Get the combined legend
leg = gtable_filter(gt, "guide-box")
# Get the individual legends - to get their widths and heights
leg1 = leg$grobs[[1]]$grobs[[1]]
leg2 = leg$grobs[[1]]$grobs[[2]]
# Calculate widths and heights for border (Note: some margin widths are dropped from widths)
rectHeight = sum(leg1$heights + leg2$heights)
rectWidth = sum(unit.pmax(leg1$widths[3:5], leg2$widths[3:5]))
# Create border with widths and heights
rect <- rectGrob( width = rectWidth, height = rectHeight,
gp = gpar(col = "black", fill = NA, lwd = 5), name = "gRect")
# Create combined legend - i.e., legend plus border
leg <- grobTree(leg, rect)
# Insert combined legend back into ggplot grob
gt$grobs[gt$layout$name == "guide-box"][[1]] <- leg
# Draw it
grid.newpage()
grid.draw(gt)
I am creating a graphic using facet_grid to facet a categorical variable on the y-axis. I decided not to use facet_wrap because I need space = 'free' and labeller = label_parsed. My labels are long and I have a legend on the right so I would like to move the labels from the right of the panel to the top of the panel.
Here is an example to show where I'm getting stuck.
library(ggplot2)
library(gtable)
mt <- ggplot(mpg, aes(x = cty, y = model)) + geom_point() +
facet_grid(manufacturer ~ ., scales = 'free', space = 'free') +
theme_minimal() +
theme(panel.margin = unit(0.5, 'lines'), strip.text.y = element_text(angle = 0))
Now I would like to move the strip text from the right of each panel to the top of each panel. I can store the grobs for the strip labels and remove them from the plot:
grob <- ggplotGrob(mt)
strips.y <- gtable_filter(grob, 'strip-right')
grob2 <- grob[,-5]
But now I'm stuck when it comes to rbind-ing the grobs back so the labels go to the top of the panels.
Another possible solution would be to use facet_wrap and then re-size the panels as discussed in another question, but in that case I would have to manually change the labels on the facets because there is no labeller = label_parsed for facet_wrap.
I'd appreciate suggestions on either approach!
Thanks for reading,
Tom
This takes your first approach. It inserts a row above each of the panels, grabs the strip grobs (on the right), and inserts them into the new rows.
library(ggplot2)
library(gtable)
library(grid)
mt <- ggplot(mpg, aes(x = cty, y = model)) + geom_point() +
facet_grid(manufacturer ~ ., scales = 'free', space = 'free') +
theme(panel.spacing = unit(0.5, 'lines'),
strip.text.y = element_text(angle = 0))
# Get the gtable
gt <- ggplotGrob(mt)
# Get the position of the panels in the layout
panels <-c(subset(gt$layout, grepl("panel", gt$layout$name), se=t:r))
# Add a row above each panel
for(i in rev(panels$t-1)) gt = gtable_add_rows(gt, unit(.5, "lines"), i)
# Get the positions of the panels and the strips in the revised layout
panels <-c(subset(gt$layout, grepl("panel", gt$layout$name), se=t:r))
strips <- c(subset(gt$layout, grepl("strip-r", gt$layout$name), se=t:r))
# Get the strip grobs
stripText = gtable_filter(gt, "strip-r")
# Insert the strip grobs into the new rows
for(i in 1:length(strips$t)) gt = gtable_add_grob(gt, stripText$grobs[[i]]$grobs[[1]], t=panels$t[i]-1, l=4)
# Remove the old strips
gt = gt[,-5]
# For this plot - adjust the heights of the strips and the empty row above the strips
for(i in panels$t) {
gt$heights[i-1] = unit(0.8, "lines")
gt$heights[i-2] = unit(0.2, "lines")
}
# Draw it
grid.newpage()
grid.draw(gt)
OR, you can achieve the second approach using a facet_wrap_labeller function available from here.
library(ggplot2)
library(gtable)
mt <- ggplot(mpg, aes(x = cty, y = model)) + geom_point() +
facet_wrap(~ manufacturer, scales = "free_y", ncol = 1) +
theme(panel.margin = unit(0.2, 'lines'))
facet_wrap_labeller <- function(gg.plot, labels=NULL) {
require(gridExtra)
g <- ggplotGrob(gg.plot)
gg <- g$grobs
strips <- grep("strip_t", names(gg))
for(ii in seq_along(labels)) {
modgrob <- getGrob(gg[[strips[ii]]], "strip.text",
grep=TRUE, global=TRUE)
gg[[strips[ii]]]$children[[modgrob$name]] <- editGrob(modgrob,label=labels[ii])
}
g$grobs <- gg
class(g) = c("arrange", "ggplot",class(g))
return(g)
}
## Number of y breaks in each panel
g <- ggplot_build(mt)
N <- sapply(lapply(g$panel$ranges, "[[", "y.major"), length)
# Some arbitrary strip texts
StripTexts = expression(gamma[1], sqrt(gamma[2]), C, `A really incredibly very very very long label`, gamma[5], alpha[1], alpha[2], `Land Rover`, alpha[1], beta[2], gamma^2, delta^2, epsilon[2], zeta[3], eta[4] )
# Apply the facet_wrap_labeller function
gt = facet_wrap_labeller(mt, StripTexts)
# Get the position of the panels in the layout
panels <- gt$layout$t[grepl("panel", gt$layout$name)]
# Replace the default panel heights with relative heights
gt$heights[panels] <- lapply(N, unit, "null")
# Draw it
gt
I was struggling with a similar problem but putting the labels on the bottom. I've used a code adaptation of this answer. And recently found that
ggplot2 ver.2.2.1.0 (http://docs.ggplot2.org/current/facet_grid.html)
~facet_grid(.~variable,switch='x')
option which has worked beautifully for me.
I have the following dual plot (from another SO question):
Here's the code that generates the plot:
library(ggplot2)
library(gtable)
df <- data.frame(x=c(5,2,7,3),
y=c("asdasxfqwe","a","b","c"),
facet=c(1,1,2,2))
# First plot (a bit of extra space between facets)
p <- ggplot(df, aes(x, y)) + facet_grid(~facet) +
geom_point() +
theme(panel.margin = unit(4, "lines"),
axis.text.y = element_text( hjust=0.5))
# get y-axis labels
g <- ggplotGrob(p)
axis <- gtable_filter(g, "axis-l")[["grobs"]][[1]][["children"]][["axis"]][,1]
# remove axis
g[["grobs"]][[4]][["children"]][["axis"]] <- NULL
# build plot & add axis to LHS of left facet
panels <- subset(g$layout, name == "panel")
g <- gtable_add_grob(g, grobs=axis, t = unique(panels$t),
l=tail(panels$l, -1)-1)
grid.newpage()
grid.draw(g)
As I understand, the empty space on the left is where the y-axis text used to be before it was moved using the gtable code. How to get rid of this empty space?
Upgraded comment:
Since you're editing the gtable, you can set the relevant width to something smaller,
g[["widths"]][3] <- list(unit(1, "line"))