In R, starting multi-plot with same x-offset - r

I looked for answers in other Qs, couldn't find this Q (or Answer).
Using ggplot2 to generate the two plots individually.
Then using plot_grid function from the cowplot package to combine them.
They two data have exactly the same number of common dates.
Thus the x-axis is same time, I want the two graph's grey box to start from the same vertical spot,
so that they are time aligned. Presently, due to ylabs of different size, they don't start from same vertical line. Here is a pictorial description:

This could be achieved via the patchwork package:
library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars, aes(hp, mpg)) +
geom_point()
p2 <- ggplot(mtcars, aes(hp, mpg * 1000)) +
geom_point()
p1 / p2

If you want a solution that only uses plot_grid, you could do the following (admittedly hackier than the patchwork package):
myPlot1 <- ggplot()
myPlot2 <- ggplot()
#get a ggplot that is the axis only
myYAxis1 <- get_y_axis(myPlot1)
myYAxis2 <- get_y_axis(myPlot2)
#remove all y axis stuff from the plots themselves
myPlot1 <- myPlot1 + theme(axis.text.y = element_blank(), axis.title.y = element_blank(), axis.ticks.y = element_blank())
myPlot2 <- myPlot2 + theme(axis.text.y = element_blank(), axis.title.y = element_blank(), axis.ticks.y = element_blank())
#reassemble plots
ratioAxisToPlot = .1 #determine what fraction of the arranged plot you want to be axis and what fraction you want to be plot)
plot1Reassembled <- plot_grid(myYAxis1, myPlot1, rel_widths = c(ratioAxisToPlot, 1), ncol=2)
plot2Reassembled <- plot_grid(myYAxis2, myPlot2, rel_widths = c(ratioAxisToPlot, 1), ncol=2)
#put it all together
finalPlot <- plot_grid(plot1Reassembled, plot2Reassembled, nrow=2)

Related

ggplot2 + facet_: Reverse axes for some facets?

I've got three subplots I want to put together into one plot, and faceting would be a natural way to do it. However, one of these subplots would be easier/more natural to read with a reversed x-axis (whereas I'd like to leave the others alone). Is there a way to accomplish this using facet_grid() or facet_wrap()?
The other alternative I've considered is grid.arrange(), and the chief problem I've run into there is getting it to align the subplots based on plot area (inside the axes), rather than based on the edges of the images. (My axis titles and labels are not the same size, so the default behavior is fairly ugly.)
Edited to add a MWE with some data for context. Here, since larger is "better" for the beta and R-squared subplots, it would be more natural to reverse the axis for the p subplot. (In this case it would probably also be better to add the log transform to that scale, but my real problem doesn't need to get that fancy.)
df <- data.frame(z=c(rep("R-squared",15),rep("p",15),rep("beta",15)),
x=c(runif(15),exp(-runif(15,1,10)),rnorm(15,1,0.5)),
y=rep(letters[1:15],3))
plot <- ggplot(df) + geom_point(aes(x=x,y=y)) + facet_grid(.~z, scales="free_x", switch="x")
Here's a solution using patchwork
library(ggplot2)
library(dplyr)
df <- data.frame(z=c(rep("R-squared",15),rep("p",15),rep("beta",15)),
x=c(runif(15),exp(-runif(15,1,10)),rnorm(15,1,0.5)),
y=rep(letters[1:15],3))
p1 <- ggplot(filter(df, z == "beta"), aes(x, y)) +
geom_point()
p2 <- ggplot(filter(df, z == "p"), aes(x, y)) +
geom_point() +
scale_x_reverse() +
theme(axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
p3 <- ggplot(filter(df, z == "R-squared"), aes(x, y)) +
geom_point() +
theme(axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
#devtools::install_github("thomasp85/patchwork")
library(patchwork)
p1 + p2 + p3

Smart association of graphs where one is faceted - ggplot2

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!

Change ggplot legends

I am relatively new using the ggplot package. I want to rename the legend of a plot using the names "Sp1" and "Sp2". I have tried to make it using the following code but I have been unable to do it.
This is the code:
t<-read.table ("covartimesfinal2.txt", header=T)
attach(t)
p <- ggplot(t,aes(x=Ratio,y=Time)) + geom_point(aes(shape=factor(Sp)))
p + geom_smooth(aes(linetype=factor(Sp), ),colour="black", method='lm',
se=F)+theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),panel.background = element_blank(), axis.line =
element_line(colour = "black"))+
scale_shape_discrete(name ="Species",labels=c("Sp1", "Sp2"))
My aim is to get rid of the legend named "factor(Sp)" and make the numbers of the axis black and not grey.
Thanks in advance! Please find attached a sample plot
The following drops the unwanted legend labels, I created an own data example:
Data example
t<-data.frame(Ratio=c(1:10,1:10), Time=c(1:10,11:20), Sp=as.factor(c(rep("H", 10), rep("N", 10))))
Ggplot
library(ggplot2)
p <- ggplot(t,aes(x=Ratio,y=Time, group=Sp, shape=Sp)) + geom_point() + geom_line()
p <- p + scale_shape_discrete(name="Species",labels=c("Sp1", "Sp2"))
p <- p + theme(axis.line=element_line(colour = "black"), axis.text=element_text(colour="black"))

R, ggplot - Graphs sharing the same y-axis but with different x-axis scales

Context
I have some datasets/variables and I want to plot them, but I want to do this in a compact way. To do this I want them to share the same y-axis but distinct x-axis and, because of the different distributions, I want one of the x-axis to be log scaled and the other linear scaled.
Example
Suppose I have a long tailed variable (that I want the x-axis to be log-scaled when plotted):
library(PtProcess)
library(ggplot2)
set.seed(1)
lambda <- 1.5
a <- 1
pareto <- rpareto(1000,lambda=lambda,a=a)
x_pareto <- seq(from=min(pareto),to=max(pareto),length=1000)
y_pareto <- 1-ppareto(x_pareto,lambda,a)
df1 <- data.frame(x=x_pareto,cdf=y_pareto)
ggplot(df1,aes(x=x,y=cdf)) + geom_line() + scale_x_log10()
And a normal variable:
set.seed(1)
mean <- 3
norm <- rnorm(1000,mean=mean)
x_norm <- seq(from=min(norm),to=max(norm),length=1000)
y_norm <- pnorm(x_norm,mean=mean)
df2 <- data.frame(x=x_norm,cdf=y_norm)
ggplot(df2,aes(x=x,y=cdf)) + geom_line()
I want to plot them side by side using the same y-axis.
Attempt #1
I can do this with facets, which looks great, but I don't know how to make each x-axis with a different scale (scale_x_log10() makes both of them log scaled):
df1 <- cbind(df1,"pareto")
colnames(df1)[3] <- 'var'
df2 <- cbind(df2,"norm")
colnames(df2)[3] <- 'var'
df <- rbind(df1,df2)
ggplot(df,aes(x=x,y=cdf)) + geom_line() +
facet_wrap(~var,scales="free_x") + scale_x_log10()
Attempt #2
Use grid.arrange, but I don't know how to keep both plot areas with the same aspect ratio:
library(gridExtra)
p1 <- ggplot(df1,aes(x=x,y=cdf)) + geom_line() + scale_x_log10() +
theme(plot.margin = unit(c(0,0,0,0), "lines"),
plot.background = element_blank()) +
ggtitle("pareto")
p2 <- ggplot(df2,aes(x=x,y=cdf)) + geom_line() +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
plot.margin = unit(c(0,0,0,0), "lines"),
plot.background = element_blank()) +
ggtitle("norm")
grid.arrange(p1,p2,ncol=2)
PS: The number of plots may vary so I'm not looking for an answer specifically for 2 plots
Extending your attempt #2, gtable might be able to help you out. If the margins are the same in the two charts, then the only widths that change in the two plots (I think) are the spaces taken by the y-axis tick mark labels and axis text, which in turn changes the widths of the panels. Using code from here, the spaces taken by the axis text should be the same, thus the widths of the two panel areas should be the same, and thus the aspect ratios should be the same. However, the result (no margin to the right) does not look pretty. So I've added a little margin to the right of p2, then taken away the same amount to the left of p2. Similarly for p1: I've added a little to the left but taken away the same amount to the right.
library(PtProcess)
library(ggplot2)
library(gtable)
library(grid)
library(gridExtra)
set.seed(1)
lambda <- 1.5
a <- 1
pareto <- rpareto(1000,lambda=lambda,a=a)
x_pareto <- seq(from=min(pareto),to=max(pareto),length=1000)
y_pareto <- 1-ppareto(x_pareto,lambda,a)
df1 <- data.frame(x=x_pareto,cdf=y_pareto)
set.seed(1)
mean <- 3
norm <- rnorm(1000,mean=mean)
x_norm <- seq(from=min(norm),to=max(norm),length=1000)
y_norm <- pnorm(x_norm,mean=mean)
df2 <- data.frame(x=x_norm,cdf=y_norm)
p1 <- ggplot(df1,aes(x=x,y=cdf)) + geom_line() + scale_x_log10() +
theme(plot.margin = unit(c(0,-.5,0,.5), "lines"),
plot.background = element_blank()) +
ggtitle("pareto")
p2 <- ggplot(df2,aes(x=x,y=cdf)) + geom_line() +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
plot.margin = unit(c(0,1,0,-1), "lines"),
plot.background = element_blank()) +
ggtitle("norm")
gt1 <- ggplotGrob(p1)
gt2 <- ggplotGrob(p2)
newWidth = unit.pmax(gt1$widths[2:3], gt2$widths[2:3])
gt1$widths[2:3] = as.list(newWidth)
gt2$widths[2:3] = as.list(newWidth)
grid.arrange(gt1, gt2, ncol=2)
EDIT
To add a third plot to the right, we need to take more control over the plotting canvas. One solution is to create a new gtable that contains space for the three plots and an additional space for a right margin. Here, I let the margins in the plots take care of the spacing between the plots.
p1 <- ggplot(df1,aes(x=x,y=cdf)) + geom_line() + scale_x_log10() +
theme(plot.margin = unit(c(0,-2,0,0), "lines"),
plot.background = element_blank()) +
ggtitle("pareto")
p2 <- ggplot(df2,aes(x=x,y=cdf)) + geom_line() +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
plot.margin = unit(c(0,-2,0,0), "lines"),
plot.background = element_blank()) +
ggtitle("norm")
gt1 <- ggplotGrob(p1)
gt2 <- ggplotGrob(p2)
newWidth = unit.pmax(gt1$widths[2:3], gt2$widths[2:3])
gt1$widths[2:3] = as.list(newWidth)
gt2$widths[2:3] = as.list(newWidth)
# New gtable with space for the three plots plus a right-hand margin
gt = gtable(widths = unit(c(1, 1, 1, .3), "null"), height = unit(1, "null"))
# Instert gt1, gt2 and gt2 into the new gtable
gt <- gtable_add_grob(gt, gt1, 1, 1)
gt <- gtable_add_grob(gt, gt2, 1, 2)
gt <- gtable_add_grob(gt, gt2, 1, 3)
grid.newpage()
grid.draw(gt)
The accepted answer is exactly what makes people run when comes to plotting using R! This is my solution:
library('grid')
g1 <- ggplot(...) # however you draw your 1st plot
g2 <- ggplot(...) # however you draw your 2nd plot
grid.newpage()
grid.draw(cbind(ggplotGrob(g1), ggplotGrob(g2), size = "last"))
This takes care of the y axis (minor and major) guide-lines to align in multiple plots, effortlessly.
Dropping some axis text, unifying the legends, ..., are other tasks that can be taken care of while creating the individual plots, or by using other means provided by grid or gridExtra packages.
The accepted answer looks a little too daunting to me. So I find two ways to get around it with less efforts. Both are based on your Attempt #2 grid.arrange() method.
1. Make plot 1 no y-axis as well
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()
So all the plots will be the same. You won't have problems with different aspects ratios. You will need to generate a separate y-axis with R or your favorite image editting app.
2. Fix and respect aspects ratio
Add aspect.ratio = 1 or whatever ratio you desire to theme() of individual plots. Then use respect=TRUE in your grid.arrange()
This way you can keep y-axis in plot1 and still maintains aspects ratio in all plots. Inspired by this answer.
Hope you find these helpful!

Reproducing lattice dendrogram graph with ggplot2

Is this possible to reproduce this lattice plot with ggplot2?
library(latticeExtra)
data(mtcars)
x <- t(as.matrix(scale(mtcars)))
dd.row <- as.dendrogram(hclust(dist(x)))
row.ord <- order.dendrogram(dd.row)
dd.col <- as.dendrogram(hclust(dist(t(x))))
col.ord <- order.dendrogram(dd.col)
library(lattice)
levelplot(x[row.ord, col.ord],
aspect = "fill",
scales = list(x = list(rot = 90)),
colorkey = list(space = "left"),
legend =
list(right =
list(fun = dendrogramGrob,
args =
list(x = dd.col, ord = col.ord,
side = "right",
size = 10)),
top =
list(fun = dendrogramGrob,
args =
list(x = dd.row,
side = "top",
size = 10))))
EDIT
From 8 August 2011 the ggdendro package is available on CRAN
Note also that the dendrogram extraction function is now called dendro_data instead of cluster_data
Yes, it is. But for the time being you will have to jump through a few hoops:
Install the ggdendro package (available from CRAN). This package will extract the cluster information from several types of cluster methods (including Hclust and dendrogram) with the express purpose of plotting in ggplot.
Use grid graphics to create viewports and align three different plots.
The code:
First load the libraries and set up the data for ggplot:
library(ggplot2)
library(reshape2)
library(ggdendro)
data(mtcars)
x <- as.matrix(scale(mtcars))
dd.col <- as.dendrogram(hclust(dist(x)))
col.ord <- order.dendrogram(dd.col)
dd.row <- as.dendrogram(hclust(dist(t(x))))
row.ord <- order.dendrogram(dd.row)
xx <- scale(mtcars)[col.ord, row.ord]
xx_names <- attr(xx, "dimnames")
df <- as.data.frame(xx)
colnames(df) <- xx_names[[2]]
df$car <- xx_names[[1]]
df$car <- with(df, factor(car, levels=car, ordered=TRUE))
mdf <- melt(df, id.vars="car")
Extract dendrogram data and create the plots
ddata_x <- dendro_data(dd.row)
ddata_y <- dendro_data(dd.col)
### Set up a blank theme
theme_none <- theme(
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_blank(),
axis.title.x = element_text(colour=NA),
axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.line = element_blank()
#axis.ticks.length = element_blank()
)
### Create plot components ###
# Heatmap
p1 <- ggplot(mdf, aes(x=variable, y=car)) +
geom_tile(aes(fill=value)) + scale_fill_gradient2()
# Dendrogram 1
p2 <- ggplot(segment(ddata_x)) +
geom_segment(aes(x=x, y=y, xend=xend, yend=yend)) +
theme_none + theme(axis.title.x=element_blank())
# Dendrogram 2
p3 <- ggplot(segment(ddata_y)) +
geom_segment(aes(x=x, y=y, xend=xend, yend=yend)) +
coord_flip() + theme_none
Use grid graphics and some manual alignment to position the three plots on the page
### Draw graphic ###
grid.newpage()
print(p1, vp=viewport(0.8, 0.8, x=0.4, y=0.4))
print(p2, vp=viewport(0.52, 0.2, x=0.45, y=0.9))
print(p3, vp=viewport(0.2, 0.8, x=0.9, y=0.4))
As Ben says, everything is possible. Some work to support dendrograms has been done. Andrie de Vries has made a fortify method of tree objects. However, the resulting graphic is not pretty as you can see.
The tile would be easy to do. For the dendrogram I would inspect plot.dendrogram (using getAnywhere) to see how the coordinates for the segments are calculated. Extract those coordinates and use geom_segment to plot the dendrogram. Then use viewports to plot the tiles and the dendrogram together. Sorry I can't give a example, it's a lot of work and it's too late.
I hope this helps
Cheers
Doubtful. I do not see any functions in the Index for ggplot2 that would suggest support for dendrograms, and when this blogger put together a set of translations of the illustrations in Sarkar's Lattice book, he was unable to get a ggplot dendrogram legend:
http://learnr.wordpress.com/2009/08/10/ggplot2-version-of-figures-in-lattice-multivariate-data-visualization-with-r-part-9/
These links provide a solution for heatmaps with dendrograms in ggplot2:
https://gist.github.com/chr1swallace/4672065
https://github.com/chr1swallace/random-functions/blob/master/R/ggplot-heatmap.R
and also this one:
Align ggplot2 plots vertically

Resources