I would like to put a title on a page of plots created using R lattice. For example I can put four plots on a page as follows:
#load lattice
require(lattice).
# data
a<-c(1,3,4)
b<-c(1,2,3)
# make plots
plt1<-xyplot(a~b,main="plt1")
plt2<-xyplot(a~b,main="plt2")
plt3<-xyplot(a~b,main="plt3")
plt4<-xyplot(a~b,main="plt4")
# plot plots
plot(plt1, split=c(1,1,2,2),newpage=FALSE)
plot(plt2, split=c(1,2,2,2),newpage=FALSE)
plot(plt3, split=c(2,1,2,2),newpage=FALSE)
plot(plt4, split=c(2,2,2,2),newpage=FALSE)
Now how do a put the title "My Page of Plots" centered in the top margin above plt1 and plt3?
You could use grid to push a viewport and add the title:
library(grid)
vp2 <- viewport(x = 0.5, y = 1, width = 1, height = .1, just = c("center", "top"))
pushViewport(vp2)
grid.rect(gp = gpar(vol = "blue")) # just to see dimensions/position of the viewport
grid.text("My Title", gp = gpar(cex = 2))
You have to play with the position and dimensions of the viewport a bit. And ideally you would also add a top margin to your lattice call, such that you create some white space for your title.
Related
I had trouble trying to remove the vertical white space between each plot. I have several plots so I used ggarrange to combine them.
This is my code:
figure <- ggarrange(lima_exceso,loreto_exceso,amazonas_exceso,madre_exceso,
callao_exceso,apurimac_exceso,ancash_exceso,
sanmartin_exceso, ica_exceso,ayacucho_exceso,
arequipa_exceso, piura_exceso,junin_exceso,pasco_exceso,
cusco_exceso,lambayeque_exceso,huancavelica_exceso,
puno_exceso,tumbes_exceso,lalibertad_exceso,
tacna_exceso,huanuco_exceso,cajamarca_exceso,
labels = c(""),
ncol = 4, nrow = 6)
annotate_figure(figure,
top = text_grob("Exceso de fallecidos (media móvil-7d)",
color = "black", face = "bold", size = 12),
bottom = text_grob("Data source: \n Minsa ",
color = "black", hjust = 0, x = 0, face = "italic",
size = 8), fig.lab = "", fig.lab.face = "bold",
fig.lab.pos = "top.right" , fig.lab.size = 8)
ggsave(file="ex31.pdf" , height = 350, units = "mm" , width = 340)
This is the outcome:
As you can see there is a lot of vertical space between the graphics and that's why the title is too close from the plots. The margin of the pdf is tiny.
So I tried to narrow that space by setting margins for each plot (just for the first column), however, the outcome was not what I wanted.
I add this line code to the plots of the first column:
+theme(plot.margin = unit(c(-0.25,0.5,-0.25,0.5), "lines"))
This was the outcome:
As you can see I narrowed the vertical space but now the plots of the first column are taller than the others, and the title of the first plot (right corner) is too close from the ggarrange title. I want to reduce the space between the plots but I don't want to modify anything else.
Is there a way to reduce the vertical space of my plots using ggarrange?
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 want to plot a normal plot and a pie chart (plotrix package) side-by-side via par(mfrow = c(1, 2). The main titles of both graphics should have the same vertical position. However, by default both main titles have a different positioning.
Question: How could I ensure that the main title of the pie chart has the same vertical position as the title in a normal plot?
Consider the following reproducible example in R. "Lower main title" should be at the same height as "Main title with usual height".
# Set panel layout
par(mfrow = c(1, 2))
# Normal plot with normal height of main
plot(1:10, 1:10, main = "Main title with usual height")
# Load plotrix package for piecharts
library("plotrix")
# Pie chart with lower main title position
pie3D(1:5, labels = letters[1:5], main = "Lower main title")
The problem is that the two panels get different plot regions. If you use pie3D(..., pty = "m") they will get the same plot regions, but the pie will look distorted, unless you've chosen a window that makes the plot region for the pie approximately square.
Another solution is to change the plot region for the pie to match the other one, and plot the title after that. For example,
# Set panel layout
par(mfrow = c(1, 2))
# Normal plot with normal height of main
plot(1:10, 1:10, main = "Main title with usual height")
# Save the plot region
plt <- par("plt")
# Load plotrix package for piecharts
library("plotrix")
# Pie chart with lower main title position
pie3D(1:5, labels = letters[1:5], main = "")
# Restore the original plot region and add the title
par(plt = plt)
title(main = "Pie title with matching height")
This works until you change the shape of the plot window; the pie region tries to stay square, and it will move the title up or down.
If you don't mind that both plots are the same size you could do this by using layout() function that divides the device according to rows or columns specified.
So first you specify the two rows where your plots will be
# So create matrix with 1 row, and specify size and width of your plots
two.rows.plot <- layout(matrix(c(1, 2), nrow = 1), widths = c(5, 5), heights = c(5, 5), TRUE)
layout.show(two.rows.plot) # how the device is being split up into different figure regions
UPDATE
According to #user2554330 the above code can be replaced with only setting
par(pty = "s")
which means that we set a graphical parameter which will generate a square plotting region.
Then Use your code
# Normal plot with normal height of main
plot(1:10, 1:10, main = "Main title with usual height")
# Load plotrix package for piecharts
library("plotrix")
# Pie chart with lower main title position
pie3D(1:5, labels = letters[1:5], main = "Lower main title")
ant the output:
I'm using lsmip from lsmeans to plot my model,
library(lsmeans)
PhWs1 <- lsmip(GausNugget1, Photoperiod:Ws ~ Month,
ylab = "Observed log(number of leaves)", xlab = "Month",
main = "Interaction between Photoperiod and Water stress over the months (3 photoperiods)",
par.settings = list(fontsize = list(text = 15, points = 10)))
but I was not able to get a suggestion on the internet on how to handle the legend position, size, title, etc.
I used trellis.par.get() to see the parameters but I could not find the one related to my issue. As you can see from the graph, the legend should be "Photoperiod*Ws" but Ws is not visible.
I see two possibly complementing alternatives to approach this issue. The first would be to create a fully customized legend and pass it on to the key argument of xyplot (which lsmip is heavily based on). Here is an example taken from ?lsmip to clarify my point.
## default trellis point theme
trellis_points <- trellis.par.get("superpose.symbol")
## create customized key
key <- list(title = "Some legend title", # legend title
cex.title = 1.2,
x = .7, y = .9, # legend position
points = list(col = trellis_points$col[1:2], # points
pch = trellis_points$pch[1:2],
cex = 1.5),
text = list(c("A", "B"), cex = .9)) # text
## create results and extract lattice plot
d <- lsmip(warp.lm, wool ~ tension, plotit = FALSE,
main = "Some figure title", key = key)
p <- attr(d, "lattice")
p
As you can see, setting up a customized legend let's you modify all the different components of the legend - including labels, text and symbol sizes, legend spacing, etc. Have a deeper look at the key argument described in ?xyplot which describes the various modification options in detail.
Now, if you have a long legend title and you do not want to include the legend inside the plot area, you could also define separate viewports, thus allowing the legend to occupy more space at the right margin. Note the use of update to remove the initially created legend from p and the subsequent assembly of the single figure components using grid functionality.
## remove legend from figure
p <- update(p, legend = NULL)
## assemble figure incl. legend
library(grid)
png("plot.png", width = 14, height = 10, units = "cm", res = 300)
grid.newpage()
## add figure without legend
vp0 <- viewport(x = 0, y = 0, width = .75, height = 1,
just = c("left", "bottom"))
pushViewport(vp0)
print(p, newpage = FALSE)
## add legend
upViewport(0)
vp1 <- viewport(x = .7, y = 0, width = .3, height = 1,
just = c("left", "bottom"))
pushViewport(vp1)
draw.key(key, draw = TRUE)
dev.off()
Good Day All,
I want to add text floating in my wireframe plot and I am rather confused. I can certainly add the text as a title (e.g. main="Hello World") but I would rather not have my particular text in the title
Here is a sample wireframe:
library(lattice)
#set up some simplified data
x <- seq(-.8, .8, .1)
y <- seq(-.8, .8, .1)
myGrid <- data.frame(expand.grid(x,y))
colnames(myGrid) <- c("x","y")
myGrid$z <- myGrid$x + myGrid$y
wireframe(
myGrid$z ~ myGrid$x * myGrid$y,
xlab="X", ylab="Y", zlab="Z",
scales = list(z.ticks=5, arrows=FALSE, col="black", font=3, tck=1)
)
If I wanted to add "Hello World" in this plot floating somewhere how would I do that ?
Override the panel function and add text with grid.text.
wireframe(
myGrid$z ~ myGrid$x * myGrid$y,
xlab="X", ylab="Y", zlab="Z",
scales = list(z.ticks=5, arrows=FALSE, col="black", font=3, tck=1),
panel = function(...)
{
panel.wireframe(...)
grid.text("some text", 0, 0, default.units = "native")
}
)
Alternatively you could add the text after you plotted your wireframe with
grid::grid.text("some text", x=unit(0.7, "npc"), y=unit(0.8, "npc"))
The unit function allows you to specify the location of the text. If you use "npc" as your unit, the total width and height of your graph is 1. So the example above would display your text in the top right corner while x=y=unit(0.5, "npc") would plot it in the center.