Assuming I have two lists:
xx <- as.list(1:3)
yy <- as.list(LETTERS[1:3])
How do I combine the two such that each element of the new list is a list of the corresponding elements of each component list. So if I combined the two above, I should get:
> combined_list
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] "a"
[[2]]
[[2]][[1]]
[1] 2
[[2]][[2]]
[1] "b"
[[3]]
[[3]][[1]]
[1] 3
[[3]][[2]]
[1] "c"
If you can suggest a solution, I'd like to scale this to 3 or more.
This should do the trick. Nicely, mapply() will take an arbitrary number of lists as arguments.
xx <- as.list(1:3)
yy <- as.list(LETTERS[1:3])
zz <- rnorm(3)
mapply(list, xx, yy, zz, SIMPLIFY=FALSE)
Related
If I have a symmetric binary operator that I want to apply over the pairs of elements from a list, is there an easy way I can do this in R? I tried:
A <- list(1,2,3)
mapply(function(x,y) x+y, A,A)
but this only gives x[n]+y[n] for all n=1..N but I want x[n]+y[m] for all m=1..n, n=1..N returned as a list. outer(..) does that for m=1..N, n=1..N which involves redundant computation so I want to discount that.
Notice I don't want solution to this simple example. I need a general solution that works for non-numeric input as well. The thing I'm trying to do is like:
mapply(function(set_1, set_2) setequal(intersect(set_1, set_2), set_3), list_of_sets, list_of_sets)
In both cases addition and intersection are symmetric. In the first example, I expect list(3,4,5) from list(1+2,1+3,2+3). For the second case me input list_of_sets is:
> list_of_sets
[[1]]
numeric(0)
[[2]]
[1] 1
[[3]]
[1] 2
[[4]]
[1] 1 2
[[5]]
[1] 3
[[6]]
[1] 1 3
[[7]]
[1] 2 3
[[8]]
[1] 1 2 3
and set_3 being c(1,2) as a simple example.
You may use outer -
values <- c(1, 2, 3)
outer(values, values, `+`)
# [,1] [,2] [,3]
#[1,] 2 3 4
#[2,] 3 4 5
#[3,] 4 5 6
outer also works for non-numeric input. If the function that you want to apply is not vectorised you can use Vectorize. Since OP did not provide an example I have created one of my own.
list_of_sets_1 <- list(c('a', 'b', 'c'), c('a'))
list_of_sets_2 <- list(c('a', 'c'), c('a', 'b'))
fun <- function(x, y) intersect(x, y)
result <- outer(list_of_sets_1, list_of_sets_2, Vectorize(fun))
result
We need combn to do pairwise computation without redundancy
combn(A, 2, FUN = function(x) x[[1]] + x[[2]], simplify = FALSE)
-output
[[1]]
[1] 3
[[2]]
[1] 4
[[3]]
[1] 5
This will also work with non-numeric elements
list_of_sets <- list(c('a', 'b', 'c'), "a", c("a", "c"))
combn(list_of_sets, 2, FUN = function(x) Reduce(intersect, x), simplify = FALSE)
-output
[[1]]
[1] "a"
[[2]]
[1] "a" "c"
[[3]]
[1] "a"
We may also do
combn(list_of_sets, 2, FUN = function(x)
setequal(intersect(x[[1]], x[[2]]), set_3), simplify = FALSE)
Suppose I create an empty list of 3 elements, then I want to fill the first and third element with the same copy of another list. Can't seem to figure out how to do this.
z = vector('list', 3)
z[c(1, 3)] = 'a'
z # works fine
[[1]]
[1] "a"
[[2]]
NULL
[[3]]
[1] "a"
z[c(1, 3)] = list('a', 'b')
z # doesn't work
[[1]]
[1] "a"
[[2]]
NULL
[[3]]
[1] "b"
Desired result is that both [[1]] and [[3]] contain the list('a','b'). Saw some other posts, but those have to do with retrieving values, not assigning them.
I would like to add a sequential element onto a list. Suppose I have the following list
lst <- list("A"=list(e1="a",e2="!"), "B"=list(e1="b", e2="#"))
$A
$A$e1
[1] "a"
$A$e2
[1] "!"
$B
$B$e1
[1] "b"
$B$e2
[1] "#"
I would like to append a e3 which is the position index of that element in the list so essentially I would like my list to be:
$A
$A$e1
[1] "a"
$A$e2
[1] "!"
$A$e3
[1] 1
$B
$B$e1
[1] "b"
$B$e2
[1] "#"
$B$e3
[1] 2
setNames(lapply(seq_along(lst), function(i){
temp = lst[[i]]
temp$e3 = i
temp
}), names(lst))
#$`A`
#$`A`$`e1`
#[1] "a"
#$`A`$e2
#[1] "!"
#$`A`$e3
#[1] 1
#$B
#$B$`e1`
#[1] "b"
#$B$e2
#[1] "#"
#$B$e3
#[1] 2
Here is a solution that doesn't assume that the sub-lists have the same known number of elements.
library("tidyverse")
library("glue")
lst <- list("A"=list(e1="a",e2="!"), "B"=list(e1="b", e2="#"))
# The part
# `setNames(list(.y), glue("e{length(.x) + 1}"))`
# creates a one-element list named accordingly to append to the previous list
map2(lst, seq(lst),
~ append(.x, setNames(list(.y), glue("e{length(.x) + 1}") )))
#> $A
#> $A$e1
#> [1] "a"
#>
#> $A$e2
#> [1] "!"
#>
#> $A$e3
#> [1] 1
#>
#>
#> $B
#> $B$e1
#> [1] "b"
#>
#> $B$e2
#> [1] "#"
#>
#> $B$e3
#> [1] 2
# If naming the additional element is not important, then this can simplified to
map2(lst, seq(lst), append)
# or
map2(lst, seq(lst), c)
Created on 2019-03-06 by the reprex package (v0.2.1)
Another option using Map
Map(function(x, y) c(x, "e3" = y), x = lst, y = seq_along(lst))
#$A
#$A$e1
#[1] "a"
#$A$e2
#[1] "!"
#$A$e3
#[1] 1
#$B
#$B$e1
#[1] "b"
#$B$e2
#[1] "#"
#$B$e3
#[1] 2
This could be written even more concise as
Map(c, lst, e3 = seq_along(lst))
Thanks to #thelatemail
We can use a for loop as well
for(i in seq_along(lst)) lst[[i]]$e3 <- i
Assuming I understood correctly, that you want to add a 3rd element to each nested list which contains the index of that list in it's parent list. This works:
library(rlist)
lst <- list("A"=list(e1="a",e2="!"), "B"=list(e1="b", e2="#"))
for(i in seq(1:length(lst))){
lst[[i]] <- list.append(lst[[i]],e3=i)
}
lst
We can loop along the length of lst with lapply, adding this sequential index to each element.
lst2 <- lapply(seq_along(lst), function(i) {
df <- lst[[i]]
df$e3 <- i
return(df)
})
names(lst2) <- names(lst) # Preserve names from lst
Or, if you're not scared about modifying in place:
lapply(seq_along(lst), function(i) {
lst[[i]]$e3 <<- i
})
Both give the same output:
$A
$A$e1
[1] "a"
$A$e2
[1] "!"
$A$e3
[1] 1
$B
$B$e1
[1] "b"
$B$e2
[1] "#"
$B$e3
[1] 2
I am scraping json from an apartment rental platform's rest api.
Let's say I have the following data structure, which conveys the square footage of each rental unit in a single building:
sqft1 <- list(c("1500"),
NULL,
c("1300"))
It's important that I retain the dimensionality of this data. If I try to unlist and aggregate into a data frame alongside other rental unit properties, I will lose the 2nd element and get thrown an error.
But by finding the indices in the list that have a NULL element, I can replace them with a character vector containing an empty string as follows:
isNull1 <- lapply(1:length(sqft1), function(x) is.null(sqft1[[x]]))
sqft1[unlist(isNull1)] <- c("")
My problem arises when I try to apply the same replacement function over a result set for multiple buildings. After running the following block, no replacements get made.
sqft3 <- list(list(c("1500"),
NULL,
c("1300")),
list(c("1400"),
c("1700")),
list(NULL,
c("1200")))
isNull3 <- lapply(1:length(sqft3), function(x) lapply(1:length(sqft3[[x]]), function(y) is.null(sqft3[[x]][[y]])))
lapply(1:length(sqft3), function(x) sqft3[[x]][unlist(isNull3[[x]])] <- c(""))
What concept about applying functions am I misunderstanding here? Any ideas how to make it work?
Thanks!
You could use a nested lapply as follows
lapply(sqft3, function (x) lapply(x, function (y) ifelse(is.null(y), "", y)))
This is similiar to #MKR's solution.
Using the purrr package, you could also use modify_depth
library(purrr)
modify_depth(sqft3, .depth = 2, .f = ~ifelse(is.null(.x), "", .x))
The ifelse from above could also be replaced by this function %||% ("null-default", also from purrr) as follows
modify_depth(sqft3, 2, `%||%`, "")
(%||% is short-hand for if (is.null(x)) y else x)
Result
#[[1]]
#[[1]][[1]]
#[1] "1500"
#
#[[1]][[2]]
#[1] ""
#
#[[1]][[3]]
#[1] "1300"
#
#
#[[2]]
#[[2]][[1]]
#[1] "1400"
#
#[[2]][[2]]
#[1] "1700"
#
#
#[[3]]
#[[3]][[1]]
#[1] ""
#
#[[3]][[2]]
#[1] "1200"
One option is to use map function twice as:
map(sqft3,function(x){
map(x, function(y){
y[is.null(y)] <- ""
y
})})
# [[1]]
# [[1]][[1]]
# [1] "1500"
#
# [[1]][[2]]
# [1] ""
#
# [[1]][[3]]
# [1] "1300"
#
#
# [[2]]
# [[2]][[1]]
# [1] "1400"
#
# [[2]][[2]]
# [1] "1700"
#
#
# [[3]]
# [[3]][[1]]
# [1] ""
#
# [[3]][[2]]
# [1] "1200"
Data:
sqft3 <- list(list(c("1500"),
NULL,
c("1300")),
list(c("1400"),
c("1700")),
list(NULL,
c("1200")))
sqft3
# [[1]]
# [[1]][[1]]
# [1] "1500"
#
# [[1]][[2]]
# NULL
#
# [[1]][[3]]
# [1] "1300"
#
#
# [[2]]
# [[2]][[1]]
# [1] "1400"
#
# [[2]][[2]]
# [1] "1700"
#
#
# [[3]]
# [[3]][[1]]
# NULL
#
# [[3]][[2]]
# [1] "1200"
I have a rather simple task but haven't find a good solution.
> mylist
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
[[2]]
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
[[3]]
[1] 25 26 27 28 29 30 31 32
y <- c(3,5,9)
I would like to extract from mylist the sub-elements 3,5, and 9 of each component in the list.
I have tried, sapply[mylist,"[[",y] but not luck!, and others like vapply, lapply, etc..
You could use sapply(mylist, "[", y):
mylist <- list(1:5, 6:10, 11:15)
sapply(mylist, "[", c(2,3))
Try using [ instead of [[ (and depending on what you're after you light actually want lapply).
From ?'[[':
The most important distinction between [, [[ and $ is that the [ can
select more than one element whereas the other two select a single
element.
Using lapply:
# create mylist
list1<-1:10
list2<-letters[1:26]
list3<-25:32
mylist<-list(list1,list2,list3)
# select 3,5,9th element from each list
list.2 <- lapply(mylist, function(x) {x[c(3,5,9)]})
purrr provides another solution for solving these kinds of list manipulations within the tidyverse
library(purrr)
library(dplyr)
desired_values <- c(1,3)
mylist <- list(1:5, letters[1:6], 11:15) %>%
purrr::map(`[`,desired_values)
mylist
An easy way to subset repeated named elements of a list, similar to other answers here.
(so I can find it next time I look this question up)
E.g., subset the "b" elements from a repeating list where each element includes an "a" and "b" sub-element:
mylist <- list(
list(
"a" = runif(3),
"b" = runif(1)
),
list(
"a" = runif(3),
"b" = runif(1)
)
)
mylist
#> [[1]]
#> [[1]]$a
#> [1] 0.7547490 0.6528348 0.2339767
#>
#> [[1]]$b
#> [1] 0.8815888
#>
#>
#> [[2]]
#> [[2]]$a
#> [1] 0.51352909 0.09637425 0.99291650
#>
#> [[2]]$b
#> [1] 0.8407162
blist <- lapply(
X = mylist,
FUN = function(x){x[["b"]]}
)
blist
#> [[1]]
#> [1] 0.8815888
#>
#> [[2]]
#> [1] 0.8407162
Created on 2019-11-06 by the reprex package (v0.3.0)
I don't think sgibb's answer gives what you would want. I suggest making a new function:
subsetList <- function(myList, elementNames) {
lapply(elementNames, FUN=function(x) myList[[x]])
}
Then you can use it like this:
x <- list(a=3, b="hello", c=4.5, d="world")
subsetList(x, c("d", "a"))
subsetList(x, c(4, 1))
These both give
[[1]]
[1] "world"
[[2]]
[1] 3
which is what you would want, I think.
There are better ways of doing this, but here's a quick solution.
# your values
list1<-1:10
list2<-letters[1:26]
list3<-25:32
# put 'em together in a list
mylist<-list(list1,list2,list3)
# function
foo<-function(x){x[c(3,5,9)]}
# apply function to each of the element in the list
foo(mylist[[1]])
foo(mylist[[2]])
foo(mylist[[3]])
# check the output
> foo(mylist[[1]])
[1] 3 5 9
> foo(mylist[[2]])
[1] "c" "e" "i"
> foo(mylist[[3]])
[1] 27 29 NA