Ben Bolker - 2 months ago 22

R Question

R has a handy tool for manipulating formulas,

`update.formula()`

`x`

`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`

`?update.formula`

[after substituting, ...] The result is then simplifiedvia‘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()`

Answer

**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
```