Ben Bolker - 1 year ago 65
R Question

# removing offset terms from a formula

R has a handy tool for manipulating formulas,

`update.formula()`
. This works nicely when you want to get something like "formula containing all terms in previous formula except
`x`
", e.g.

``````f1 <- z ~ a + b + c
(f2 <- update.formula(f1, . ~ . - c))
## z ~ a + b
``````

However, this doesn't seem to work with offset terms:

``````f3 <- z ~ a + offset(b)
update(f3, . ~ . - offset(b))
## z ~ a + offset(b)
``````

I've dug down as far as
`terms.formula`
, which
`?update.formula`
references:

[after substituting, ...] The result is then simplified via ‘terms.formula(simplify = TRUE)’.

``````terms.formula(z ~ a + offset(b) - offset(b), simplify=TRUE)
## z ~ a + offset(b)
``````

(i.e., this doesn't seem to remove
`offset(b)`
...)

I know I can hack up a solution either by using
`deparse()`
and text-processing, or by processing the formula recursively to remove the term I don't want, but these solutions are ugly and/or annoying to implement. Either enlightenment as to why this doesn't work, or a reasonably compact solution, would be great ...

1) Recursion Recursively descend through the formula replacing `offset(...)` with `offset` and then remove `offset` using `update`. No string manipulation is done and although it does require a number of lines of code it's still fairly short and does remove single and multiple `offset` terms.

If there are multiple offsets one can preserve some of them by setting `subset` so, for example, if `subset = 2` then the second offset is preserved and any others are removed. The default is to preserve none, i.e. remove them all.

``````no.offset <- function(x, subset = NULL) {
k <- 0
proc <- function(x) {
if (length(x) == 1) return(x)
if (x[[1]] == as.name("offset") && !((k<<-k+1) %in% subset)) return(x[[1]])
replace(x, -1, lapply(x[-1], proc))
}
update(proc(x), . ~ . - offset)
}

# tests

no.offset(z ~ a + offset(b))
## z ~ a

no.offset(z ~ a + offset(b) + offset(c))
## z ~ a
``````

2) terms this neither uses string manipulation directly nor recursion. First get the `terms` object, zap it's `offset` attribute and fix it using `fixFormulaObject` which we extract out of the guts of `terms.formula`. This could be made a bit less brittle by copying the source code of `fixFormulaObject` into your source and removing the `eval` line below. `subset` acts as in (1).

``````no.offset2 <- function(x, subset = NULL) {
tt <- terms(x)
attr(tt, "offset") <- if (length(subset)) attr(tt, "offset")[subset]
eval(body(terms.formula)[[2]]) # extract fixFormulaObject
f <- fixFormulaObject(tt)
environment(f) <- environment(x)
f
}

# tests

no.offset2(z ~ a + offset(b))
## z ~ a

no.offset2(z ~ a + offset(b) + offset(c))
## z ~ a
``````
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download