replace list element with another list element based on name - r

I have two list m.list and the other r.list. The m.list has NA values. For those that have NA values, I would like to replace it with elements from r.list. The problem is when I use replace function in R it is taking the index of the r.list and returns a incorrect value. Below is reproducible examples. IS there a way to replace value of one list based on the element name from another list?
m.list <- list(a= 1,b=NA,c=3,d=NA)
r.list <- list(a= 4,d=8,c=9)
mr.list <- replace(m.list, which(is.na(m.list)), r.list[which(is.na(m.list))])
Here is the output that I get b should be NA and d should be 8:
> mr.list
$a
[1] 1
$b
[1] 8
$c
[1] 3
$d
NULL
here is the desired output:
$a
[1] 1
$b
[1] NA
$c
[1] 3
$d
[1] 8

Here is an option with modifyList
modifyList(m.list, r.list[intersect(names(r.list), names(which(is.na(m.list))))])
#$a
#[1] 1
#$b
#[1] NA
#$c
#[1] 3
#$d
#[1] 8
If we split the code, the idea is to find the names in 'm.list' where the value is missing and that is also found in 'r.list' (intersect), then with modifyList replace the values in 'm.list' with the subset of 'r.list'
nm1 <- intersect(names(r.list), names(which(is.na(m.list))))
modifyList(m.list, r.list[nm1])

You can use replace but you need to match the names of the lists and make sure that you also include the is.na(m.list) in the replacement argument to ensure equal lengths
l1 <- replace(m.list, is.na(m.list),
r.list[match(names(m.list),
names(r.list))][is.na(m.list)])
which gives,
$a
[1] 1
$b
NULL
$c
[1] 3
$d
[1] 8
NOTE: To replace NULL with NA, simply,
l1[lengths(l1) == 0] <- NA

We can first find the position of the value that is to be replaced. It must exist in r.list
a=names(m.list)%in%names(r.list)&is.na(m.list)
replace(m.list, a, r.list[names(which(a))])
$a
[1] 1
$b
[1] NA
$c
[1] 3
$d
[1] 8

Related

How to generate a named list containing vectors with repeating integers from a named list with character vectors in R?

Given is the list below. This list contains character vectors of variable length.
l1 <- list("a" = c("x1", "x2", "x3"),
"b" = c("x4", "x5"),
"c" = c("x6", "x7", "x8", "x9"))
> l1
$a
[1] "x1" "x2" "x3"
$b
[1] "x4" "x5"
$c
[1] "x6" "x7" "x8" "x9"
The desired output, let's call it l2, is the following:
$a
[1] 1 1 1
$b
[1] 2 2
$c
[1] 3 3 3 3
This output has the following characteristics:
l2 is a named list in which the names of the original list l1 are preserved.
The length of list l2 is the same as list l1.
The order of list elements in l1 is preserved in l2.
l2 contains vectors with repeating integers. The length of each vector in l2 is the same as the corresponding character vector in l1.
Part of solution
I found this post in which the statement below helped me to construct a partial solution.
The usual work-around is to pass it the names or indices of the vector instead of the vector itself.
l2 <- lapply(X = seq_along(l1),
FUN = function(x) rep(x, times = length(l1[[x]])))
l2
[[1]]
[1] 1 1 1
[[2]]
[1] 2 2
[[3]]
[1] 3 3 3 3
All criteria are met, except that the names are not preserved in l2.
How can I fix this in one go (not using a seperate statement after the lapply statement)?
After you run your above code,, just add the code below:-
names(l2) <- names(l1)
This will assign the names of l1 to l2, and hence, you will have the same names.
Edit: You can't achieve this with lapply, but you can do it with sapply by doing the following the following:-
l2 <- sapply(X = names(l1),
FUN = function(x) rep(which(names(l1) == x), times = length(l1[[x]])))
l2
$a
[1] 1 1 1
$b
[1] 2 2
$c
[1] 3 3 3 3
Turns out, if X argument of sapply is character vector, it will return the list by using X as names of the returned list.
You can try the following base R option, using lengths + rep + relist like below
> relist(rep(seq_along(l1), lengths(l1)), l1)
$a
[1] 1 1 1
$b
[1] 2 2
$c
[1] 3 3 3 3
You can use [] to preserve the names the list.
l1[] <- lapply(seq_along(l1), function(x) rep(x, times = length(l1[[x]])))
l1
#$a
#[1] 1 1 1
#$b
#[1] 2 2
#$c
#[1] 3 3 3 3
Another solution with Map.
l1[] <- Map(rep, seq_along(l1), lengths(l1))
In case you want to have another objects l2 keeping l1 as it is, create a copy of l1 in l2 by doing l2 <- l1.

Extract values from data table

I have the following data table (in the example I just take 3 rows):
data <- data.table(var=c("a","b","c"), value=c(-1,2,1))
I would like to extract each variable as value without subsetting each time that I need one of them.
I would like to get the follwing output:
a <- -1
b <- 2
c <- 1
An option is split to return a key/value pair. It is better not to create objects in the global environment
lst1 <- split(data$value, data$var)
lst1
#$a
#[1] -1
#$b
#[1] 2
#$c
#[1] 1
But, if we need it,
list2env(lst1, .GlobalEnv)
a
#[1] -1
b
#[1] 2
c
#[1] 1
Or another option is deframe and then use list2env
library(tibble)
list2env(as.list(deframe(data)), .GlobalEnv)
Though creating objects in the global environment is not recommended, we can try eval(parse(...)) methodology to address your question,
eval(parse(text = do.call(paste, c(data, sep = '<-'))))
a
#[1] -1
b
#[1] 2
c
#[1] 1
With purrr:
res<-purrr::map2(data$var, data$value,
function(x,y)
x <- y
)
names(res) <- data$var
The above sets the names after getting the result.
res
$a
[1] -1
$b
[1] 2
$c
[1] 1
With base's mapply, we could use:
as.list(mapply(function(x,y) assign(x,y,new.env()),
data$var,data$value))
$a
[1] -1
$b
[1] 2
$c
[1] 1
Or:
mapply(function(x,y) append(list(),y),
data$var,data$value)
$a
[1] -1
$b
[1] 2
$c
[1] 1

R - Merge Two member lists of a list

I have list l of the form say :
R> l
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
Now, I want to merge to member lists of this list. For instance, when I say I want to merge l[[1]] and l[[2]], I want the result to be :
R> l
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4
While merging two list, I don't need dupilcate elements in the result. Hence, I am using union over two member lists.
R> union(l[[1]], l,[[2]])
[1] 1 2
How can I do this merge operation? Can I do this using some predefined function?
Thanks
You can write you own merge as follows:
l <- as.list(1:4)
mymerge <- function(mylist, element1, element2){
mylist[[element1]] <- union(mylist[[element1]], mylist[[element2]])
mylist[[element2]] <- NULL
return(mylist)
}
l <- mymerge(l,1,2)
Result:
> l
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4
Comment: As akrun pointed out, you can also use c instead of union

X value of each element in list

how do I get the x value of each element in the list.
example:
list1 <- list(1:3,4:6)
list1
#[[1]]
#[1] 1 2 3
#
#[[2]]
#[1] 4 5 6
Imaginary function I'm looking for:
function(list1, 1)
# [1] 1 4
function(list2, 2)
# [1] 2 5
How can I do this?
Use sapply or lapply, in combination with the [ extraction function (see ?Extract for more info) like so:
> sapply(list1,"[",1)
[1] 1 4
...or with a list output:
> lapply(list1,"[",1)
[[1]]
[1] 1
[[2]]
[1] 4

R list from variables using variable names [duplicate]

This question already has answers here:
Can lists be created that name themselves based on input object names?
(4 answers)
Closed 4 years ago.
I've looked extensively for a solution for this very simple task and though I have a solution, it seems like there must be a better way. The task is to create a list from a set of variables, using the variable names as names for each element in the list, e.g.:
a <- 2
b <- 'foo'
c <- 1:4
My current solution:
named.list <- function(...) {
l <- list(...)
names(l) <- sapply(substitute(list(...)), deparse)[-1]
l
}
named.list(a,b,c)
Produces:
$a
[1] 2
$b
[1] "foo"
$c
[1] 1 2 3 4
A couple of ways I can think of include mget (make assumptions about the environment your objects are located in):
mget( c("a","b","c") )
$a
[1] 2
$b
[1] "foo"
$c
[1] 1 2 3 4
Or as an edit to your function, you could use, match.call like this:
named.list <- function(...) {
l <- list(...)
names(l) <- as.character( match.call()[-1] )
l
}
named.list( a,b,c)
$a
[1] 2
$b
[1] "foo"
$c
[1] 1 2 3 4
Or you can do it in one go using setNames like this:
named.list <- function(...) {
l <- setNames( list(...) , as.character( match.call()[-1]) )
l
}
named.list( a,b,c)
$a
[1] 2
$b
[1] "foo"
$c
[1] 1 2 3 4
If you already using tidyverse packages, then you might be interested in using the tibble::lst function which does this
tibble::lst(a, b, c)
# $a
# [1] 2
#
# $b
# [1] "foo"
#
# $c
# [1] 1 2 3 4

Resources