single call of `plot()` for plotting 3 separate plots in R? - r

Suppose I have the following 3 matrices:
p.b7.4 = matrix(1:4, nrow = 2)
p.b6.4 = matrix(1:6, nrow = 3)
p.b5.4 = matrix(1:8, nrow = 4)
Further suppose, I divide my graphical device in 3 particularly ordered pieces:
m = matrix(1:3); layout(m)
Question
I'm wondering, at this point, if there is a way I could use a single plot() call to plot these 3 matrices above in my graphical device?
NOTE: This means I want 3 separate plots.

You can achieve the 3 separate plots using a lapply():
lapply(list(p.b5.4, p.b6.4, p.b7.4), plot)

I'm not quite sure what you mean. Do you mean something like this?:
plot(p.b7.4,xlim=c(1,10),ylim=c(1,10),col="red",pch=16,cex=2)
points(p.b6.4,col="blue",pch=16,cex=2)
points(p.b5.4,col="green",pch=16,cex=2)

Related

Efficient way to plot multiple ggplots from list using gridextra?

I have generated a list containing 25 ggplot elements and I want to plot them all on one page. Since I couldn't find a way to use par() for ggplots, I used the package gridextra and specifically the function grid.arrange().
It comes in less handy than par() for base-R plots....My attempt was the following and I wonder if there is a more efficient way to write it?
Thanks in advance!
plot_collection <- grid.arrange(DBScan_plots[[1]], DBScan_plots[[2]], DBScan_plots[[3]], DBScan_plots[[4]], DBScan_plots[[5]],
DBScan_plots[[6]], DBScan_plots[[7]], DBScan_plots[[8]], DBScan_plots[[9]], DBScan_plots[[10]],
DBScan_plots[[11]], DBScan_plots[[12]], DBScan_plots[[13]], DBScan_plots[[14]], DBScan_plots[[15]],
DBScan_plots[[16]], DBScan_plots[[17]], DBScan_plots[[18]], DBScan_plots[[19]], DBScan_plots[[20]],
DBScan_plots[[21]], DBScan_plots[[22]], DBScan_plots[[23]], DBScan_plots[[24]], DBScan_plots[[25]],
nrow = 5,
ncol = 5)
grid.arrange works with lists of plots. Just specify it with grobs = .
In your case:
plot_collection <- grid.arrange(grobs = DBScan_plots, nrow = 5, ncol = 5)

Save multiple ggplots from a for loop in a single plot in a particular layout

I am trying to plot a single image that contains 35 ggplots. The order of the plots in the single image is fixed and is shown below.
I also want blank grids as shown in the grid image. Each grid should have the plot with a particular drug number. I have a data frame "drug_dctv2" which I am splitting, and making into a list to read data into the for loop.
The problem is: In plot_list[[i]], only the last plot is saved 35 times with i (1 to 35). I am also not sure how to save the plots in the particular order as shown in the grid.
Through my internet search, I found library like "cowplot" and "gridextra" but I couldn't find a proper way to implement these.
I made a plot layout file which contains the drug names in the following order as shown in the grid image and in place of blank spaces, I inserted "tab". But I do not find a way to proceed from there.
I am new to R. Any help and suggestion will be appreciated.
Data set looks like as shown below. Each Drug has 10 data points.
**Drug_name conc viab**
Drug_1 1 1.0265
Drug_1 0.1 1.2365
Drug_1 0.01 0.5896
-- -- --
Drug_2 1 2.0584
Drug_2 0.1 1.0277
Drug_2 0.01 1.5696
-- -- --
#
split <- split(file,rep(1:35,each=10)) #### this will be used in the for loop
plot_list = list()
for(i in 1:length(split))
{
data <- split[[i]]
c <- data$conc
v <- data$viab
p = ggplot(data = data,aes(x=c,y=v))+geom_point()+ylim(0,1.5)+
scale_x_continuous(trans='log10')+
theme(axis.text = element_blank(),axis.title = element_blank()) +
geom_line(data=line_data, aes(x=x,y=y2),color ="red",size=1)
plot_list[[i]] = p
}
Thank you in advance !!
ggplot, as many tidyverse packages, use delayed non standard evaluation. The expression you provide inside aes is not evaluated until the plot is built (e.g. printed or saved).
The expression in your question refers to the vectors c and v defined in the for loop. These vectors change on each iteration, but the aes call only contains an expression to the reference to c and v in the environment where the for loop is running, so the c and v values used in the plot are the ones available when the plot is printed or saved.
You can, as mentioned in the comments, use a column from the data frame directly, since ggplot evaluates the data frame when ggplot() is called.
An alternative if you wanted to keep using c and v, is to make sure each iteration runs in an independent environment, so ggplot references for c and v point to the different c and v on each loop iteration. This can be done for instance replacing the for loop with an lapply call.
plot_list <- lapply(split, function(data_drug) {
c <- data_drug$conc
v <- data_drug$viab
ggplot(data = data_drug,aes(x=c,y=v))+geom_point()+ylim(0,1.5)+
scale_x_continuous(trans='log10')+
theme(axis.text = element_blank(),axis.title = element_blank()) +
geom_line(data=line_data, aes(x=x,y=y2),color ="red",size=1)
})
This is one beautiful example where a for loop and an lapply call produce different results and it's a great learning experience about non standard evaluation and variable environments.
To combine the plots look at cowplot::plot_grid https://wilkelab.org/cowplot/articles/plot_grid.html
Something like this should work
library(cowplot)
plot_grid(
plot_list[[35]], plot_list[[5]], plot_list[[3]], plot_list[[2]],
plot_list[[34]], plot_list[[1]], plot_list[[4]], plot_list[[6]],
plot_list[[32]], plot_list[[8]], NULL, NULL,
plot_list[[30]], plot_list[[7]], plot_list[[33]] , NULL,
labels = "AUTO", ncol = 4
)
You can put all the function arguments in a list and use do.call to call the function with the arguments:
plot_order <- c(
35, 5, 3, 2,
34, 1, 4, 6,
32, 8, NA, NA
)
plot_grid_args <- c(plot_list[plot_order], list(ncol = 4))
do.call(plot_grid, plot_grid_args)
So, Finally I was able to solve this problem.
I made a variable layout with the position of the drugs as they are in the split[i] list. For eg: drug_35 has to come first on the grid and it is on 35th position in split[i] list, so in "layout" variable 35 comes first and so on.
I made a text file with the grid layout as shown above in the image and then read that file in the R script and by some lines of codes I was able to make the layout variable. For the sake of simplicity I am not showing those code lines here. But, I hope the concept is clear.
lay <- read.delim("layout.txt",stringsAsFactors = FALSE,sep = "\t", header = F)
lay1 = c(t(lay))
col_n = ncol(lay)
row_n = nrow(lay)
split <- split(file,rep(1:35,each=10))
## layout = 35 5 3 2 34 1 4 6 32 8 0 0 30 7 33 .....
## 0 means blank spaces
png("PLOT.png", width = 6, height = 10, units = "in", res = 400)
par(mfrow=c(row_n,col_n),mar=c(2,0.7,1.5,0.5)) ## margins: bottom, left, top and right
for(i in layout)
{
if(i== 0) { frame(); next; }
## Here if 0 comes then the for loop will be skipped and frame() will generate a blank in the grid image
data <- split[[i]]
c <- data$conc
v <- data$viab
plot(c,v,xlab = NULL,ylab = NULL, axes = F,log = "x")
}
dev.off()

Using loops to set layout dimensions R

I am not sure this is possible. Basically what I'm trying to do is create a plot loop where if more than 5 plots are to be plotted then a second row of plots should be done ncol = to 5- number of plots.
data=matrix(rbinom(10*1000, 1, .5), ncol=10)
subdata1 = data[1:5,]
subdata2 = data[1:7,]
if (nrow(subdata1) <= 5){
par(mfrow = c(1, nrow(subdata1)))
for (i in 1:nrow(subdata1)){
plot(as.numeric(subdata1[i,1:5]), as.numeric(subdata1[i,6:10]))
}
}else{
## need to figure out how to bind layout based on nrows
## i.e. subdata2
return(NULL)
}
Basically I'm building a shinny app where based on users selections there could be anywhere from 1 plot to 10 and I want to be able to display this as nice as possible.
If you want to be as nice as possible perhaps you should look at the easy option of using the n2mfrow() function. This takes a number and turns it into the best row/column combination. With your example you can do par(mfrow = n2mfrow(nrow(data))) before running your for-loop plot. However, this will not fix the plots to 5 columns.

Add elements to a previous subplot within an active base R graphics device?

Let's say I generate 9 groups of data in a list data and plot them each with a for loop. I could use *apply here too, whichever you prefer.
data = list()
layout(mat = matrix(1:9, nrow = 3))
for(i in 1:9){
data[[i]] = rnorm(n = 100, mean = i, sd = 1)
plot(data[[i]])
}
After creating all the data, I want to decide which one is best:
best_data = which.min(sapply(data, sd))
Now I want to highlight that best data on the plot to distinguish it. Is there a plotting function that lets me go back to a specified sub-plot in the active device and add an element (maybe a title)?
I know I could make a second for loop: for loop 1 generates the data, then I assess which is best, then for loop 2 creates the plots, but this seems less efficient and more verbose.
Does such a plotting function exist for base R graphics?
#rawr's answer is simple and easy. But I thought I'd point out another option that allows you to select the "best" data set before you plot, in case you want more flexibility to plot the "best" data set differently from the rest.
For example:
# Create the data
data = lapply(1:9, function(i) rnorm(n = 100, mean = i, sd = 1))
par(mar=c(4,4,1,1))
layout(mat = matrix(1:9, nrow = 3))
rng = range(data)
# Plot each data frame
lapply(1:9, function(i) {
# Select data frame with lowest SD
best = which.min(sapply(data, sd))
# Highlight data frame with lowest SD by coloring points red
plot(data[[i]], col=ifelse(best==i,"red","black"), pch=ifelse(best==i, 3, 1), ylim=rng)
})

Densityplots using colwise - different colors for each line?

I need a plot of different density lines, each in another color. This is an example code (but much smaller), using the built-in data.fame USArrests. I hope it is ok to use it?
colors <- heat.colors(3)
plot(density(USArrests[,2], bw=1, kernel="epanechnikov", na.rm=TRUE),col=colors[1])
lines1E <- function(x)lines(density(x,bw=1,kernel="epanechnikov",na.rm=TRUE))
lines1EUSA <- colwise(lines1E)(USArrests[,3:4])`
Currently the code produces with colwise() just one color. How can I get each line with another color? Or is there ab better way to plot several density lines with different colors?
I don't quite follow your example, so I've created my own example data set. First, create a matrix with three columns:
m = matrix(rnorm(60), ncol=3)
Then plot the density of the first column:
plot(density(m[,1]), col=2)
Using your lines1E function as a template:
lines1E = function(x) {lines(density(x))}
We can add multiple curves to the plot:
colwise(lines1E)(as.data.frame(m[ ,2:3]))
Personally, I would just use:
##Added in NA for illustration
m = matrix(rnorm(60), ncol=3)
m[1,] = NA
plot(density(m[,1], na.rm=T))
sapply(2:ncol(m), function(i) lines(density(m[,i], na.rm=T), col=i))
to get:

Resources