I'm trying to display 4 venn.diagramm plots in a grid. I need to increase the space between diagrams in order that my legends don't overlap with other diagrams (or be outside of the plots).
I tried to do so by playing with margin in the function venn.diagram but this lead to increase the distance between the diagrams and their respective subtitles (what is not good).
I have seen some questions related to mine (e.g. controlling the inner figure margin within grid.layout) but they didn't work in my case.
Here is my code:
library(VennDiagram)
library(grid)
library(gridBase)
library(lattice)
# data
l1 <- list(Deletion=1:1420, Insertion=967:2042)
l2 <- list(Deletion=1:502, Insertion=324:660)
l3 <- list(Deletion=1:142, Insertion=85:184)
l4 <- list(Deletion=1:161, Insertion=22:217)
venns <- list(Subtargets=l1, Targets=l2, Genes=l3, Promoters=l4)
# set up grid layout
gl <- grid.layout(nrow=2, ncol=2)
# setup viewports
vp.1 <- viewport(layout.pos.col=1, layout.pos.row=1)
vp.2 <- viewport(layout.pos.col=2, layout.pos.row=1)
vp.3 <- viewport(layout.pos.col=1, layout.pos.row=2)
vp.4 <- viewport(layout.pos.col=2, layout.pos.row=2)
# init layout
pushViewport(viewport(layout=gl))
for (i in 1:4){
# access the relevant viewport
vp <- paste("vp.", i, sep="")
pushViewport(get(vp))
# draw the venn diagram
temp <- venn.diagram(venns[[i]], fill = c("red", "green"), alpha = c(0.5, 0.5),
cex = 1,cat.fontface = 2, lty =2, filename = NULL, sub=names(venns)[i],
margin = 0.5, sub.pos = c(0.5, 0.78), sub.col="blue")
# plot the venn diagram on the viewport
grid.draw(temp)
# done with this viewport
popViewport()
}
Any idea? Maybe by increasing the margins between viewports without changing the parameters within wiewport?
I met this problem before and my solution is to create more grids.
gl <- grid.layout(nrow=3, ncol=3, widths = c(1, 0.2, 1), heights = c(1, 0.2, 1))
grid.show.layout(gl)
Then you can plot your venn diagram on the corner grids.
Related
i need to overlay multiple subplots onto a single plot which is already contained inside a multirow figure (see image)
the reason why i need subplots instead of screen layout is because the figure will be possibly multicolumn, also (a 5 by 3 plot, in total)
there are packages which assist in doing subplots, but they break when you use multirow figures, and sequential subplots, except the first one, are rendered next to the overall figure border, not relative to the current row/column plot borders
i understand large packages such as ggplot2 allow this relatively easily, but base R plots are highly preferable
UPD:
the minimum reproducible example depicting the problem is here:
require(Hmisc)
COL.1 <- c('red','orange','yellow'); COL.2 <- c('blue','green','turquoise')
SUBPLOT.FUN <- function(COL) {plot(rnorm(100), type='l', col=COL)}
PLOT.FUN <- function(i) {
plot(rnorm(100),ylim=c(-1,1))
subplot(SUBPLOT.FUN(COL.1[i]), 100,1, vadj=1,hadj=1,pars=list(mfg=c(1,i)))
subplot(SUBPLOT.FUN(COL.2[i]), 100,-1,vadj=0,hadj=1,pars=list(mfg=c(1,i)))
}
plot.new(); par(mfrow=c(1,3))
for (i in 1:3) {
PLOT.FUN(i)
}
which looks like that:
while what is required is shown on the first image (meaning, EACH of the three plots must contain 3 subplots in their respective locations (along the right border, arranged vertically))
N.B. either the figure is multirow or multicolumn (as depicted) does not matter
Something like this? Inspired in this R-bloggers post.
# reproducible test data
set.seed(2022)
x <- rnorm(1000)
y <- rbinom(1000, 1, 0.5)
z <- rbinom(1000, 4, 0.5)
# save default values and prepare
# to create custom plot areas
old_par <- par(fig = c(0,1,0,1))
# set x axis limits based on data
h <- hist(x, plot = FALSE)
xlim <- c(h$breaks[1] - 0.5, h$breaks[length(h$breaks)] + 2)
hist(x, xlim = xlim)
# x = c(0.6, 1) right part of plot
# y = c(0.5, 1) top part of plot
par(fig = c(0.6, 1, 0.5, 1), new = TRUE)
boxplot(x ~ y)
# x = c(0.6, 1) right part of plot
# y = c(0.1, 0.6) bottom part of plot
par(fig = c(0.6, 1, 0.1, 0.6), new = TRUE)
boxplot(x ~ z)
# put default values back
par(old_par)
Created on 2022-08-18 by the reprex package (v2.0.1)
I'm building a custom ggplot theme to standardize the look & feel of graphs I produce. The goal is more complex than this minimal example, so I'm looking for a general solution. I have a few key goals:
I want all graphs to export at the same size (3000 pixels wide, 1500 pixels high).
I want to control the aspect ratio of the plot panel itself.
I want to use textGrobs to include figure numbers.
I want the image to be left-aligned
The challenge I'm facing is that when combining these two constraints, the image that gets saved centers the ggplot graph within the window, which makes sense as a default, but looks bad in this case.
I'm hoping there's a general solution to left-align the ggplot panel when I export. Ideally, this will also work similarly for faceted graphs.
It seems that something should be possible using one of or some combination of the gridExtra, gtable, cowplot, and egg packages, but after experimenting for a few hours I'm at a bit of a loss. Does anybody know how I can accomplish this? My code is included below.
This is the image that gets produced. As you can see, the caption is left-aligned at the bottom, but the ggplot itself is horizontally centered. I want the ggplot graph left-aligned as well.
Graph output: https://i.stack.imgur.com/5EM2c.png
library(ggplot2)
# Generate dummy data
x <- paste0("var", seq(1,10))
y <- LETTERS[1:10]
data <- expand.grid(X=x, Y=y)
data$Z <- runif(100, -2, 2)
# Generate heatmap with fixed aspect ratio
p1 <- ggplot(data, aes(X, Y, fill= Z)) +
geom_tile() +
labs(title = 'A Heatmap Graph') +
theme(aspect.ratio = 1)
# A text grob for the footer
figure_number_grob <- grid::textGrob('Figure 10',
x = 0.004,
hjust = 0,
gp = grid::gpar(fontsize = 10,
col = '#01A184'))
plot_grid <- ggpubr::ggarrange(p1,
figure_number_grob,
ncol = 1,
nrow = 2,
heights = c(1,
0.05))
# save it
png(filename = '~/test.png', width = 3000, height = 1500, res = 300, type = 'cairo')
print(plot_grid)
dev.off()
I was able to find a solution to this that works for my needs, though it does feel a bit hacky.
Here's the core idea:
Generate the plot without a fixed aspect ratio.
Split the legend from the plot as its own component
Use GridExtra's arrangeGrob to combine the plot, a spacer, the legend, and another spacer horizontally
Set the width of the plot to some fraction of npc (normal parent coordinates), in this case 0.5. This means that the plot will take up 50% of the horizontal space of the output file.
Note that this is not exactly the same as setting a fixed aspect ratio for the plot. If you know the size of the output file, it's close to the same thing, but the size of axis text & axis titles will affect the output aspect ratio for the panel itself, so while it gets you close, it's not ideal if you need a truly fixed aspect ratio
Set the width of the spacers to the remaining portion of the npc (in this case, 0.5 again), minus the width of the legend to horizontally center the legend in the remaining space.
Here's my code:
library(ggplot2)
# Generate dummy data
x <- paste0("var", seq(1,10))
y <- LETTERS[1:10]
data <- expand.grid(X=x, Y=y)
data$Z <- runif(100, -2, 2)
# Generate heatmap WITHOUT fixed aspect ratio. I address this below
p1 <- ggplot(data, aes(X, Y, fill= Z)) +
geom_tile() +
labs(title = 'A Heatmap Graph')
# Extract the legend from our plot
legend = gtable::gtable_filter(ggplotGrob(p1), "guide-box")
plot_output <- gridExtra::arrangeGrob(
p1 + theme(legend.position="none"), # Remove legend from base plot
grid::rectGrob(gp=grid::gpar(col=NA)), # Add a spacer
legend, # Add the legend back
grid::rectGrob(gp=grid::gpar(col=NA)), # Add a spacer
nrow=1, # Format plots in 1 row
widths=grid::unit.c(unit(0.5, "npc"), # Plot takes up half of width
(unit(0.5, "npc") - legend$width) * 0.5, # Spacer width
legend$width, # Legend width
(unit(0.5, "npc") - legend$width) * 0.5)) # Spacer width
# A text grob for the footer
figure_number_grob <- grid::textGrob('Figure 10',
x = 0.004,
hjust = 0,
gp = grid::gpar(fontsize = 10,
col = '#01A184'))
plot_grid <- ggpubr::ggarrange(plot_output,
figure_number_grob,
ncol = 1,
nrow = 2,
heights = c(1,
0.05))
# save it
png(filename = '~/test.png', width = 3000, height = 1500, res = 300, type = 'cairo')
print(plot_grid)
dev.off()
And here's the output image: https://i.stack.imgur.com/rgzFy.png
I have a matrix that would like to display as a raster, in a 45 degree rotated fashion and it should stretch along the x- and y- directions to fit the graphics device.
Questions that I've found that are somewhat related are here, here and here, but don't adress the stretching part.
Here is an example an unrotated raster that stretches with the graphics window:
library(grid)
# Make dummy raster
set.seed(1234)
x <- cumsum(rnorm(10))
x <- x %*% t(x)
x <- scales::rescale(x)
grid.newpage()
grid.raster(x, interpolate = F,
width = unit(1, "npc"),
height = unit(1, "npc"))
I can rotate this raster 45 degrees by adjusting the viewport. This works well if the aspect ratio of the graphics device is 1:1. However, this raster doesn't stretch if the graphics device is resized.
grid.newpage()
grid.rect(gp = gpar(col = "red")) # To illustrate boundaries
grid.raster(x, interpolate = F,
vp = viewport(angle = 45,
width = unit(1, "npc"),
height = unit(1, "npc")))
I can get the exact graphical output that I want by converting each pixel in the raster to polygons and applying a linear transformation to these coordinates.
df <- reshape2::melt(x)
# Convert pixels to vertex coordinates
coords <- matrix(
c(rep(df$Var1 - 0.5, 2),
rep(df$Var1 + 0.5, 2),
df$Var2 - 0.5,
rep(df$Var2 + 0.5, 2),
df$Var2 - 0.5),
ncol = 2
)
# Rotate coordinates
rotmat <- matrix(c(0.5, -1, 0.5, 1), ncol = 2)
coords <- t(rotmat %*% (t(coords)))
# Re-assemble data
df <- rbind(df, df, df, df)
df$Var1 <- scales::rescale(coords[, 1])
df$Var2 <- scales::rescale(coords[, 2])
df$id <- rep(seq_len(length(x)), 4)
df <- df[order(df$id), ]
grid.newpage()
grid.rect(gp = gpar(col = "red")) # To illustrate boundaries
grid.polygon(
x = df$Var1, y = df$Var2,
id = df$id,
gp = gpar(col = NA,
fill = rgb(df$value, df$value, df$value)[!duplicated(df$id)])
)
However, this seems like an inefficient way of doing this and it doesn't scale very well if the original raster gets quite large. Also, when exported to pngs and pdfs, you can sometimes notice the artifacts of these polygons. I would definitely like to stay within the realm of the grid package, because my end-goal is to build a ggplot2 geom that supports this rotation.
If there is no real solution within grid, that is fine as well, but I'd like to know. I'll just stick to the inefficient solution in that case.
When plotting a mixture of continuous and factor rasters with rasterVis::levelplot using print(..., more=TRUE), the height and width of the panels is inconsistent. This seems to be due to differences in the width of the colorkey (legend), and the colorkey's tick labels.
For example:
library(raster)
library(rasterVis)
r1 <- raster(matrix(runif(100), 10))
r2 <- as.factor(raster(matrix(rbinom(100, 1, 0.8), 10)))
levels(r2)[[1]]$name <- c('gray', 'lightblue')
p1 <- levelplot(r1, margin=FALSE, scales=list(draw=FALSE),
at=seq(0, 1, length.out=100))
p2 <- levelplot(r2, scales=list(draw=FALSE),
col.regions=c('gray90', 'lightblue'))
print(p1, split=c(1, 1, 1, 2), more=TRUE)
print(p2, split=c(1, 2, 1, 2))
Is there a way to modify the trellis graphical parameters (e.g., layout widths/heights?) to achieve consistently sized plots, such that it looks more like the layout used when plotting a RasterStack?
Or is there an alternative way of combining these rasters, which would scale to layouts with multiple columns as well as multiple rows? (Other plotting frameworks are fine, but base plot doesn't readily support factor rasters.)
You can use ?c.trellis function from the latticeExtra package.
library(latticeExtra)
c(p1, p2, layout = c(1, 2), merge.legends = TRUE)
However, in your case the legend overlaps slightly. If the order of the plots is not relevant you can instead use
c(p2, p1, layout = c(1, 2), merge.legends = TRUE)
Alternatively you can add some space to your first plot as follows.
p1 <- levelplot(r1, margin=FALSE, scales=list(draw=FALSE),
at=seq(0, 1, length.out=100),
par.settings = rasterTheme(layout.widths = list(key.right = 1.5)))
c(p1, p2, layout = c(1, 2), merge.legends = TRUE)
I've prepared a plot and two zoom areas, but am having problems inserting the zooms in the space underneath.
This is the main plot with some white space before the legend to insert the zoom plots:
I first thought of using subplot from the Hmisc package, but couldn't work out how to scale the inserts down to 30%.
Another option might be to just import the png images of all plots and then use the grid package to scale and place them, but I haven't tried this yet.
Any ideas?
Since you already have three plots - I've prepared a plot and two zoom areas
I was trying to quickly wrap up three pictures contains zoom relation.. but the whole idea is to show you how to use viewport to arrange several plots.
library(ggplot2)
library(grid)
data_x <- 5:10
data_y <- 6:11
a <- qplot(data_x, data_y, xlim=c(0, 15), ylim=c(0, 15), size=data_x)
b <- qplot(data_x, data_y, xlim=c(5, 10), ylim=c(5, 10), size=data_x) + theme(legend.position="none")
c <- qplot(data_y, data_y, xlim=c(7.5, 9.5), ylim=c(7.5, 10.5), size=data_x) + theme(legend.position="none")
vpb <- viewport(width = 0.3,
height = 0.3,
x = 0.3,
y = 0.8)
vpc <- viewport(width = 0.3,
height = 0.3,
x = 0.6,
y = 0.3)
# print and overlap
print(a)
print(b, vp = vpb)
print(c, vp = vpc)