Skip to content

Instantly share code, notes, and snippets.

@mjskay
Last active June 6, 2022 23:36
Show Gist options
  • Select an option

  • Save mjskay/cd70fa5671600d7c51db24ab47662812 to your computer and use it in GitHub Desktop.

Select an option

Save mjskay/cd70fa5671600d7c51db24ab47662812 to your computer and use it in GitHub Desktop.

All combinations of right-hand-side terms in a formula

Given this formula…

fixed = y ~ x1 + x2 + x3 + z1 + x3:z1

We can extract all the terms as bare expressions (I prefer this to using attr(terms(fixed), "term.labels") because the latter returns a character vector, and I prefer to stick to manipulating unevaluated expressions rather than parsing / unparsing — too much room for corner cases):

get_terms = function(x) {
  if (length(x) > 1 && x[[1]] == quote(`+`)) c(get_terms(x[[2]]), get_terms(x[[3]]))
  else x
}
terms = get_terms(fixed[[3]])
terms
## [[1]]
## x1
## 
## [[2]]
## x2
## 
## [[3]]
## x3
## 
## [[4]]
## z1
## 
## [[5]]
## x3:z1

Then we’ll build a matrix where rows are combinations and columns indicate if each term is present in that combination:

term_logicals = rep(list(c(TRUE, FALSE)), length(terms))
is_term_present = as.matrix(expand.grid(term_logicals))
is_term_present
##        Var1  Var2  Var3  Var4  Var5
##  [1,]  TRUE  TRUE  TRUE  TRUE  TRUE
##  [2,] FALSE  TRUE  TRUE  TRUE  TRUE
##  [3,]  TRUE FALSE  TRUE  TRUE  TRUE
##  [4,] FALSE FALSE  TRUE  TRUE  TRUE
##  [5,]  TRUE  TRUE FALSE  TRUE  TRUE
##  [6,] FALSE  TRUE FALSE  TRUE  TRUE
##  [7,]  TRUE FALSE FALSE  TRUE  TRUE
##  [8,] FALSE FALSE FALSE  TRUE  TRUE
##  [9,]  TRUE  TRUE  TRUE FALSE  TRUE
## [10,] FALSE  TRUE  TRUE FALSE  TRUE
## [11,]  TRUE FALSE  TRUE FALSE  TRUE
## [12,] FALSE FALSE  TRUE FALSE  TRUE
## [13,]  TRUE  TRUE FALSE FALSE  TRUE
## [14,] FALSE  TRUE FALSE FALSE  TRUE
## [15,]  TRUE FALSE FALSE FALSE  TRUE
## [16,] FALSE FALSE FALSE FALSE  TRUE
## [17,]  TRUE  TRUE  TRUE  TRUE FALSE
## [18,] FALSE  TRUE  TRUE  TRUE FALSE
## [19,]  TRUE FALSE  TRUE  TRUE FALSE
## [20,] FALSE FALSE  TRUE  TRUE FALSE
## [21,]  TRUE  TRUE FALSE  TRUE FALSE
## [22,] FALSE  TRUE FALSE  TRUE FALSE
## [23,]  TRUE FALSE FALSE  TRUE FALSE
## [24,] FALSE FALSE FALSE  TRUE FALSE
## [25,]  TRUE  TRUE  TRUE FALSE FALSE
## [26,] FALSE  TRUE  TRUE FALSE FALSE
## [27,]  TRUE FALSE  TRUE FALSE FALSE
## [28,] FALSE FALSE  TRUE FALSE FALSE
## [29,]  TRUE  TRUE FALSE FALSE FALSE
## [30,] FALSE  TRUE FALSE FALSE FALSE
## [31,]  TRUE FALSE FALSE FALSE FALSE
## [32,] FALSE FALSE FALSE FALSE FALSE

Then we’ll get the corresponding terms and stick them together to create the right-hand side of each formula:

combinations = apply(is_term_present, 1, \(i) terms[i])
rhs = lapply(combinations, \(l) Reduce(\(x, y) bquote(.(x) + .(y)), l))
rhs
## [[1]]
## x1 + x2 + x3 + z1 + x3:z1
## 
## [[2]]
## x2 + x3 + z1 + x3:z1
## 
## [[3]]
## x1 + x3 + z1 + x3:z1
## 
## [[4]]
## x3 + z1 + x3:z1
## 
## [[5]]
## x1 + x2 + z1 + x3:z1
## 
## [[6]]
## x2 + z1 + x3:z1
## 
## [[7]]
## x1 + z1 + x3:z1
## 
## [[8]]
## z1 + x3:z1
## 
## [[9]]
## x1 + x2 + x3 + x3:z1
## 
## [[10]]
## x2 + x3 + x3:z1
## 
## [[11]]
## x1 + x3 + x3:z1
## 
## [[12]]
## x3 + x3:z1
## 
## [[13]]
## x1 + x2 + x3:z1
## 
## [[14]]
## x2 + x3:z1
## 
## [[15]]
## x1 + x3:z1
## 
## [[16]]
## x3:z1
## 
## [[17]]
## x1 + x2 + x3 + z1
## 
## [[18]]
## x2 + x3 + z1
## 
## [[19]]
## x1 + x3 + z1
## 
## [[20]]
## x3 + z1
## 
## [[21]]
## x1 + x2 + z1
## 
## [[22]]
## x2 + z1
## 
## [[23]]
## x1 + z1
## 
## [[24]]
## z1
## 
## [[25]]
## x1 + x2 + x3
## 
## [[26]]
## x2 + x3
## 
## [[27]]
## x1 + x3
## 
## [[28]]
## x3
## 
## [[29]]
## x1 + x2
## 
## [[30]]
## x2
## 
## [[31]]
## x1
## 
## [[32]]
## NULL

Formulas would then be something like:

lapply(rhs, \(r) bquote(y ~ .(r)))
## [[1]]
## y ~ x1 + x2 + x3 + z1 + x3:z1
## 
## [[2]]
## y ~ x2 + x3 + z1 + x3:z1
## 
## [[3]]
## y ~ x1 + x3 + z1 + x3:z1
## 
## [[4]]
## y ~ x3 + z1 + x3:z1
## 
## [[5]]
## y ~ x1 + x2 + z1 + x3:z1
## 
## [[6]]
## y ~ x2 + z1 + x3:z1
## 
## [[7]]
## y ~ x1 + z1 + x3:z1
## 
## [[8]]
## y ~ z1 + x3:z1
## 
## [[9]]
## y ~ x1 + x2 + x3 + x3:z1
## 
## [[10]]
## y ~ x2 + x3 + x3:z1
## 
## [[11]]
## y ~ x1 + x3 + x3:z1
## 
## [[12]]
## y ~ x3 + x3:z1
## 
## [[13]]
## y ~ x1 + x2 + x3:z1
## 
## [[14]]
## y ~ x2 + x3:z1
## 
## [[15]]
## y ~ x1 + x3:z1
## 
## [[16]]
## y ~ x3:z1
## 
## [[17]]
## y ~ x1 + x2 + x3 + z1
## 
## [[18]]
## y ~ x2 + x3 + z1
## 
## [[19]]
## y ~ x1 + x3 + z1
## 
## [[20]]
## y ~ x3 + z1
## 
## [[21]]
## y ~ x1 + x2 + z1
## 
## [[22]]
## y ~ x2 + z1
## 
## [[23]]
## y ~ x1 + z1
## 
## [[24]]
## y ~ z1
## 
## [[25]]
## y ~ x1 + x2 + x3
## 
## [[26]]
## y ~ x2 + x3
## 
## [[27]]
## y ~ x1 + x3
## 
## [[28]]
## y ~ x3
## 
## [[29]]
## y ~ x1 + x2
## 
## [[30]]
## y ~ x2
## 
## [[31]]
## y ~ x1
## 
## [[32]]
## y ~ NULL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment