Creating a named list from two vectors (names, values) - r

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.

Related

How to easily create a lookup table from two vectors in R [duplicate]

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.

predicate function in map

I have been studying purrr family functions recently and while I was reading the documentation of map_if I came across an alternative definition form for .p argument aka. predicate function that I could not understand. It say:
"Alternatively, if the elements of .x are themselves lists of objects,
a string indicating the name of a logical element in the inner lists"
I was wondering if someone could tell me what it means and how I can go about using it while I deal with a list whose elements are also lists. Something like this:
x <- list(a = list(foo = 1:2, bar = 3:4), b = list(baz = 5:6))
A simple example would be much appreciated as I've done some research and could not find any indication of it.
Thank you very much in advance.
Though I am not fully sure what actually you want to understand, but taking the case of list of lists, we need to consider that here only map_if is available and pmap_if is not available. Let's take another list of lists than you have suggested.
x <- list(a = list(foo = 1:2, bar = 3:4), b = list(baz = 5:6), c = list(bird = 7:10))
Now map_if applies .f wherever .p is T. So if we want to take mean of all odd indexed lists in list x, we have to actually use nested map again.
see
map_if(x, as.logical(seq_along(x) %% 2) , ~map(.x, ~mean(.x)))
$a
$a$foo
[1] 1.5
$a$bar
[1] 3.5
$b
$b$baz
[1] 5 6
$c
$c$bird
[1] 8.5
we may also other predicate functions in .p. The below example produces same output.
map_if(x, names(x) %in% c("a", "c") , ~map(.x, ~mean(.x)))
Or if let's say x is named something like this
x <- list(val1 = list(foo = 1:2, bar = 3:4), ind1 = list(baz = 5:6), val2 = list(bird = 7:10))
then below syntax will produce similar results
map_if(x, str_detect(names(x), "val") , ~map(.x, ~mean(.x)))
I hope this is somewhat near to you may want to understand.
P.S. You can give it a read too.
It seems to refer to an inner variable name with a TRUE/FALSE value. Here is the basic example I created to test it.
Create a list where the inner list has boolean values for one variable:
A <- list(foo=list(x=1, y=TRUE), bar=list(x=2, y=FALSE))
Reference the boolean variable (y) as the .p predicate by passing a string with the variable name:
map_if(A, "y", as.character)
$foo
[1] "1" "TRUE"
$bar
$bar$x
[1] 2
$bar$y
[1] FALSE
So, it only modified the foo variable since y was TRUE and bar wasn't altered since y was FALSE.

How to combine the character list vectors into one character vector while keeping the contents of each list unchanged [duplicate]

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))

Replacing elements in a list of lists

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)))

Accessing same named list elements of the list of lists in R

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)

Resources