how to do the systematic permutation in R? - 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,]

Related

Adding lists together with a loop

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

How can I make this iterative combinaiton loop code work with more than x per combination?

I found this life-saving code created by #QuantIbex. It creates combinations of variables iteratively to be used in a loop without generating them in advance or storing them.
First answer here
## Function definition
gen.next.cbn <- function(cbn, n){
## Generates the combination that follows the one provided as input
cbn.bin <- rep(0, n)
cbn.bin[cbn] <- 1
if (tail(cbn.bin, 1) == 0){
ind <- tail(which(cbn.bin == 1), 1)
cbn.bin[c(ind, ind+1)] <- c(0, 1)
}else{
ind <- 1 + tail(which(diff(cbn.bin) == -1), 1)
nb <- sum(cbn.bin[-c(1:ind)] == 1)
cbn.bin[c(ind-1, (n-nb+1):n)] <- 0
cbn.bin[ind:(ind+nb)] <- 1
}
cbn <- which(cbn.bin == 1)
}
## Example parameters
n <- 6
k <- 3
## Iteration example
for (i in 1:choose(n, k)){
if (i == 1){
cbn <- 1:k
}else{
cbn <- gen.next.cbn(cbn, n)
}
print(cbn)
}
# [1] 1 2 3
# [1] 1 2 4
# [1] 1 2 5
# [1] 1 2 6
# [1] 1 3 4
# [1] 1 3 5
# [1] 1 3 6
# [1] 1 4 5
# [1] 1 4 6
# [1] 1 5 6
# [1] 2 3 4
# [1] 2 3 5
# [1] 2 3 6
# [1] 2 4 5
# [1] 2 4 6
# [1] 2 5 6
# [1] 3 4 5
# [1] 3 4 6
# [1] 3 5 6
# [1] 4 5 6
The code itself is beyond my own technical capabilities in R. I have been able to adapt it for my use, and insert my analyses in the loop and it works very well. One of the things I have not been able to figure out is how to make it go through more than x number of variables per combination at a time.
In the code above n = the number of variables to be used to generate the combinations. k = the number of variables per combination. In the example, k=3. I can change k to anything I want, but is there a way for k to a equal a range such as k = 3:10?
I set the values:
n <- 31
k <- 3:10
It stops once done with combinations of 3 and gives me this:
Warning messages:
1: In 1:choose(n, k) :
numerical expression has 8 elements: only the first used
2: In 1:k : numerical expression has 8 elements: only the first used
I know a lot of people warn of how slow loops are, and will probably warn me that combinations of 10 will take forever, but these are things I accept.
Since you don't mind loops, why not just iterate over the elements of k?
## Example parameters
n <- 6
k <- 1:3
for(j in seq_along(k))
{
## Iteration example
for (i in 1:choose(n, k[j])){
if (i == 1){
cbn <- 1:k[j]
}else{
cbn <- gen.next.cbn(cbn, n)
}
print(cbn)
}
}
Output:
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> [1] 5
#> [1] 6
#> [1] 1 2
#> [1] 1 3
#> [1] 1 4
#> [1] 1 5
#> [1] 1 6
#> [1] 2 3
#> [1] 2 4
#> [1] 2 5
#> [1] 2 6
#> [1] 3 4
#> [1] 3 5
#> [1] 3 6
#> [1] 4 5
#> [1] 4 6
#> [1] 5 6
#> [1] 1 2 3
#> [1] 1 2 4
#> [1] 1 2 5
#> [1] 1 2 6
#> [1] 1 3 4
#> [1] 1 3 5
#> [1] 1 3 6
#> [1] 1 4 5
#> [1] 1 4 6
#> [1] 1 5 6
#> [1] 2 3 4
#> [1] 2 3 5
#> [1] 2 3 6
#> [1] 2 4 5
#> [1] 2 4 6
#> [1] 2 5 6
#> [1] 3 4 5
#> [1] 3 4 6
#> [1] 3 5 6
#> [1] 4 5 6
This will work when k is a vector but will keep the same functionalty you had before using a single integer.
As a footnote, choosing a set of 10 from 20 elements using this method (without my addition) takes my computer about a minute, which I would live with if I only had to run it now and again.
I believe
one way to accomplish what you want to start using combn;
But I noticed you are avoiding repeating the elements in the
combinations. So you will need to exclude repetitions... I include example code for a function for it.
Afterwards, you can substitute the loop for lapply for efficiency and use the function.
n<-31
combnWoRepl<-function(n,k){
abc<-combn(1:n,k)
abc<-t(abc)
abc<-data.frame(abc)
colnames(abc)<-c("a","b","c")
abc[!abc$a==abc$b,]
}
ResultList<-lapply(3:10,function(x){
combnWoRepl(n,x)
})

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