Reputation: 623
I'm generating and plotting multiple ggplots based on data from two lists, therefore I'm using mapply
. One of the lists has named elements, which I would like to use as ggtitle
. But it only takes the first element for all the plots
> names(sample_subset_list)
[1] "water after day 43 dna min reads per OTU 5"
[2] "biofilm after day 43 dna min reads per OTU 5"
[3] "water after day 43 cdna min reads per OTU 5"
[4] "biofilm after day 43 cdna min reads per OTU 5"
[5] "water after day 44 dna min reads per OTU 5"
[6] "biofilm after day 44 dna min reads per OTU 5"
[7] "water after day 44 cdna min reads per OTU 5"
[8] "biofilm after day 44 cdna min reads per OTU 5"
and this is the plotting function:
ordination_plots <- list()
counter <- 0
ordination_plots <- mapply(function(x,y,counter) {
counter <- counter + 1
plot_ordination(x, y, type = "sample") +
ggtitle(names(sample_subset_list)[counter]) +
}, x = sample_subset_list, y = ordination_nmds, counter = 0, SIMPLIFY = FALSE)
this will give me plots where the title is always the first element of
names(sample_subset_list)
.
The same happens calling ggtitle(names(sample_subset_list)[]) +
If I use counter <<-
(suggested here: Using a counter inside an apply structured loop in R) or call ggtitle like
ggtitle(names(sample_subset_list)) +
or
ggtitle(names(sample_subset_list)[[]]) +
I get no title at all.
I started without a counter, which also gave me the same title for all plots. Could someone explain to me how I can iterate over the names of the list elements to use them for the ggplots?
Upvotes: 0
Views: 78
Reputation: 78832
Let's reduce the complexity of the example:
counter <- 0
invisible(mapply(function(letter, counter) {
counter <- counter + 1
cat("Letter: ", letter, "; Counter: ", counter, "\n", sep="")
}, letters[1:10], counter))
NOTE: I only used invisible()
to stop printing the result of mapply()
.
letters[1:10]
is a 10-element vector of lower-case letter (built in data).
You define counter
outside of mapply()
. Unlike for
or while
, functions in mapply()
do not — by default — create or modify variables in the parent scope (outside of mapply()
, so the result is this:
Letter: a; Counter: 1
Letter: b; Counter: 1
Letter: c; Counter: 1
Letter: d; Counter: 1
Letter: e; Counter: 1
Letter: f; Counter: 1
Letter: g; Counter: 1
Letter: h; Counter: 1
Letter: i; Counter: 1
Letter: j; Counter: 1
It's fine to pass in a second parameter with info to the function argument of mapply()
but if the intent is to have a side-effect of incrementing something outside the scope of the function in mapply()
then you really shouldn't pass it to in as parameter and just modify it using the <<-
operator, which — according to the help page:
"The operators <<-
and ->>
are normally only used in functions, and cause a search to be made through parent environments for an existing definition of the variable being assigned. If such a variable is found (and its binding is not locked) then its value is redefined, otherwise assignment takes place in the global environment."
So, we can just do this:
# TO MY FUTURE SELF AND TEAM MEMBERS
# `counter` is modified as a side-effect of operations in the `mapply()`
# that follows the object declaration
counter <- 0
invisible(mapply(function(letter) {
counter <<- counter + 1
cat("Letter: ", letter, "; Counter: ", counter, "\n", sep="")
}, letters[1:10]))
to get this:
Letter: a; Counter: 1
Letter: b; Counter: 2
Letter: c; Counter: 3
Letter: d; Counter: 4
Letter: e; Counter: 5
Letter: f; Counter: 6
Letter: g; Counter: 7
Letter: h; Counter: 8
Letter: i; Counter: 9
Letter: j; Counter: 10
The comment was not meant for snark. You are using a side-effect that may be non-obvious to your future self or folks you share the code with so noting it will help you re-figure out and them figure out what's happening.
Upvotes: 2