Input must be grobs Error when using do.call() - r

I am trying to create four parallel coordinate plots into a big figure. Below is just toy data to make it reproducible so it has no particular meaning other than mimicking my real data of meaning.
library(GGally)
library(gridExtra)
library(ggplot2)
cNum=4
x = data.frame(a=2*runif(100)-1,b=2*runif(100)-1,c=2*runif(100)-1,d=2*runif(100)-1,e=2*runif(100)-1)
plot_i = vector("list", length=cNum)
for (i in 1:4){
x = x + i
plot_i[[i]] = ggparcoord(x, columns=1:5, alphaLines=0.5)
do.call("grid.arrange", c(plot_i, ncol=1))
}
Here, it seems that the error occurs on the first iteration of the do.call part. I get an error:
Error in arrangeGrob(..., as.table = as.table, clip = clip, main = main, :
input must be grobs!
I see this is a well-searched Error, although the solution seems to be dependent on the context. For instance, here, the user misused ncol (Error with grid.arrange:input must be grobs) but that does not seem to be my situation.

Related

Error in axis(side = side, at = at, labels = labels, ...) : invalid value specified for graphical parameter "pch"

I have applied DBSCAN algorithm on built-in dataset iris in R. But I am getting error when tried to visualise the output using the plot( ).
Following is my code.
library(fpc)
library(dbscan)
data("iris")
head(iris,2)
data1 <- iris[,1:4]
head(data1,2)
set.seed(220)
db <- dbscan(data1,eps = 0.45,minPts = 5)
table(db$cluster,iris$Species)
plot(db,data1,main = 'DBSCAN')
Error: Error in axis(side = side, at = at, labels = labels, ...) :
invalid value specified for graphical parameter "pch"
How to rectify this error?
I have a suggestion below, but first I see two issues:
You're loading two packages, fpc and dbscan, both of which have different functions named dbscan(). This could create tricky bugs later (e.g. if you change the order in which you load the packages, different functions will be run).
It's not clear what you're trying to plot, either what the x- or y-axes should be or the type of plot. The function plot() generally takes a vector of values for the x-axis and another for the y-axis (although not always, consult ?plot), but here you're passing it a data.frame and a dbscan object, and it doesn't know how to handle it.
Here's one way of approaching it, using ggplot() to make a scatterplot, and dplyr for some convenience functions:
# load our packages
# note: only loading dbscacn, not loading fpc since we're not using it
library(dbscan)
library(ggplot2)
library(dplyr)
# run dbscan::dbscan() on the first four columns of iris
db <- dbscan::dbscan(iris[,1:4],eps = 0.45,minPts = 5)
# create a new data frame by binding the derived clusters to the original data
# this keeps our input and output in the same dataframe for ease of reference
data2 <- bind_cols(iris, cluster = factor(db$cluster))
# make a table to confirm it gives the same results as the original code
table(data2$cluster, data2$Species)
# using ggplot, make a point plot with "jitter" so each point is visible
# x-axis is species, y-axis is cluster, also coloured according to cluster
ggplot(data2) +
geom_point(mapping = aes(x=Species, y = cluster, colour = cluster),
position = "jitter") +
labs(title = "DBSCAN")
Here's the image it generates:
If you're looking for something else, please be more specific about what the final plot should look like.

How can automate this section of grid.arrange function in R

I have a code chunk that looks like this:
p1 <- plotQC(sce_1, type = "highest-expression")
p2 <- plotQC(sce_2, type = "highest-expression")
p3 <- plotQC(sce_3, type = "highest-expression")
p4 <- plotQC(sce_4, type = "highest-expression")
grid.arrange(p1,p2,p3,p4,ncol=2)
This works very well and has no errors or warnings.
I want to put a loop around. What I have done is
for (i in 1:length(paths))
assign(paste0("p",i), plotQC(get(paste0("sce_",i)), type = "highest-expression"))
grid.arrange(p1,p2,p3,p4,ncol=2)
The second chunk also works very well.However, I would like to make grid.arrange work without manually telling it about p1,p2,p3,p4 but it should detect it the number of p objects.
How can I do this? I am working in R markdown.
If you want to keep with your loop, you could also try this:
p.list <- list()
for (i in 1:length(paths)){
p <- plotQC(get(paste0("sce_",i)), type = "highest-expression")
p.list[[i]] <- p
}
cowplot::plot_grid(plotlist = p.list)
Here, instead of assigning the plot, we save it in a list called p.list, then we build your grid from the list. I used plot_grid from cowplot because it accepts a list of plots as an argument, and I find it easier to work with the plot grid overall.
While all this works, I think you'll agree that the following is better as it in no place requires you to repeat any of the lines or manually specify some numbers:
sce <- list(sce_1, sce_2, sce_3, sce_4)
p <- lapply(sce, plotQC, type = "highest-expression")
do.call(grid.arrange, c(p, ncol = 2))
In particular, working with lists is much better in such cases. For this purpose you probably should produce sce_1, ..., sce_4 also differently, as list elements.

gridsvg does not work when used inside a function

I want to define a plot saving function that uses the gridsvg device from the package gridSVG.
library(ggplot2)
library(gridExtra)
mtcars$gear <- factor(mtcars$gear,levels=c(3,4,5),
labels=c("3gears","4gears","5gears"))
mtcars$am <- factor(mtcars$am,levels=c(0,1),
labels=c("Automatic","Manual"))
mtcars$cyl <- factor(mtcars$cyl,levels=c(4,6,8),
labels=c("4cyl","6cyl","8cyl"))
myPlot <- qplot(mpg, data=mtcars, geom="density", fill=gear, alpha=I(.5),
main="Distribution of Gas Milage", xlab="Miles Per Gallon",
ylab="Density")
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
gridSVG:::gridsvg(name = filename, width = plotWidth, height = plotHeight)
print(plot)
dev.off(which = dev.cur())
}
However if I then try to use the function it does not work. An error results:
savePlot("~/Desktop/myplot.svg", myPlot)
Show Traceback
Rerun with Debug
Error in eval(expr, envir, enclos) : object 'filename' not found
However if I do those steps from the console it works:
gridSVG::gridsvg(name = "~/Desktop/myPlot.svg", width = 15, height = 10)
myPlot
dev.off()
Is there a way I might be able to use the gridsvg function from within another function?
I wonder if I might be able to do it with eval from some environment.
Thanks,
Ben.
Here's a roundabout, but slightly more principled, way without sticking everything in the global environment (inspired by the scoping discussion in R Inferno):
library(gridSVG)
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
gridsvg(sys.frame(1))
print(plot)
grid.export(filename)
grDevices::dev.off(which = dev.cur())
}
sys.frame(1) gives us the evaluation frame of the parent context (there's an ok explanation here for all variations on functions that access the call stack).
I separated out the call to grid.export() from the call to dev.off(), because essentially all the dev.off from gridSVG does is call grid.export, then call the grDevices::dev.off. This also lets us explicitly feed the file name to grid.export.
How curious. gridsvg seems to have an eval(fncall[[i]]) step where it walks through all the arguments and assigns them, and it must be looking in the wrong environment or something?? I am not sure if this is a problem with the gridSVG package; eval-semantics always confuse me.
Here's a workaround: if you make sure the argument -values- get passed to gridsvg (rather than the argument names) it works, though I agree this isn't particularly elegant. And you have to explicitly library(gridSVG).
library(gridSVG)
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
eval(call('gridsvg', name=filename, width=plotWidth, height=plotHeight))
print(plot)
dev.off(which = dev.cur())
}
All it does is essentially call gridsvg with width=15 rather than width=plotWidth and so on.

ggplot2 : printing multiple plots in one page with a loop

I have several subjects for which I need to generate a plot, as I have many subjects I'd like to have several plots in one page rather than one figure for subject.
Here it is what I have done so far:
Read txt file with subjects name
subjs <- scan ("ListSubjs.txt", what = "")
Create a list to hold plot objects
pltList <- list()
for(s in 1:length(subjs))
{
setwd(file.path("C:/Users/", subjs[[s]])) #load subj directory
ifile=paste("Co","data.txt",sep="",collapse=NULL) #Read subj file
dat = read.table(ifile)
dat <- unlist(dat, use.names = FALSE) #make dat usable for ggplot2
df <- data.frame(dat)
pltList[[s]]<- print(ggplot( df, aes(x=dat)) + #save each plot with unique name
geom_histogram(binwidth=.01, colour="cyan", fill="cyan") +
geom_vline(aes(xintercept=0), # Ignore NA values for mean
color="red", linetype="dashed", size=1)+
xlab(paste("Co_data", subjs[[s]] , sep=" ",collapse=NULL)))
}
At this point I can display the single plots for example by
print (pltList[1]) #will print first plot
print(pltList[2]) # will print second plot
I d like to have a solution by which several plots are displayed in the same page, I 've tried something along the lines of previous posts but I don't manage to make it work
for example:
for (p in seq(length(pltList))) {
do.call("grid.arrange", pltList[[p]])
}
gives me the following error
Error in arrangeGrob(..., as.table = as.table, clip = clip, main = main, :
input must be grobs!
I can use more basic graphing features, but I d like to achieve this by using ggplot. Many thanks for consideration
Matilde
Your error comes from indexing a list with [[:
consider
pl = list(qplot(1,1), qplot(2,2))
pl[[1]] returns the first plot, but do.call expects a list of arguments. You could do it with, do.call(grid.arrange, pl[1]) (no error), but that's probably not what you want (it arranges one plot on the page, there's little point in doing that). Presumably you wanted all plots,
grid.arrange(grobs = pl)
or, equivalently,
do.call(grid.arrange, pl)
If you want a selection of this list, use [,
grid.arrange(grobs = pl[1:2])
do.call(grid.arrange, pl[1:2])
Further parameters can be passed trivially with the first syntax; with do.call care must be taken to make sure the list is in the correct form,
grid.arrange(grobs = pl[1:2], ncol=3, top=textGrob("title"))
do.call(grid.arrange, c(pl[1:2], list(ncol=3, top=textGrob("title"))))
library(gridExtra) # for grid.arrange
library(grid)
grid.arrange(pltList[[1]], pltList[[2]], pltList[[3]], pltList[[4]], ncol = 2, main = "Whatever") # say you have 4 plots
OR,
do.call(grid.arrange,pltList)
I wish I had enough reputation to comment instead of answer, but anyway you can use the following solution to get it work.
I would do exactly what you did to get the pltList, then use the multiplot function from this recipe. Note that you will need to specify the number of columns. For example, if you want to plot all plots in the list into two columns, you can do this:
print(multiplot(plotlist=pltList, cols=2))

How to create plots dynamically in grid.arrange in R?

I have a function get.single.plot, which takes one character argument and returns a ggplot2 plot object. I would like to build a grid.arrange object with n plots on it, where the n is the size of vector of (mentioned) character arguments.
E.g., I woould like something like this to work:
character.argument.vector <- c("12", "1", "2")
grid.arrange(unlist(lapply(character.argument.vector, function(n) get.single.plot(n))),
ncol = 1)
Such thing does not work - I receive the following information:
Error in arrangeGrob(..., as.table = as.table, clip = clip, main = main, :
input must be grobs!
How should I do it?
With gridExtra v>=2.0 you can simply pass a list of grobs (or plots) to grid.arrange,
grid.arrange(grobs = lapply(...), ncol=1)

Resources