How to change the format of an individual facet_wrap panel? - r

Is it possible to alter the format of an individual facet plot? For example, using the sample code below, can one change the color of the title or background for the cyl=8 plot?
library(ggplot2)
ggplot(mtcars, aes(x=gear)) +
geom_bar(aes(y=gear), stat="identity", position="dodge") +
facet_wrap(~cyl)

You can modify the ggplot2 grobs, for instance:
library("ggplot2")
d <- ggplot(mtcars, aes(x=gear)) +
geom_bar(aes(y=gear), stat="identity", position="dodge") +
facet_wrap(~cyl)
grob <- ggplotGrob(d)
strip_bg <- grid.ls(getGrob(grob, "strip.background.rect",
grep=TRUE, global=TRUE))$name
panel_bg <- grid.ls(getGrob(grob, "panel.background.rect",
grep=TRUE, global=TRUE))$name
strip_text <- grid.ls(getGrob(grob, "strip.text.x",
grep=TRUE, global=TRUE))$name
grob <- geditGrob(grob, strip_bg[2], gp=gpar(fill="gray60"))
grob <- geditGrob(grob, panel_bg[2], gp=gpar(fill="darkolivegreen2"))
grob <- geditGrob(grob, strip_text[2], gp=gpar(col="white"))
grid.draw(grob)
Update: This should work with ggplot2 0.9.3
grob <- ggplotGrob(d)
elem <- grob$grobs$panel2
panel_bg <- grid.ls(getGrob(elem, "panel.background.rect", grep=TRUE))$name
grob$grobs$panel2 <- editGrob(elem, panel_bg, gp=gpar(fill="darkolivegreen"), grep=TRUE)
elem <- grob$grobs$strip_t.1
strip_bg <- grid.ls(getGrob(elem, "strip.background.rect", grep=TRUE))$name
grob$grobs$strip_t.1 <- editGrob(elem, strip_bg, gp=gpar(fill="gray60"), grep=TRUE)
elem <- grob$grobs$strip_t.1
strip_text <- grid.ls(getGrob(elem, "strip.text.x.text", grep=TRUE))$name
grob$grobs$strip_t.1 <- editGrob(elem, strip_text, gp=gpar(col="white"), grep=TRUE)
grid.draw(grob)

I know this is an old question so the original poster is probably long gone but I still think it is worth answering as a resource to future searchers. The accepted answer from rcs does work, but I found it to be rather unstable and hacky. In the end I decided that a more modest but more stable approach is in order. With this method you can only change the background but that suffices for my purposes and might for others, my approach is to use geom_rect to recolour the background, like so:
highlights <- data.frame(cyl=c(8))
ggplot() +
geom_rect(data=highlights,aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='red', alpha=0.2) +
geom_bar(data = mtcars, aes(x=gear), position="dodge", fill = 'black') +
facet_wrap(~cyl)

This might help you get a little bit closer to what you want:
mtcars2 = subset(mtcars, cyl != 8)
subs = subset(mtcars, cyl == 8)
require(ggplot2)
ggplot(mtcars2, aes(x=gear)) +
geom_bar(aes(y=gear, fill = 'black'), stat="identity", position="dodge") +
geom_bar(data = subs, aes(x = gear), fill = 'blue', binwidth = 1) +
facet_wrap(~cyl)

Related

Control size of subplots created with patchwork - code improvement

I want to make a multipanel figure containing multiple labeled plots. My plots are produced in ggplot2 & I would like to arrange them with patchwork.
I want to combine two subplots in the first row, followed by two other plots arranged one plot per row:
plot3 + plot4 - both in row #1
plot1 - in row #2
plot2 - in row #3
Here is a dummy example to illustrate the problem:
```{r, fig.width=10, fig.height=13}
library(ggplot2)
library(patchwork)
#Dummy plots
plot1 <- ggplot2::ggplot(data = mpg, aes(x = class, fill=drv)) +
geom_bar(aes(y = ..count..)) + ggplot2::ggtitle("Plot1")
plot2 <- ggplot2::ggplot(data = mpg, aes(x = displ, y = hwy, color=class)) +
geom_point()+ ggplot2::ggtitle("Plot2")
plot3 <- ggplot2::ggplot(data = mpg, aes(x = cty)) +
geom_density()+ ggplot2::ggtitle("Plot3")
plot4 <- ggplot2::ggplot(data = mpg, aes(x = cty, y=drv, fill = fl)) +
geom_col()+ ggplot2::ggtitle("Plot4")
# this works, but it is not the desired layout
final <- plot1 + plot2 + {plot3 + plot4 + patchwork::plot_layout(ncol=2)} +
patchwork::plot_layout(ncol=1,heights = unit(c(4, 7, 2),c('cm')))
plot(final)
#this does not work
final2 <- {plot3 + plot4 + patchwork::plot_layout(ncol=2)} + plot1 + plot2 +
patchwork::plot_layout(ncol=1, heights = unit(c(2, 4, 7),c('cm')))
print(final2)
```
This is the output I can produce, but this is not what I want:
And this is the picture I would like to obtain:
Some of my other attempts:
#this does not work either
up_patch <- plot3 + plot4 + patchwork::plot_layout(ncol=2)
final2 <- up_patch + plot1 + plot2 + patchwork::plot_layout(ncol=1, heights = unit(c(2, 4, 7),c('cm')))
print(final2)
#and this as well
up_patch <- plot3 + plot4 + patchwork::plot_layout(ncol=2, heights= unit(2,c('cm')))
bottom_patch <- plot1 + plot2 + patchwork::plot_layout(ncol=1, heights = unit(c(4, 7),c('cm')))
final2 <- up_patch + bottom_patch
print(final2)
# THIS WORKS but needs improvement
final_desired <- (plot3 | plot4) / plot1 + plot2
print(final_desired)
In the last attempt I was able to produce the desired layout, however I would like to be able to control the dimensions of the subplots as in my dummy example in the beginning of this post). It is important for me to adjust the image size to the size of the page.
I would also like to know how to use a namespace qualifier while calling patchwork in the working example, so I would not call a function from another package by an accident.
I followed the instructions from these sources:
https://ggplot2-book.org/arranging-plots.html
https://patchwork.data-imaginist.com/articles/guides/layout.html - (footnote: I do not understand the textual representation of layout)
Combine multiple patchworks
One option would be to use the design argument to specify the layout:
library(ggplot2)
library(patchwork)
design = "
CD
AA
BB
"
plot1 + plot2 + plot3 + plot4 +
plot_layout(
design = design,
heights = c(2, 4, 7))

R ggplot2 geom_ribbon: shade/coloring area bounded by two crossing lines on the sides when no line is below and no line is above

Problem: I want to color betwen the lines
Trick title, I know. I have three lines which look like this
I want to shade/color the area between the green and the red one. Basically the red and the green lines represent boundaries of some sort of confidence interval for the blue line. I can only colour half of it for now, and that's because the blue is below the green before intersection, and the green is below the blue after intersection. However, I cannot color the other half because the blue and the red one are divergent.
Is there a simple way to color between two almost vertical lines like this?
I tried geom_ribbon, but didn't get anywhere. Here is my current code, coloring only half.
Minimal Working Example
library(ggplot2)
# Lines to plot
blue_line <- function(x) 28.6*x - 51
red_line <- function(x) -16*x + 28
green_line <- function(x) 5.5*x-10
# Data to plot
x <- seq(from=1, to=4, length.out=200)
df <- data.frame(x=x, yblue=blue_line(x), yred=red_line(x), ygreen=green_line(x))
ggplot(data=df, aes(x=x)) +
geom_ribbon(aes(x=x, ymin=ygreen, ymax=yblue), fill="grey80") +
geom_line(aes(x=x, y=yblue), color="blue") +
geom_line(aes(x=x, y=yred), color="red") +
geom_line(aes(x=x, y=ygreen), color="green") +
coord_cartesian(xlim=c(-3.5, 8), ylim=c(-4, 12))
which produces
Second Minimal Working Example
This code works, however notice how tedious, repetitive and just plain stupid it is. Hopefully there's a much better way.
library(ggplot2)
# Lines to plot
blue_line <- function(x) 28.6*x - 51
red_line <- function(x) -16*x + 28
green_line <- function(x) 5.5*x-10
# Data to plot
x <- seq(from=0.9, to=4.2, length.out=200)
# Below
x_below <- seq(from=0.9, to=2.06, length.out=200)
y_below <- rep(-5, 200)
y_below_above <- c(green_line(x_below[x_below <= 1.76744]), red_line(x_below[x_below > 1.76744]))
# Above
x_above <- seq(from=-2.5625, to=4.181818, length.out=200)
y_above <- rep(13, 200)
y_above_below <- c(red_line(x_above[x_above<=1.76744]), green_line(x_above[x_above>1.76744]))
df <- data.frame(x=x, yblue=blue_line(x), yred=red_line(x), ygreen=green_line(x),
ybelow=y_below, xbelow=x_below, y_below_above=y_below_above,
xabove=x_above, yabove=y_above, y_above_below=y_above_below)
ggplot(data=df, aes(x=x)) +
geom_ribbon(aes(x=xbelow, ymin=ybelow, ymax=y_below_above), fill="grey80") +
geom_ribbon(aes(x=xabove, ymin=y_above_below, ymax=yabove), fill="grey80") +
geom_line(aes(x=x, y=yblue), color="blue") +
geom_line(aes(x=x, y=yred), color="red") +
geom_line(aes(x=x, y=ygreen), color="green") +
coord_cartesian(xlim=c(-3.5, 8), ylim=c(-4, 12))
producing the desired
Besides your example with data manipulation, I am not aware of how to fill using geom_ribbon from xmin to xmax without coord_flip as mentioned here.
However you can use geom_polygon to create a filled region between two lines as follows:
poly_df <- rbind(setNames(df[, c(1,3)],c('x','y')),
setNames(df[, c(1,4)],c('x','y')))
ggplot(data=df, aes(x=x)) +
geom_line(aes(y=yblue), color="blue") +
geom_line(aes(y=yred), color="red") +
geom_line(aes(y=ygreen), color="green") +
coord_cartesian(xlim=c(-3.5, 8), ylim=c(-4, 12)) +
geom_polygon(data = poly_df, aes(x = x,y = y), fill = "lightblue", alpha = 0.25)

Modifying Aesthetics - ggplot2

I am trying to learn ggplot2 and have made below plots:
Using this code:
library(ggplot2); library(gridExtra)
gg <- ggplot(mydata,aes(x=Level))
plot1 <- gg + geom_line(aes(y=Experience,colour="xp"),size=1) +
labs(title="xp")
g <- ggplot(mydata,aes(x=Level))
plot2 <- g + geom_line(aes(y=Experience,colour="xp"),size=1) + geom_line(aes(y=Accu,colour="accu"),size=1) +
labs(title="xp vs Accumulated")
grid.arrange(plot1,plot2,ncol=2)
Where mydata is a data frame containing 3 columns (Level, xp and accu) and 30 rows.
What I am wondering is:
How to get the y-axis on the left-hand plot to have the same form as the
right-hand plot.
How to make the color of "xp" the same in both plots
without removing the descriptions of what the lines represent.
How about this (with some random data)?
library(ggplot2)
library(gridExtra)
library(scales)
gg <- ggplot(mydata,aes(x=Level))
plot1 <- gg + geom_line(aes(y=Experience,colour="xp"),size=1) +
labs(title="xp") + scale_y_continuous(labels = comma) +
scale_colour_manual(values = c("red"))
g <- ggplot(mydata,aes(x=Level))
plot2 <- g + geom_line(aes(y=Experience,colour="xp"),size=1) +
geom_line(aes(y=Accu,colour="accu"),size=1) +
labs(title="xp vs Accumulated") + scale_y_continuous(labels = comma) +
scale_colour_manual(values = c("blue", "red"))
grid.arrange(plot1,plot2,ncol=2)

two plots sharing x-axis with different geoms in ggplot2 [duplicate]

I'm using ggplot and have two graphs that I want to display on top of each other. I used grid.arrange from gridExtra to stack them. The problem is I want the left edges of the graphs to align as well as the right edges regardless of axis labels. (the problem arises because the labels of one graph are short while the other is long).
The Question:
How can I do this? I am not married to grid.arrange but the ggplot2 is a must.
What I've tried:
I tried playing with widths and heights as well as ncol and nrow to make a 2 x 2 grid and place the visuals in opposite corners and then play with the widths but I couldn't get the visuals in opposite corners.
require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip()
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip()
grid.arrange(A, B, ncol=1)
Try this,
gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
gA$widths[2:5] <- as.list(maxWidth)
gB$widths[2:5] <- as.list(maxWidth)
grid.arrange(gA, gB, ncol=1)
Edit
Here's a more general solution (works with any number of plots) using a modified version of rbind.gtable included in gridExtra
gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))
I wanted to generalize this for any number of plots. Here is a step-by-step solution using the approach by Baptiste:
plots <- list(A, B, C, D)
grobs <- list()
widths <- list()
collect the widths for each grob of each plot
for (i in 1:length(plots)){
grobs[[i]] <- ggplotGrob(plots[[i]])
widths[[i]] <- grobs[[i]]$widths[2:5]
}
use do.call to get the max width
maxwidth <- do.call(grid::unit.pmax, widths)
asign the max width to each grob
for (i in 1:length(grobs)){
grobs[[i]]$widths[2:5] <- as.list(maxwidth)
}
plot
do.call("grid.arrange", c(grobs, ncol = 1))
Using cowplot package:
A <- ggplot(CO2, aes(x = Plant)) + geom_bar() + coord_flip()
B <- ggplot(CO2, aes(x = Type)) + geom_bar() + coord_flip()
library(cowplot)
plot_grid(A, B, ncol = 1, align = "v")
On http://rpubs.com/MarkusLoew/13295 is a really easy solution available (last item)
Applied to this problem:
require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip()
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip()
grid.draw(rbind(ggplotGrob(A), ggplotGrob(B), size="first"))
you can also use this for both width and height:
require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip()
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip()
C <- ggplot(CO2, aes(x=conc)) + geom_bar() +coord_flip()
D <- ggplot(CO2, aes(x=uptake)) + geom_bar() +coord_flip()
grid.draw(cbind(
rbind(ggplotGrob(A), ggplotGrob(B), size="first"),
rbind(ggplotGrob(C), ggplotGrob(D), size="first"),
size='first'))
The egg package wraps ggplot objects into a standardised 3x3 gtable, enabling the alignment of plot panels between arbitrary ggplots, including facetted ones.
library(egg) # devtools::install_github('baptiste/egg')
library(ggplot2)
p1 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
geom_point()
p2 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
geom_point() + facet_wrap( ~ cyl, ncol=2, scales = "free") +
guides(colour="none") +
theme()
ggarrange(p1, p2)
Here is another possible solution using melt from the reshape2 package, and facet_wrap:
library(ggplot2)
library(reshape2)
dat = CO2[, c(1, 2)]
dat$id = seq(nrow(dat))
mdat = melt(dat, id.vars="id")
head(mdat)
# id variable value
# 1 1 Plant Qn1
# 2 2 Plant Qn1
# 3 3 Plant Qn1
# 4 4 Plant Qn1
# 5 5 Plant Qn1
# 6 6 Plant Qn1
plot_1 = ggplot(mdat, aes(x=value)) +
geom_bar() +
coord_flip() +
facet_wrap(~ variable, nrow=2, scales="free", drop=TRUE)
ggsave(plot=plot_1, filename="plot_1.png", height=4, width=6)
The patchwork package handles this by default:
library(ggplot2)
library(patchwork)
A <- ggplot(CO2, aes(x = Plant)) + geom_bar() + coord_flip()
B <- ggplot(CO2, aes(x = Type)) + geom_bar() + coord_flip()
A / B
Created on 2019-12-08 by the reprex package (v0.3.0)
I know this is an old post, and that it has already been answered, but may I suggest combining #baptiste's approach with purrr to make it nicer-looking:
library(purrr)
list(A, B) %>%
map(ggplotGrob) %>%
do.call(gridExtra::gtable_rbind, .) %>%
grid::grid.draw()
At best this is a hack:
library(wq)
layOut(list(A, 1, 2:16), list(B, 2:3, 1:16))
It feels really wrong though.

Display two parallel axes on a ggplot (R)

Let's say we have a simple plot of the following kind.
library(ggplot2)
df = data.frame(y=c(0,1.1,2.3,3.1,2.9,5.8,6,7.4,8.2,9.1),x=seq(1,100, length.out=10))
ggplot(df,aes(x=x,y=y)) + geom_point()
x perfectly correlates with z. The relation is: Constant=x^2*z=1.23
therefore I could rewrite the data.frame like this:
df = cbind(df,1.23/df$x^2)
The question is:
How can I display both variables xand zone the x-axis? It could be one at the bottom and one at the top of the graph or both at the bottom.
Here's a dangerous attempt. Previous version with a log-scale was just wrong.
library(ggplot2)
df = data.frame(y=c(0,1.1,2.3,3.1,2.9,5.8,6,7.4,8.2,9.1),
x=seq(1,100, length.out=10))
df$z = 1.23/df$x^2
## let's at least remove the gridlines
p1 <- ggplot(df,aes(x=x,y=y)) + geom_point() +
scale_x_continuous(expand=c(0,0)) +
theme(panel.grid.major=element_blank(),
panel.grid.minor = element_blank())
## make sure both plots have expand = c(0,0)
## otherwise data and top-axis won't necessarily be aligned...
p2 <- ggplot(df,aes(x=z,y=y)) + geom_point() +
scale_x_continuous(expand=c(0,0))
library(gtable)
g1 <- ggplotGrob(p1)
g2 <- ggplotGrob(p2)
tmp <- gtable_filter(g2, pattern="axis-b")
## ugly tricks to extract and reshape the axis
axis <- tmp[["grobs"]][[1]][["children"]][["axis"]] # corrupt the children
axis$layout <- axis$layout[2:1,]
axis$grobs[[1]][["y"]] <- axis$grobs[[1]][["y"]] - unit(1,"npc") + unit(0.15,"cm")
## back to "normality"
g1 <- gtable_add_rows(g1, sum(tmp$heights), 2)
gtableAddGrobs <- gtable_add_grob # alias, making sure #!hadley doesn't see this
g1 <- gtableAddGrobs(g1,
grobs=list(gtable_filter(g2, pattern="xlab"),axis),
t=c(1,3), l=4)
grid.newpage()
grid.draw(g1)
A both-on-the-bottom approach can be done with the excellent cowplot library.
library(ggplot2)
library(cowplot)
data <- data.frame(temp_c=runif(100, min=-5, max=30), outcome=runif(100))
plot <- ggplot(data) +
geom_point(aes(x=temp_c, y=outcome)) +
theme_classic() +
labs(x='Temperature (Celsius)')
x2plot <- ggplot(data) +
geom_point(aes(x=temp_c, y=outcome)) +
theme_classic() +
scale_x_continuous(label=function(x){round(x*(9/5) + 32)}) +
labs(x='Temperature (Fahrenehit)')
x <- get_x_axis(x2plot)
xl <- get_plot_component(x2plot, "xlab-b")
plot_grid(plot, ggdraw(x), ggdraw(xl), align='v', axis='rl', ncol=1,
rel_heights=c(0.8, 0.05, 0.05))

Resources