Adding lists together with a loop - r

I'm trying to add lists together using a loop. Here is some example data.
df <- data.frame(var1 = c(1,1,2,2,2,2,3,3,3,3,3), var2= 1:11)
> df
var1 var2
1 1 1
2 1 2
3 2 3
4 2 4
5 2 5
6 2 6
7 3 7
8 3 8
9 3 9
10 3 10
11 3 11
I've run this loop code, and would like the items to be stored in a file that contains 3 lists
list_container <- list()
for (i in unique(df$var1) ) {
templist <- df[ df$var1==i , "var2"]
list_container <- list(list_container, templist)
}
it doesn't work, and ends up looking like this
> list_container
[[1]]
[[1]][[1]]
[[1]][[1]][[1]]
list()
[[1]][[1]][[2]]
[1] 1 2
[[1]][[2]]
[1] 3 4 5 6
[[2]]
[1] 7 8 9 10 11
I want the 3 sets of list to sit separately, it should end up like this
list_result <- list(1:2, 3:6, 7:11)
> list_result
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11
Is there anyway I can modify my code to get the desired result? Any help greatly appreciated. Thanks

split would be more direct and faster
with(df, unname(split(var2, var1)))
-output
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11
If we want to use the == with unique elements, initialize with a NULL list of length same as the length of unique elements of 'var1' column. Loop over the sequence of unique elements, and assign the subset of 'var2' to the ith element of 'list_container'
un1 <- unique(df$var1)
list_container <- vector('list', length(un1))
for(i in seq_along(un1))
list_container[[i]] <- df$var2[df$var1 == un1[i]]
-output
list_container
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11

Another base R option using tapply
> with(df, tapply(var2, var1, c))
$`1`
[1] 1 2
$`2`
[1] 3 4 5 6
$`3`
[1] 7 8 9 10 11
or aggregate
> aggregate(var2 ~ ., df, c)$var2
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11

You could also use unstack:
unstack(df, var2~var1)
$`1`
[1] 1 2
$`2`
[1] 3 4 5 6
$`3`
[1] 7 8 9 10 11
if you do not want the names, you can get rid of them:
unname(unstack(df, var2~var1))
[[1]]
[1] 1 2
[[2]]
[1] 3 4 5 6
[[3]]
[1] 7 8 9 10 11

Related

How can you generate multiple vectors of different length in R?

I would like to use a for loop to generate multiple vectors and save their values for later use. The end result ideally would be:
vector_1 = c(1)
vector_2 = c(1,2,3)
vector_3 = c(1,2,3,4,5,6)
.
.
.
vector_i = c(1,2,3,...,n) #for some n generated during the loop. This n does not always have an upper bound.
This is so that I can use each vector later on to plot multiple lines on the same graph with the axis of the graph scaled correctly.
The following code is the best example I can come up with to try and describe the idea but obviously using 'vector_i' for each loop is not going to work.
for (i in 1:n){
length = sample(1:i^2,1)
vector_i = seq(1,length)
}
You could use the following function:
make_vectors <- function(n) lapply(seq(n), function(i) seq(sample(i^2, 1)))
Which allows:
vector <- make_vectors(5)
vector
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 1 2 3 4
#>
#> [[3]]
#> [1] 1 2 3 4
#>
#> [[4]]
#> [1] 1 2 3 4 5 6
#>
#> [[5]]
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
And you can access each one like this:
vector[[5]]
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
This keeps your global environment tidy and allows you to iterate through your vectors more easily than writing them all as independent entities.
We could use sequence
v1 <- sequence(c(1, 3, 6))
v1
#[1] 1 1 2 3 1 2 3 4 5 6
If we need it in a list
split(v1, cumsum(v1 == 1))
#$`1`
#[1] 1
#$`2`
#[1] 1 2 3
#$`3`
#[1] 1 2 3 4 5 6

how to do the systematic permutation in R?

I have a vector including 9 observations and set to 3 treatments. I need to do the systematic permutation,not 9!. It is 9!/(2!3!4!)=1260. first 9 choose 2,then 7 choose 3, rest obs will in treatment 3.
I have already write the code for 2 parts,but I can not figure out how to list all the possible outcomes. Since there are some observations are replicate. the permutation are not quite right. I need to assign the Id for each observation first. I have some problem about how to assign the id and permute id first, then return the real observations.
list is my R code.there are some mistakes. I need a help to complete this program. Thank you!
for (k in 1260) {
allperm<-c(A,B,C)
A <- combn(complete, 2)
for (i in 1:36){
complete_B <- complete[!(complete %in% A[,i])]
B <- combn(complete_B, 3)
for (j in 1:35){
C <- complete_B[!(complete_B %in% c(B[,j], A[,i]))]
}
}
}
here is a base R solution to obtain all permutations allPerms as well as the partitions A,B and C
v <- 1:9
allPerms <- lapply(sapply(combn(v,2,simplify = FALSE),
function(p) combn(v[-p],3,FUN = function(k) c(p,k),simplify = FALSE)),
function(k) c(k,v[-k]))
A <- lapply(allPerms, `[`,1:2)
B <- lapply(allPerms, `[`,3:5)
C <- lapply(allPerms, `[`,6:9)
such that
> head(allPerms)
[[1]]
[1] 1 2 3 4 5 6 7 8 9
[[2]]
[1] 1 2 3 4 6 5 7 8 9
[[3]]
[1] 1 2 3 4 7 5 6 8 9
[[4]]
[1] 1 2 3 4 8 5 6 7 9
[[5]]
[1] 1 2 3 4 9 5 6 7 8
[[6]]
[1] 1 2 3 5 6 4 7 8 9
> head(A)
[[1]]
[1] 1 2
[[2]]
[1] 1 2
[[3]]
[1] 1 2
[[4]]
[1] 1 2
[[5]]
[1] 1 2
[[6]]
[1] 1 2
> head(B)
[[1]]
[1] 3 4 5
[[2]]
[1] 3 4 6
[[3]]
[1] 3 4 7
[[4]]
[1] 3 4 8
[[5]]
[1] 3 4 9
[[6]]
[1] 3 5 6
> head(C)
[[1]]
[1] 6 7 8 9
[[2]]
[1] 5 7 8 9
[[3]]
[1] 5 6 8 9
[[4]]
[1] 5 6 7 9
[[5]]
[1] 5 6 7 8
[[6]]
[1] 4 7 8 9
If you want outputs in the format of matrix, you can try the code below
v <- 1:9
allPerms <- sapply(sapply(combn(v,2,simplify = FALSE),
function(p) combn(v[-p],3,FUN = function(k) c(p,k),simplify = FALSE)),
function(k) c(k,v[-k]))
A <- allPerms[1:2,]
B <- allPerms[3:5,]
C <- allPerms[6:9,]

Filter different values in each list using vectorized functions

I have a list:
> list
[[1]]
[1] 5 3 7 9 3 8 3 4 5 7
[[2]]
[1] 2 8 7 8 7 9 6 3 1 4
[[3]]
[1] 7 2 1 7 9 8 9 8 8 2
[[4]]
[1] 5 2 2 1 8 8 2 1 10 7
And now I have a list of elements that I want to filter.
> filtering
[[1]]
[1] 11 10 12
[[2]]
[1] 7 3 9
[[3]]
[1] 3 7 8
[[4]]
[1] 2 6 9
I want to filter, without using any looping, list[[1]] with elements in filtering[[1]], then elements from list[[2]] with elements in filtering[[2]], etc...
Something like this (but mapply is still a loop):
# example data
mylist <- list(1:5, 11:15)
myfilter <- list(c(2,4), c(12, 13))
mapply(FUN = function(x, y){ x[ x %in% y] }, mylist, myfilter, SIMPLIFY = FALSE)
# [[1]]
# [1] 2 4
#
# [[2]]
# [1] 12 13
Or as suggested in the comments by #akrun, using purrr package:
library(purrr)
map2(mylist, myfilter, ~ .x[.x %in% .y])

Convert a data.frame/tibble to a list in a grouped manner

I'd like to convert a data frame in a list with a specific format (minimal example ahead).
For the following data frame, I' like to end up with a list of 5 elements which each have a list of 5 elements within themselves.
parameters = tidyr::expand(tibble(id=1:5, value = 1:5 * 2),id,value)
# A tibble: 6 x 2
id value
<int> <dbl>
1 1 2
2 1 4
3 1 6
4 1 8
5 1 10
6 2 2
result = base::split(parameters, list(parameters$id))
result = lapply(result, function(x) { x["id"] <- NULL; x })
so far it gets the id right (5 elements) but not the sub-elements within them. It gives for the first element
> a[1]
$`1`
# A tibble: 5 x 1
value
<dbl>
1 2
2 4
3 6
4 8
5 10
whereas I'd like to have it as in
> as.list(a$`1`$value)
[[1]]
[1] 2
[[2]]
[1] 4
[[3]]
[1] 6
[[4]]
[1] 8
[[5]]
[1] 10
such that the outcome looks something like this
[1]
[[1]]
[1] 2
[[2]]
[1] 4
[[3]]
[1] 6
[[4]]
[1] 8
[[5]]
[1] 10
[2]
[[1]]
[1] 2
[[2]]
[1] 4
[[3]]
[1] 6
[[4]]
[1] 8
[[5]]
[1] 10
Is there any elegant way to achieve this?
Wrap as.list in lapply should do what you want:
lapply(with(parameters, split(value, id)), as.list)
$`1`
$`1`[[1]]
[1] 2
$`1`[[2]]
[1] 4
$`1`[[3]]
[1] 6
$`1`[[4]]
[1] 8
$`1`[[5]]
[1] 10
$`2`
$`2`[[1]]
[1] 2
$`2`[[2]]
[1] 4
$`2`[[3]]
[1] 6
$`2`[[4]]
[1] 8
$`2`[[5]]
[1] 10

How do I split a vector into a list of vectors when a condition is met?

I would like to split a vector of into a list of vectors. The resulting vectors will be of variable length, and I need the split to occur only when certain conditions are met.
Sample data:
set.seed(3)
x <- sample(0:9,100,repl=TRUE)
For example, in this case I would like to split the above vector x at each 0.
Currently I do this with my own function:
ConditionalSplit <- function(myvec, splitfun) {
newlist <- list()
splits <- which(splitfun(x))
if (splits == integer(0)) return(list(myvec))
if (splits[1] != 1) newlist[[1]] <- myvec[1:(splits[1]-1)]
i <- 1
imax <- length(splits)
while (i < imax) {
curstart <- splits[i]
curend <- splits[i+1]
if (curstart != curend - 1)
newlist <- c(newlist, list(myvec[curstart:(curend-1)]))
i <- i + 1
}
newlist <- c(newlist, list(myvec[splits[i]:length(vector)]))
return(newlist)
}
This function gives the output I'd like, but I'm certain there's a better way than mine.
> MySplit <- function(x) x == 0
> ConditionalSplit(x, MySplit)
[[1]]
[1] 1 8 3 3 6 6 1 2 5 6 5 5 5 5 8 8 1 7 8 2 2
[[2]]
[1] 0 1
[[3]]
[1] 0 2 7 5 9 5 7 3 3 1 4 2 3 8 2 5 2 2 7 1 5 4 2
...
The following line seems to work just fine:
split(x,cumsum(x==0))
Another solution is to use tapply. A good reason to use tapply instead of split is because it lets you perform other operations on the items in the list while you're splitting it.
For example, in this solution to the question:
> x <- sample(0:9,100,repl=TRUE)
> idx <- cumsum(x==0)
> splitList <- tapply(x, idx, function(y) {list(y)})
> splitList
$`0`
[1] 2 9 2
$`1`
[1] 0 5 5 3 8 4
$`2`
[1] 0 2 5 2 6 2 2
$`3`
[1] 0 8 1 7 5
$`4`
[1] 0 1 6 6 3 8 7 2 4 2 3 1
$`5`
[1] 0 6 8 9 9 1 1 2
$`6`
[1] 0 1 2 2 2 7 8 1 9 7 9 3 4 8 4 6 4 5 3 1
$`7`
[1] 0 2 7 8 5
$`8`
[1] 0 3 4 8 4 7 3
$`9`
[1] 0 8 4
$`10`
[1] 0 4 3 9 9 8 7 4 4 5 5 1 1 7 3 9 7 4 4 7 7 6 3 3
Can be modified so that you divide each element by the number of elements in that list.
list(y/length(y))
instead of
list(y)

Resources