qplot call overwrites list elements - r

Running the R script
list1<-list()
list2<-list()
for(i in 1:3){
list1[[i]]<-i
}
for(i in 1:3){
list2[[i]]<-qplot(i)
}
I recognize that list1 contains the elements 1,2,3. But list2 contains three times the element qplot(3).
Is qplot not compatible with looping? How can I save my plots in a list using a loop?

In ggplot the aesthetics are stored as expressions and evaluated when the plot is rendered. So qplot(i) does not generate a plot, but rather a plot definition, using a reference to the variable i. All three plots are the same in the sense that they all reference i.
If you type
list2[[1]]
after the second loop has run, you cause the ggplot object stored in list2[[1]] to be rendered, using whatever value i is set to at the moment (which is 3 after the loop).
Try this:
i <- 4
list2[[1]]
Now the plot rendered is equivalent to qplot(4).
The workaround depends on what you are trying to achieve. The basic idea is not to use external variables in aesthetics. So in your trivial case,
for(i in 1:3){
list2[[i]]<-ggplot(data.frame(x=i), aes(x))+geom_histogram()
}
will work. This is because the reference to the external variable i is not in the aesthetics (e.g., the call to aes(...).

Related

Any way to access the plot object generated by DescTools::Desc()?

I am using Desc() from DescTools to describe some variables in a rmarkdown PDF document. The problem is that it generates 3 plots that are kept in line when I knit the document, thus clipping the images.
Example:
dates <- sample(seq(as.Date('1999/01/01'), as.Date('2021/01/01'), by="day"), 1000)
results <- DescTools::Desc(dates)
results
The output contains 3 plots. I can find the individual responses using the list in results[[1]]], but I can't find the plot objects, which I think could be a way to put then one below the other.
Any thoughts?
There are no plot objects in results.
Instead, when you type results in your console, it invokes the S3 generic print, which in turn dispatches the print.Desc method. By default, print.Desc will call a plotting function based on the "class" member of results, which in your example is "Date". If you type DescTools:::plot.Desc.Date in your console, you will see the function that actually generates the plot every time you print results.
So there are no plot objects. There is data to create a plot, and whenever you print results to the console, the plots are created by a call to a plotting function.
The Desc plotting functions seem to have very few options available to allow modifications, so the best option would probably be to use the data inside results to create your own plots. If you wish to see the contents of results without the plots, simply type:
print(results, plotit = FALSE)
And if you want the three plots one at a time, you can do:
DescTools:::plot.Desc.Date(results[[1]], type = 1)
DescTools:::plot.Desc.Date(results[[1]], type = 2)
DescTools:::plot.Desc.Date(results[[1]], type = 3)

How to save the plot output and object output of a function call in R

I am making a function call in R. The function returns a list and in it has a call to plot(). From this one call, I need to record the plot as an object and store the list as a separate object. I need to store the plot because I later give it to ggarrange() along with other plots. I must store both outputs in one call of the function because the function runs permutations. As a result, it will produce a slightly different output each time. Therefore, in order for the data in the list to match the plot, the call can only be made once.
The line of code below is what I am currently using that successfully stores the plot as ggplot object. It does not store the list.
my_plot <- as.ggplot(~(my_function(input1,input2, permutations=1000)))
The code below will return the list, but not save the plot.
my_list <- my_function(input1,input2, permutations=1000)
Does anyone know a way of accomplishing what I am trying to do?

How do I remove an object from within a function environment in R?

How do I remove an object from the current function environment?
I'm trying to achieve this:
foo <- function(bar){
x <- bar
rm(bar, envir = environment())
print(c(x, is.null(bar)))
}
Because I want the function to be able to handle multiple inputs.
Specifically I'm trying to pass either a dataframe or a vector to the function, and if I'm passing a dataframe I want to set the vector to NULL for later error handling.
If you want, you can watch my DepthPlotter script, where I want to let the second function check if depth is a dataframe, and if so, assign it to df in stead and remove depth from the environment.
Here is a very brief sketch of how to set this up using S3 method dispatch.
First, you define your generic:
DepthPlotter <- function(depth,...){
UseMethod("DepthPlotter", depth)
}
Then you define methods for specific classes of the argument depth. As a very basic example in your case, you might create only two, a data.frame method and a default method to handle the vector case:
DepthPlotter.default <- function(depth, variable, ...){
#Here you write a function assuming that depth is
# anything but a data frame
}
DepthPlotter.data.frame <- function(depth,...){
#Here you'd write a function that assumes
# that depth is a data frame
}
And then you can call DepthPlotter() using either type of argument and the correct function will be run based upon the result of class(depth).
The example I've sketched out here is a little crude, since I've used a default method to handle the vector case. You could write .numeric and .integer methods to handle numeric or integer vectors more specifically. In my example, the .default method will be called for any case other than data.frame, so if you go this route you'd want to write some code in there that checks for strange cases like depth being a complicated list, or other odd object, if you think there's a chance something like that might be passed to the function.

assign rownames within a loop to constructed variables names

I'm creating data frames within a loop. The data frames' name should be the combination of a name and a number (the loops iteration). I use the assign function for this and works. I also want to assign names to the data frames' rows. I tried two ways, but I'm getting the error messages "target of assignment expands to non-language object" and "only the first element is used as variable name". Below is a reproducible example of I'm trying to do.
rows<-c("a","b")
df<-data.frame(var1=c(1,2),var2=c(10,20))
for (n in 1:2){
assign (paste("data",n,sep="_"),df)
rownames(get(paste("data",n,sep="_")))<-rows # it doesn't work
assign(rownames(get(paste("data",n,sep="_"))),rows) # it doesn't work
}
I'd like to know why it doesn't work and how to solve it. I found similar threads like this and this, but I was not able to solve my case. Thank you.
Based on Roland's comment, I come out with this solution:
rows<-c("a","b")
df<-data.frame(var1=c(1,2),var2=c(10,20))
dfs<-list()
for (n in 1:2){
dfs[[n]]<-df
rownames(dfs[[n]])<-rows
}
A list is the key!
Or without list, you just need a dummy variable:
rows<-c("a","b")
df<-data.frame(var1=c(1,2),var2=c(10,20))
for (n in 1:2){
assign (paste("data",n,sep="_"),df)
labelling <- get(paste("data",n,sep="_"))
labels <- rows
rownames(labelling)<-labels
assign(paste("data",n,sep="_"),labelling)
}

How to use different parameters based on a vector for each iteration of lapply?

I have a list of vectors for which I am lapplying the function lines to plot the content of each of the elements in the list. Example code following:
l <- list()
for(i in 1:10){l[[i]] <- rnorm(10)}
plot(l[[1]], t='n')
lapply(l, lines)
Is there a way of telling lapply that for each element use a different parameter, for instance, color or line type, so I can easily attribute the corresponding features I want to each element of the list? For instance, I'd like to have a vector of colors that match a particular element on the list.
I came up with the same lapply approach as jlhoward. Here's an example which colors the line based on whether the average is greater than zero or not:
lapply(l, function(line) {lines(line, col=ifelse(mean(line) > 0, 'red', 'blue'))})
That said, your example uses a loop to create the sample data. If your actual code is using a loop, why not plot each line as part of the data-generating loop? That way you can calculate whatever plotting parameters you need on a per-line basis. Normally, I wouldn't advocate a loop over an apply function -- R is really slow at loops! But unless you are plotting tens of thousands of lines, you probably won't notice much of a performance hit. (Also, bear in mind that the lapply approach is going to return a NULL value for each line plotted ... which is kinda awkward.)
I found that mapply is the way to go

Resources