How to use elements from a second list within lapply() - r

I have two lists, for example:
L1 <- list(A=1:3, B=4:6)
L2 <- list(A=2, B=3)
and I want the elements of the first list to be repeated by the numers in the second list, resulting in:
>L3
$A
[1] 1 2 3 1 2 3
$B
[1] 4 5 6 4 5 6 4 5 6
I tried with lapply() but I cannot figure out the right times argument:
L3 <- lapply(L1, function(x) rep(x, L2))
Certainly quite easy to solve, but I don't get it right now.

Here is one solution:
lapply(names(L1), function(x) rep(L1[[x]], L2[[x]]))

Related

How to organize the output of the list of list in R

Suppose this is my list of list (I would like to organize the result as my data contains more than 40 results and it is difficult for me to organize them manually).
s <- c(1,2,3)
ss <- c(4,5,6)
S <- list(s,ss)
h <- c(4,8,7)
hh <- c(0,3,4)
H <- list(h,hh)
HH <- list(S,H)
names1 <- c("First","Second")
lapply(setNames(HH, paste0(names1, '_Model')), function(x)
setNames(x, paste0('Res_', seq_along(x))))
#$First_Model
#$First_Model$Res_1
#[1] 1 2 3
#$First_Model$Res_2
#[1] 4 5 6
#$Second_Model
#$Second_Model$Res_1
#[1] 4 8 7
#$Second_Model$Res_2
#[1] 0 3 4
I would like to have the result similar to the following:
#$First_Model
#$First_Model$Res_1
#[1] 1 2 3
#$Second_Model
#$Second_Model$Res_1
#[1] 4 8 7
#$First_Model$Res_2
#[1] 4 5 6
#$Second_Model$Res_2
#[1] 0 3 4
The problem in question is how to rearrange the nested list from "Model No. > Results No." to "Results No. > Model No."
I was going for something similar to Wimpel's answer.
Res_no <- seq_along(HH[[1]]) # results elements
lapply(setNames(Res_no, paste0("Res_", Res_no)), function(x)
lapply(setNames(HH, paste0(names1, '_Model')), `[[`, x)
)
Output
#$Res_1
#$Res_1$First_Model
#[1] 1 2 3
#
#$Res_1$Second_Model
#[1] 4 8 7
#
#
#$Res_2
#$Res_2$First_Model
#[1] 4 5 6
#
#$Res_2$Second_Model
#[1] 0 3 4
The base of this solution is to extract the x-th element of the nested list (seen in the inner lapply() function of the code). You can do this with lapply or purrr:map, as described here.
The outer lapply() function lets you repeat it for all the "Results No."
Something like this perhaps?
# From your code, create a list L
L <- lapply(setNames(HH, paste0(names1, '_Model')), function(x)
setNames(x, paste0('Res_', seq_along(x))))
# get all x-th elements from the list, and add them to new list L2
L2 <- lapply( 1:length(L[[1]]), function(x) {
lapply(L, "[[", x)
})
# set names of L2
names(L2) <- names(L[[1]])
output
# $Res_1
# $Res_1$First_Model
# [1] 1 2 3
#
# $Res_1$Second_Model
# [1] 4 8 7
#
#
# $Res_2
# $Res_2$First_Model
# [1] 4 5 6
#
# $Res_2$Second_Model
# [1] 0 3 4

combine elements of list of lists with the same name

I have a list of 4 lists with the same name:
lst1 <-
list(list(c(1,2,3)),list(c(7,8,9)),list(c(4,5,6)),list(c(10,11,12)))
names(lst1) <- c("a","b","a","b")
I want to combine the sub lists together (first "a" with second "a", first "b" with second "b":
result <- list(list(c(1,2,3,4,5,6)),list(c(7,8,9,10,11,12)))
names(result) <- c("a","b")
I have tried multiple things, but can't figure it out.
Since lst1["a"] isn't going to give us all the elements of lst1 named a, we are going to need to work with names(lst1). One base R approach would be
nm <- names(lst1)
result <- lapply(unique(nm), function(n) unname(unlist(lst1[nm %in% n])))
names(result) <- unique(nm)
result
# $a
# [1] 1 2 3 4 5 6
#
# $b
# [1] 7 8 9 10 11 12
Another option is to use unlist first and then split the resulting vector.
vec <- unlist(lst1)
split(unname(vec), sub("\\d+$", "", names(vec)))
#$a
#[1] 1 2 3 4 5 6
#$b
#[1] 7 8 9 10 11 12
Just group the elements with the same name and unlist them:
tapply(lst1,names(lst1),FUN=function(x) unname(unlist(x)))

How to merge lists with identical column names to get their union

Say I have N lists that all have the same column names. I want to merge these such that I get a resulting list with same columns, but now containing entries from all N list. Here is a MWE showing what I want:
ls<-list()
ls[[1]]<-list("a"=1,
"b"=2)
ls[[2]]<-list("a"=3,
"b"=4)
#how to write a one-liner that produces lsTotal, which is the union of ls[[1]] and ls[[2]]?
lsTotal<-list("a"=c(1,3),
"b"=c(2,4))
I found this thread, from which I can use Map(c, ls[[1]], ls[[2]]). However, writing it out is tedious if ls is very long. Is there a shortcut?
One option is tidyverse
library(purrr)
library(dplyr)
transpose(ls) %>%
map(unlist)
Or use Map with do.call
do.call(Map, c(f=c, ls))
#$a
#[1] 1 3
#$b
#[1] 2 4
Here is a simple two-liner with unlist and split.
# get a named vector
tmp <- unlist(ls)
# split on the names
split(unname(tmp), names(tmp))
$a
[1] 1 3
$b
[1] 2 4
I know this question already has a few answers but another option is to use Reduce with your Map to apply the Map to each of the elements successively in the list:
Reduce(function(x,y) Map(c,x,y), ls)
#$a
#[1] 1 3
#$b
#[1] 2 4
Or for a more complicated example, the results are:
ls <- list(list(a=1, b=2), list(a=3, b=4), list(a=2,b=4), list(a=5,b=2), list(a=3,b=2))
#$a
#[1] 1 3 2 5 3
#$b
#[1] 2 4 4 2 2

Remove elements in a list in R

I want to remove part of the list where it is a complete set of the other part of the list. For example, B intersect A and E intersect C, therefore B and E should be removed.
MyList <- list(A=c(1,2,3,4,5), B=c(3,4,5), C=c(6,7,8,9), E=c(7,8))
MyList
$A
[1] 1 2 3 4 5
$B
[1] 3 4 5
$C
[1] 6 7 8 9
$E
[1] 7 8
MyListUnique <- RemoveSubElements(MyList)
MyListUnique
$A
[1] 1 2 3 4 5
$C
[1] 6 7 8 9
Any ideas ? Any know function to do it ?
As long as your data is not too huge, you can use an approach like the following:
# preparation
MyList <- MyList[order(lengths(MyList))]
idx <- vector("list", length(MyList))
# loop through list and compare with other (longer) list elements
for(i in seq_along(MyList)) {
idx[[i]] <- any(sapply(MyList[-seq_len(i)], function(x) all(MyList[[i]] %in% x)))
}
# subset the list
MyList[!unlist(idx)]
#$C
#[1] 6 7 8 9
#
#$A
#[1] 1 2 3 4 5
Similar to the other answer, but hopefully clearer, using a helper function and 2 sapplys.
#helper function to determine a proper subset - shortcuts to avoid setdiff calculation if they are equal
is.proper.subset <- function(x,y) !setequal(x,y) && length(setdiff(x,y))==0
#double loop over the list to find elements which are proper subsets of other elements
idx <- sapply(MyList, function(x) any(sapply(MyList, function(y) is.proper.subset(x,y))))
#filter out those that are proper subsets
MyList[!idx]
$A
[1] 1 2 3 4 5
$C
[1] 6 7 8 9

Apply function to dataframe with changing argument

I have 2 objects:
A data frame with 3 variables:
v1 <- 1:10
v2 <- 11:20
v3 <- 21:30
df <- data.frame(v1,v2,v3)
A numeric vector with 3 elements:
nv <- c(6,11,28)
I would like to compare the first variable to the first number, the second variable to the second number and so on.
which(df$v1 > nv[1])
which(df$v2 > nv[2])
which(df$v3 > nv[3])
Of course in reality my data frame has a lot more variables so manually typing each variable is not an option.
I encounter these kinds of problems quite frequently. What kind of documentation would I need to read to be fluent in these matters?
One option would be to compare with equally sized elements. For this we can replicate the elements in 'nv' each by number of rows of 'df' (rep(nv, each=nrow(df))) and compare with df or use the col function that does similar output as rep.
which(df > nv[col(df)], arr.ind=TRUE)
If you need a logical matrix that corresponds to comparison of each column with each element of 'nv'
sweep(df, 2, nv, FUN='>')
You could also use mapply:
mapply(FUN=function(x, y)which(x > y), x=df, y=nv)
#$v1
#[1] 7 8 9 10
#
#$v2
#[1] 2 3 4 5 6 7 8 9 10
#
#$v3
#[1] 9 10
I think these sorts of situations are tricky because normal looping solutions (e.g. the apply function) only loop through one object, but you need to loop both through df and nv simultaneously. One approach is to loop through the indices and to use them to grab the appropriate information from both df and nv. A convenient way to loop through indices is the sapply function:
sapply(seq_along(nv), function(x) which(df[,x] > nv[x]))
# [[1]]
# [1] 7 8 9 10
#
# [[2]]
# [1] 2 3 4 5 6 7 8 9 10
#
# [[3]]
# [1] 9 10

Resources