OK so here’s an update to the permutation pyramid I made.

I had to make a list of different permutations of models but it became hard to keep track of all the different variables and their combinations. Hence, the permutation pyramid. The goal is to make a list of vector combinations and permutations. I found this super useful when generating formulas for AICc/Model selection.

Enjoy!

The function has a few options:

- vec
- The vector to draw the combinations/permutations from

- order.matters = FALSE
- A simple boolean TRUE / FALSE to add in the permutations of each vector combination. This will generate permutations of the combinations and add them to the list. This may only be useful for certain models

- req
- A vector of elements that should be required in the combinations.

- interact
- A list of vector combinations that can be found in the original combinations

## Function

pyramid <- function( vec, order.matters = FALSE, req, interact ){
# pyramid of variable combinations
# this doesn't include different
# arrangements
vrz <- lapply(
1:length( vec ),
function( x ){
combn( vec, x ) %>% as.data.frame()
}
) %>% purrr::flatten() %>% unname()
# If there are interactions
if( !missing( interact ) ){
# possible interaction combos
intx <- seq( from = 1, to = length( interact ), by = 1 ) %>% knp.perm.pyramid()
# extra interactions
vrz.int <- list()
# look through vars
for( v in vrz ){
# make a list of interactions
# and add them to the list
vrz.int <- append(
vrz.int,
lapply( intx, function( ints ){
# count number of matches to compare
# and filter out unaltered var lists
mt <- 0
# for each interaction
# check if its in the array
# then add it if it is
for ( int in ints ) {
if(
length( intersect( v, interact[[ int ]] ) ) == length( interact[[ int ]] ) &
length( interact[[ int ]] ) > 1
){
v <- c( v, paste0( interact[[ int ]], collapse = ":" ) )
mt <- mt + 1
}
}
# if the number of matches equals
# the number of interactions then
# return the altered array
if( mt == length(ints) ){
return( v )
}
return( NULL )
}) %>% plyr::compact()
)
}
# append interactions
vrz <- append( vrz, vrz.int )
}
# order matters so lets rearrange
if( order.matters ){
vrz <- lapply( vrz, function( x ){
combinat::permn( x )
}) %>% purrr::flatten()
}
# if there's any required variables in each combination
if( !missing( req ) ){
vrz <- lapply(vrz, function( x ){
if( length( intersect( x, req ) ) == length( req ) ){
return( x )
}
return( NULL )
}) %>% plyr::compact()
}
# return list of character
# permutations
return( vrz )
}

## Examples / Usage

Plain vanilla use

> a <- c( "A", "B", "C" )
> pyramid( a )
[[1]]
[1] "A"
[[2]]
[1] "B"
[[3]]
[1] "C"
[[4]]
[1] "A" "B"
[[5]]
[1] "A" "C"
[[6]]
[1] "B" "C"
[[7]]
[1] "A" "B" "C"

If the order of the elements matters

> a <- c( "A", "B", "C" )
> pyramid( a, order.matters = TRUE )
[[1]]
[1] "A"
[[2]]
[1] "B"
[[3]]
[1] "C"
[[4]]
[1] "A" "B"
[[5]]
[1] "B" "A"
[[6]]
[1] "A" "C"
[[7]]
[1] "C" "A"
[[8]]
[1] "B" "C"
[[9]]
[1] "C" "B"
[[10]]
[1] "A" "B" "C"
[[11]]
[1] "A" "C" "B"
[[12]]
[1] "C" "A" "B"
[[13]]
[1] "C" "B" "A"
[[14]]
[1] "B" "C" "A"
[[15]]
[1] "B" "A" "C"

Require variables

> a <- c( "A", "B", "C", "D", "E" )
> b <- c( "B", "D" )
> pyramid( a, req = b )
[[1]]
[1] "B" "D"
[[2]]
[1] "A" "B" "D"
[[3]]
[1] "B" "C" "D"
[[4]]
[1] "B" "D" "E"
[[5]]
[1] "A" "B" "C" "D"
[[6]]
[1] "A" "B" "D" "E"
[[7]]
[1] "B" "C" "D" "E"
[[8]]
[1] "A" "B" "C" "D" "E"

Include interactions

> a <- c( "A", "B", "C")
> b <- list( c( "A", "B" ), c( "A", "B", "C" ) )
> pyramid( a, interact = b )
[[1]]
[1] "A"
[[2]]
[1] "B"
[[3]]
[1] "C"
[[4]]
[1] "A" "B"
[[5]]
[1] "A" "C"
[[6]]
[1] "B" "C"
[[7]]
[1] "A" "B" "C"
[[8]]
[1] "A" "B" "A:B"
[[9]]
[1] "A" "B" "C" "A:B"
[[10]]
[1] "A" "B" "C" "A:B:C"
[[11]]
[1] "A" "B" "C" "A:B" "A:B:C"