The apply functions in R are a nice way to simplify for loops to get to an output. Is there an equivalent function that helps one avoid for loops when replacing the values of a vector? This is better understood by example...
# Take this list for example
x = list( list(a=1,b=2), list(a=3,b=4), list(a=5,b=6) )
# To get all of the "a" elements from each list, I can do
vapply(x,"[[",1,"a")
[1] 1 3 5
# If I want to change all of the "a" elements, I cannot do
vapply(x,"[[",1,"a") = 10:12
Error in vapply(x, "[[", 1, "a") = 10:12 :
could not find function "vapply<-"
# (this error was expected)
# Instead I must do something like this...
new.a = 10:12
for(i in seq_along(x)) x[[i]]$a = new.a[i]
Is there a simpler or faster alternative to using a loop?
One option would be to first unlist the list x, then replace the values named "a", and then relist the new list u based on the list structure of x.
u <- unlist(x)
u[names(u) == "a"] <- 10:12
relist(u, x)
vapply is a special case of sapply where you need to pre-specify the return type.
If you a multivariate version of sapply, the function you are looking for is mapply (or Map which is a wrapper with SIMPLIFY=FALSE`)
In general, functions with side-effects are frowned upon in R. The standard approach would be to create a new object when modifying.
You could use modlifyList to perform the modifications
xnew <- Map(modifyList, x, val = lapply(10:12,function(x) list(a = x)))
Related
Is there a way to use mapply on two vectors to construct a named list? The first vector would be of type character and contain the names used for the list while the second contains the values.
So far, the only solution I have is:
> dummyList = list()
> addToList <- function(name, value) {
+ dummyList[[name]] <- value
+ }
> mapply(addToList, c("foo", "bar"), as.list(c(1, 2))
$foo
`1`
$bar
`2`
This seems like a rather contrived solution, but I can't figure out how to do it otherwise. The problems I have with it are:
It requires the creation of dummyList even though dummyList is never changed and is an empty list after the call to mapply.
If the numeric vector, c(1, 2), is not converted to a list, then the result of the call to mapply is a named vector of doubles.
To get around problem 2, I can always just call mapply on two vectors and then call as.list on the result, but it seems like there should be a way to directly create a list with the values being in a vector.
You can use setNames()
setNames(as.list(c(1, 2)), c("foo", "bar"))
(for a list) or
setNames(c(1, 2), c("foo", "bar"))
(for a vector)
What I propose is made in 2 steps, and it's quite straightforward, so maybe it's easier to understand:
test_list <- list(1, 2)
names(test_list) <- c("foo", "bar")
What #ben-bolker proposes works, but just wanted to share an alternative, in case you prefer it.
Happy coding!
I share Ben's puzzlement about why you might want to do this, and his recommendation.
Just for curiosity's sake, there is a sort of "hidden" feature in mapply that will allow this:
x <- letters[1:2]
y <- 1:2
mapply(function(x,y) { y }, x, y, SIMPLIFY = FALSE,USE.NAMES = TRUE)
$a
[1] 1
$b
[1] 2
Noting that the documentation for USE.NAMES says:
USE.NAMES logical; use names if the first ... argument has names, or
if it is a character vector, use that character vector as the names.
I have a list of named values:
myList <- list('A' = 1, 'B' = 2, 'C' = 3)
I want a vector with the value 1:3
I can't figure out how to extract the values without defining a function. Is there a simpler way that I'm unaware of?
library(plyr)
myvector <- laply(myList, function(x) x)
Is there something akin to myList$Values to strip the names and return it as a vector?
Use unlist with use.names = FALSE argument.
unlist(myList, use.names=FALSE)
purrr::flatten_*() is also a good option. the flatten_* functions add thin sanity checks and ensure type safety.
myList <- list('A'=1, 'B'=2, 'C'=3)
purrr::flatten_dbl(myList)
## [1] 1 2 3
This can be done by using unlist before as.vector.
The result is the same as using the parameter use.names=FALSE.
as.vector(unlist(myList))
How can I apply a mapping (stored in a list) to a vector?
Consider I have a mapping defined in this way:
m <- list(foo='bar', a='b', 1=2)
I can get a single list element by just writing something like m[['foo']]. What I want to do is to get a list values for multiple keys at once. An obvious solution would be just iterating through the vector:
a <- c('foo', 'a')
b <- c()
for (it in a) {b <- c(b, m[[it]])}
But looks like this is not an R-style. Can I do it with a one-liner? I also tried using lapply() and mapply() with get() function, but didn't succeed in that.
Please note: I'm new to R, so I may use some terms improperly.
m <- list(foo='bar', a='b', c = 'baz')
a <- c("foo", "a")
unlist(m[a])
## foo a
## "bar" "b"
Is there a way to use mapply on two vectors to construct a named list? The first vector would be of type character and contain the names used for the list while the second contains the values.
So far, the only solution I have is:
> dummyList = list()
> addToList <- function(name, value) {
+ dummyList[[name]] <- value
+ }
> mapply(addToList, c("foo", "bar"), as.list(c(1, 2))
$foo
`1`
$bar
`2`
This seems like a rather contrived solution, but I can't figure out how to do it otherwise. The problems I have with it are:
It requires the creation of dummyList even though dummyList is never changed and is an empty list after the call to mapply.
If the numeric vector, c(1, 2), is not converted to a list, then the result of the call to mapply is a named vector of doubles.
To get around problem 2, I can always just call mapply on two vectors and then call as.list on the result, but it seems like there should be a way to directly create a list with the values being in a vector.
You can use setNames()
setNames(as.list(c(1, 2)), c("foo", "bar"))
(for a list) or
setNames(c(1, 2), c("foo", "bar"))
(for a vector)
What I propose is made in 2 steps, and it's quite straightforward, so maybe it's easier to understand:
test_list <- list(1, 2)
names(test_list) <- c("foo", "bar")
What #ben-bolker proposes works, but just wanted to share an alternative, in case you prefer it.
Happy coding!
I share Ben's puzzlement about why you might want to do this, and his recommendation.
Just for curiosity's sake, there is a sort of "hidden" feature in mapply that will allow this:
x <- letters[1:2]
y <- 1:2
mapply(function(x,y) { y }, x, y, SIMPLIFY = FALSE,USE.NAMES = TRUE)
$a
[1] 1
$b
[1] 2
Noting that the documentation for USE.NAMES says:
USE.NAMES logical; use names if the first ... argument has names, or
if it is a character vector, use that character vector as the names.
Frequently I encounter situations where I need to create a lot of similar models for different variables. Usually I dump them into the list. Here is the example of dummy code:
modlist <- lapply(1:10,function(l) {
data <- data.frame(Y=rnorm(10),X=rnorm(10))
lm(Y~.,data=data)
})
Now getting the fit for example is very easy:
lapply(modlist,predict)
What I want to do sometimes is to extract one element from the list. The obvious way is
sapply(modlist,function(l)l$rank)
This does what I want, but I wonder if there is a shorter way to get the same result?
probably these are a little bit simple:
> z <- list(list(a=1, b=2), list(a=3, b=4))
> sapply(z, `[[`, "b")
[1] 2 4
> sapply(z, get, x="b")
[1] 2 4
and you can define a function like:
> `%c%` <- function(x, n)sapply(x, `[[`, n)
> z %c% "b"
[1] 2 4
and also this looks like an extension of $:
> `%$%` <- function(x, n) sapply(x, `[[`, as.character(as.list(match.call())$n))
> z%$%b
[1] 2 4
I usually use kohske way, but here is another trick:
sapply(modlist, with, rank)
It is more useful when you need more elements, e.g.:
sapply(modlist, with, c(rank, df.residual))
As I remember I stole it from hadley (from plyr documentation I think).
Main difference between [[ and with solutions is in case missing elements. [[ returns NULL when element is missing. with throw an error unless there exist an object in global workspace having same name as searched element. So e.g.:
dah <- 1
lapply(modlist, with, dah)
returns list of ones when modlist don't have any dah element.
With Hadley's new lowliner package you can supply map() with a numeric index or an element name to elegantly pluck components out of a list. map() is the equivalent of lapply() with some extra tricks.
library("lowliner")
l <- list(
list(a = 1, b = 2),
list(a = 3, b = 4)
)
map(l, "b")
map(l, 2)
There is also a version that simplifies the result to a vector
map_v(l, "a")
map_v(l, 1)