Stretched plots in R when plotting multiple figures to PDF - r

I'm trying to create a pdf with multiple plots in pdf, when I create a pdf with 2 X 2 plots the plots are square and looks nice:
pdf(file=paste0("Test.pdf"), paper = "a4")
par(mfrow=(c(2,2)), omi=c(0,0,0,0), mar=c(2, 2, 0, 0))
for (i in 1:4)
{
plot(1:10)
}
dev.off()
However if I try to generate a pdf with 3 rows and 2 columns the plots are not square. The plots seems to be stretched so that the entire 3 x 2 matrix of plots are squared:
pdf(file=paste0("Test 2.pdf"), paper = "a4")
par(mfrow=(c(3,2)), omi=c(0,0,0,0), mar=c(2, 2, 0, 0))
for (i in 1:6)
{
plot(1:10)
}
dev.off()
How do I get the individual plots to be square in a configuration where the number of rows and columns are not equal?
Thanks in advance.

Apparantly you can use layout instead
pdf(file=paste0("Test 2.pdf"), paper = "a4")
layout(matrix(1:6, 3, 2, byrow = TRUE), respect = TRUE)
par(omi=c(0,0,0,0), mar=c(2, 2, 0, 0))
for (i in 1:6)
{
plot(1:10)
}
dev.off()

Related

Multi-panel network figure using a loop?

I'm trying to make a multipanel figure with networks in the igraph package. I'd like 2 rows, each with 3 networks. I need to be able to save the figure as a PNG and I'd like to label them each A:F in one of the corners. I've tried to do this in a loop but only one network appears in the figures. I need the V(nw)$x<- y and E(nw)$x<- y code in the loop to make my networks come out properly. My networks are in a list().
I've made a small sample of the code I've tried, I would like to avoid doing it without a loop if I can. Thanks in advance.
srs_1nw <- graph("Zachary")
srs_2nw <- graph("Heawood")
srs_3nw <- graph("Folkman")
srs_1c <- cluster_fast_greedy(srs_1nw)
srs_2c <- cluster_fast_greedy(srs_2nw)
srs_3c <- cluster_fast_greedy(srs_3nw)
listofsrs_nws <- list(srs_1nw,srs_2nw,srs_3nw)
listofsrs_cs <- list(srs_1c,srs_2c,srs_3c)
colours <- c("red","blue","green","yellow")
par(mfrow=c(2,3))
for (i in length(listofsrs_nws)) {
c<-listofsrs_cs[[i]]
nw<-listofsrs_nws[[i]]
V(nw)$size <- log(strength(nw))*6 # weighted nodes
E(nw)$arrow.size <- 2 # arrow size
c.colours <- colours[membership(c)]
plot(c, nw, col = c.colours,
mark.col = adjustcolor(colours, alpha.f = 0.4),
mark.border = adjustcolor(colours, alpha.f = 1),
vertex.frame.width = 5, edge.curved = .15)
}
We can use mapply like below
mapply(function(c, nw) {
V(nw)$size <- log(strength(nw)) * 6 # weighted nodes
E(nw)$arrow.size <- 2 # arrow size
c.colours <- colours[membership(c)]
plot(c, nw,
col = c.colours,
mark.col = adjustcolor(colours, alpha.f = 0.4),
mark.border = adjustcolor(colours, alpha.f = 1),
vertex.frame.width = 5, edge.curved = .15
)
}, listofsrs_cs, listofsrs_nws)

Is there anyway to arrange plots using par () function?

I have created 3 rows with 3 figures each and would like to centre the last 2 figures to achieve visual representativity since they are aligned to the left side of my multiple plot. Is there any way of doing that without modifying the size of the graphs?
dat=read.csv("r1new.csv", header=TRUE, sep=",", dec="."); dat
par(mfrow=c(4,3))
par(mar=c(4,4,2,2))
############################################### WC 0.1 ###############################################
res.lm1 = lm(dat$wc_10_1~dat$m_wc_10_1, data=dat)
res.lm2 = lm(dat$wc_10_3~dat$m_wc_10_3, data=dat)
res.lm3 = lm(dat$wc_10_5~dat$m_wc_10_5, data=dat)
res.res1 = resid(res.lm1)
res.res2 = resid(res.lm2)
res.res3 = resid(res.lm3)
plot(dat$m_wc_10_1 [1:1731], res.res1,
ylab="Residuals",
xlab="modelled water content, 0.1 m",
main="",
xlim=c(0.05,0.55), ylim=c(-0.25,0.15),
type="p",
col="blue",
pch=16)
points(dat$m_wc_10_3 [1:814],res.res2, col="green3",pch=16)
points(dat$m_wc_10_5 [1:1768],res.res3, col="red",pch=16)
abline(0, 0, col="gold3", lwd=2)
You should check out layout. You need to define a matrix that shows the order and placement of graphs. Then these are filled in according to number. I believe the following example is approximately what you are looking for:
M <- matrix(rep(1:12, each = 2), nrow = 4, ncol = 3*2, byrow = T)
M[4,] <- c(0,10,10,11,11,0)
M
png("testplot.png", width = 6, height = 7, units = "in", res = 200)
layout(M)
layout.show(11)
op <- par(mar = c(3,3,0.5,0.5))
for(i in seq(11)){
plot(rnorm(10), rnorm(10))
}
par(op)
dev.off()

Blank page arrange_ggsurvplots Rstudio

Using the function arrange_ggsurvplots() in order to have 4 ggsurvplot() in a 2x2 representation, but using this function, it builds a blank page before the plot of the 4 groups graphs.
I build 4 different survfit as:
library(survminer)
fit_1 <- survfit(Surv(...) ~ ..., data = data_1)
splots[[1]] <- ggsurvplot(fit_1,...)
In order to arrange it all:
arrange_ggsurvplots(splots, print = TRUE,
ncol = 2, nrow = 2)
You need to save the plot as an object, and then save it to disk with ggsave():
myplots_out <- arrange_ggsurvplots(
myplots,
print = FALSE,
ncol = 2,
nrow = 2,
title = "MySuperPlots")
ggsave(
myplots_out,
file = "Results/Survival.pdf",
width = 10.5,
height = 5)
That will avoid the production of the blank first page in the output file.

R - add title to images with rasterImage

I have 12 twelve PNG files which I want to combine in a single plot with 4x3 grid in R.
So far I can create the grid with,
plot(c(0,4), c(0,3), type = "n", xaxt = "n", yaxt = "n", xlab = "", ylab = "")
and I can add images to it with,
rasterImage(readPNG("image1.png"), 0, 3, 1, 2)
rasterImage(readPNG("image2.png"), 1, 3, 2, 2)
etc.
I get what I want, but I also want to add a title to each image in the plot. Like image1 should have a. Image1 and image2 should have b. Image2 on top of the images. Is there a way to do in R?
Thanks in advance.
#BondedDust's suggestion to use text is perfect, but using the mfrow (or mfcol) graphic parameter in par to layout the grid of plots might be sensible. You can then use plot(..., main='foo') or title(main='foo') to add the titles. For example:
Download some example png graphics, and read them into a list:
library(png)
pngs <- lapply(LETTERS[1:12], function(x) {
u <- 'http://icons.iconarchive.com/icons/mattahan/umicons/64'
download.file(mode='wb', sprintf('%s/Letter-%s-icon.png', u, x),
f <- tempfile(fileext='.png'))
readPNG(f)
})
Use mfrow to set the plot to have 4 rows and 3 columns, and add an upper margin for titles with mar. Then use sapply (for example) to iterate over elements of pngs (well, actually the indexes, 1 through 12, of the elements), plotting each in turn:
par(mfrow=c(4, 3), mar=c(0, 0, 3, 0))
sapply(seq_along(pngs), function(i) {
plot.new()
plot.window(xlim=c(0, 1), ylim=c(0, 1), asp=1)
rasterImage(pngs[[i]], 0, 0, 1, 1)
title(paste0(letters[i], '. Image ', i), font.main=2)
})
Try this:
text(x=0.5,y=2.95, labels="a. Image1")
text(x=1.5,y=2.95, labels="b. Image1")
If it needed to be bold, then plotmath expressions are needed:
text(x=1.5,y=2.95, labels=expression( bold(b.~Image1) ) )

print to pdf file using grid.table in r - too many rows to fit on one page

I'm trying to output a dataframe of about 40 rows and 5 columns to a .pdf file using grid.table in gridExtra package of R.
However, 40 rows is too long for a page so the .pdf file only shows part of the dataframe. I want to know if I can print two columns on one page so all of the rows show up on one page. Alternatively, I need to know how to print the dataframe over multiple pages. Thanks, John
Try this for drawing table on a pdf file that span multiple pages using gridExtra package:
Adjust pdf device aspect ratio
pdf(file = myfile.pdf, height = 12, width = 26)
Split the large data frame into chunks and call grid.newpage before drawing a table.
require(gridExtra)
pdf(file = myfile.pdf, height = 12, width = 26)
grid.newpage()
grid.table(sga_hits[1:38, ], show.rownames = FALSE)
grid.newpage()
grid.table(sga_hits[39:75, ], show.rownames = FALSE)
dev.off()
Automate the above as follows:
require(gridExtra)
pdf(file = myfile.pdf, height = 12, width = 26)
total_rows_per_page = 38
start_row = 1
if(total_rows_per_page > nrow(sga_hits)){
end_row = nrow(sga_hits)
}else {
end_row = total_rows_per_page
}
for(i in 1:ceiling(nrow(sga_hits)/total_rows_per_page)){
grid.newpage()
grid.table(sga_hits[start_row:end_row, ], show.rownames = FALSE)
start_row = end_row + 1
if((total_rows_per_page + end_row) < nrow(sga_hits)){
end_row = total_rows_per_page + end_row
}else {
end_row = nrow(sga_hits)
}
}
dev.off()
I'd suggest the following strategy: create the tableGrob, query its heights, split the rows to fit each page,
library(gridExtra)
library(grid)
d <- iris[sample(nrow(iris), 187, TRUE),]
tg <- tableGrob(d, rows = seq_len(nrow(d)))
fullheight <- convertHeight(sum(tg$heights), "cm", valueOnly = TRUE)
margin <- unit(0.51,"in")
margin_cm <- convertHeight(margin, "cm", valueOnly = TRUE)
a4height <- 29.7 - margin_cm
nrows <- nrow(tg)
npages <- ceiling(fullheight / a4height)
heights <- convertHeight(tg$heights, "cm", valueOnly = TRUE)
rows <- cut(cumsum(heights), include.lowest = FALSE,
breaks = c(0, cumsum(rep(a4height, npages))))
groups <- split(seq_len(nrows), rows)
gl <- lapply(groups, function(id) tg[id,])
pdf("multipage.pdf", paper = "a4", width = 0, height = 0)
for(page in seq_len(npages)){
grid.newpage()
grid.rect(width=unit(21,"cm") - margin,
height=unit(29.7,"cm")- margin)
grid.draw(gl[[page]])
}
## alternative to explicit loop:
## print(marrangeGrob(grobs=gl, ncol=1, nrow=1, top=NULL))
dev.off()
One way is to shrink the font the font size and the horizontal/vertical padding.
grid.table(mtcars, gpar.coretext = gpar(fontsize=6), gpar.coltext = gpar(fontsize=6), padding.h=unit(2, "mm"), padding.v=unit(2, "mm"), show.rownames = TRUE)
Implementing viewports from the grid is one potential solution.
A viewport defines a region in the graphics device. It is sometimes useful to define a viewport, then push it and draw inside it. A different viewport may then be pushed and drawn inside of; this method amounts to a simple way to arrange objects on a page.
First, define page and margin sizes.
# Assume total page size is 8.5in x 11in
vp.page <- viewport(x = 0.5, y = 0.5,
width = unit(x = 8.5, units = "inches"),
height = unit(x = 11, units = "inches"))
# Assume 0.5in margins (i.e., 0.5 left, right, bottom, top)
# This totals 1in for each dimension
vp.marg <- viewport(x = 0.5, y = 0.5,
width = (7.5 / 8.5), height = (10 / 11))
Next, Define viewports for each column.
To arrange columns horizontally within a viewport, their x positions will be equally spaced in the interval (0,1).
In the 2 column case, x1 = 0.25 and x2 = 0.75:
# Define the viewport for column 1
vp.col1 <- viewport(x = 0.25, y = 0.5, width = 0.5, height = 1)
# Define the viewport for column 2
vp.col2 <- viewport(x = 0.75, y = 0.5, width = 0.5, height = 1)
Now, actual data is defined.
This data will also need to be "grob'd" to be drawn into viewports.
# Assume data is stored as `dat` and has 40 rows
# Grob the data for column 1
col1 <- tableGrob(dat[1:20,], rows = NULL)
# Grob the data for column 2
col2 <- tableGrob(dat[21:40,], rows = NULL)
Now, draw the pdf:
# Initiate the pdf
pdf("results.pdf", height = 11, width = 8.5)
# Push the viewports for page and margin
pushViewport(vp.page); pushViewport(vp.marg)
# Push column 1
pushViewport(vp.col1)
# Draw column 1
grid.draw(col1)
# Return to the previous viewport
upViewport()
# Push the viewport for column 2
pushViewport(vp.col2)
# Draw column 2
grid.draw(col2)
# End the pdf and save it
dev.off()
pdf() has a width and a height argument.
Your best bet is to enlarge the dimensions and then if you're printing to paper, whichever program you're using would most likely be better suited.
Alternatively, if you want to print two columns on one page, just iterate over the columns:
# assuming `myDF` is your data.frame
pdf("filename.pdf")
for (cl in seq(from=1, to=ncol(myDF)-1, by=2)) {
plot.new()
grid.table(myDF[, cl+(0:1)])
}
dev.off()
I just used a hack. I printed the table to html using R2HTML and then I converted the html to pdf using wkhtmltopdf.
in R:
library(R2HTML)
HTML(table, file="table.html")
in the shell
wkhtmltopdf table.html table.pdf

Resources