Is there a better alternative than string manipulation to programmatically build formulas?

Solution 1:

reformulate will do what you want.

reformulate(termlabels = c('x','z'), response = 'y')
## y ~ x + z

Or without an intercept

reformulate(termlabels = c('x','z'), response = 'y', intercept = FALSE)
## y ~ x + z - 1

Note that you cannot construct formulae with multiple reponses such as x+y ~z+b

reformulate(termlabels = c('x','y'), response = c('z','b'))
z ~ x + y

To extract the terms from an existing formula (given your example)

attr(terms(RHS), 'term.labels')
## [1] "a" "b"

To get the response is slightly different, a simple approach (for a single variable response).

as.character(LHS)[2]
## [1] 'y'


combine_formula <- function(LHS, RHS){
  .terms <- lapply(RHS, terms)
  new_terms <- unique(unlist(lapply(.terms, attr, which = 'term.labels')))
  response <- as.character(LHS)[2]

  reformulate(new_terms, response)


}


combine_formula(LHS, list(RHS, RHS2))

## y ~ a + b + c
## <environment: 0x577fb908>

I think it would be more sensible to specify the response as a character vector, something like

combine_formula2 <- function(response, RHS, intercept = TRUE){
  .terms <- lapply(RHS, terms)
  new_terms <- unique(unlist(lapply(.terms, attr, which = 'term.labels')))
  response <- as.character(LHS)[2]

  reformulate(new_terms, response, intercept)


}
combine_formula2('y', list(RHS, RHS2))

you could also define a + operator to work with formulae (update setting an new method for formula objects)

`+.formula` <- function(e1,e2){
  .terms <- lapply(c(e1,e2), terms)
  reformulate(unique(unlist(lapply(.terms, attr, which = 'term.labels'))))
}

RHS + RHS2
## ~a + b + c

You can also use update.formula using . judiciously

 update(~a+b, y ~ .)
 ##  y~a+b