2016-10-28 1 views
10

R verfügt über ein handliches Werkzeug zum Bearbeiten von Formeln, update.formula(). Dies funktioniert gut, wenn Sie etwas wie "Formel, die alle Begriffe in der vorherigen Formel außer x enthält", z.Entfernen von Versatzausdrücken aus einer Formel

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

Allerdings scheint dies nicht mit Offset Bedingungen zu arbeiten:

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

Ich habe so weit wie terms.formula gegraben nach unten, die ?update.formula Hinweise:

[nach Substitution , ...] Das Ergebnis wird dann vereinfacht über 'terms.formula (simplify = TRUE)'.

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

(dh dies scheint nicht offset(b) zu entfernen ...)

Ich weiß, dass ich eine Lösung zerhacken entweder durch deparse() und Textverarbeitung, oder durch die Formel Verarbeitung rekursiv, um den Begriff zu entfernen, den ich nicht möchte, aber diese Lösungen sind hässlich und/oder ärgerlich zu implementieren. Entweder Erleuchtung wie zu warum dies nicht funktioniert, oder eine einigermaßen kompakte Lösung, wäre toll ...

+2

ein wenig mehr in den Code der Graben 'terms.formula' weist darauf hin, dass ** der Offsetterm explizit beibehalten wird **, obwohl dies offenbar noch nirgends dokumentiert ist ... –

+3

Wenn man'? offset' sieht, steht in der Dokumentation: "Es kann mehr geben als ein Offset in einer Modellformel, aber - ist n ot wird für Offsetterme unterstützt (und entspricht +). "'. Könnte das der Grund dafür sein, dass deine offset() Begriffe nicht einfach sind? –

+0

Nicht die glamouröseste, aber könnten Sie auch versuchen, stattdessen einen Offset (-b) hinzuzufügen? Ihre Formel sieht nicht vereinfacht aus, aber ich denke, der Effekt sollte derselbe sein. Wenn Sie versuchen, lm (mpg ~ cyl, data = mtcars); lm (mpg ~ cyl + offset (disp), data = mtcars); lm (mpg ~ cyl + offset (disp) + offset (-disp), data = mtcars); 'Sie sehen die 1. und 3.' lm() 's sind die gleichen. –

Antwort

7

1) Rekursion Recursively durch die Formel rep absteigen Schnürung offset(...) mit offset und dann offset mit update entfernen. Es wird keine Zeichenfolgenmanipulation durchgeführt und obwohl es eine Anzahl von Codezeilen erfordert, ist es immer noch ziemlich kurz und entfernt einzelne und mehrere offset Begriffe.

Wenn mehrere Offsets vorhanden sind, können einige davon beibehalten werden, indem preserve festgelegt wird. Wenn beispielsweise preserve = 2 dann der zweite Offset beibehalten wird und alle anderen entfernt werden. Der Standardwert ist, keine zu behalten, d. H., Sie alle zu entfernen.

no.offset <- function(x, preserve = NULL) { 
    k <- 0 
    proc <- function(x) { 
    if (length(x) == 1) return(x) 
    if (x[[1]] == as.name("offset") && !((k<<-k+1) %in% preserve)) 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 

Beachten Sie, dass wenn Sie das preserve Argument nicht dann die Zeile Initialisierung k weggelassen werden kann und die if vereinfacht:

if (x[[1]] == as.name("offset")) return(x[[1]]) 

2) Begriffe diese Weder verwendet String-Manipulation direkt noch Rekursion.Zuerst erhalten Sie das terms Objekt, zap sein offset Attribut und beheben Sie es mit fixFormulaObject, die wir aus den Eingeweiden von terms.formula extrahieren. Dies könnte etwas weniger spröde gemacht werden, indem der Quellcode fixFormulaObject in die Quelle kopiert und die Zeile eval entfernt wird. preserve wirkt wie in (1).

no.offset2 <- function(x, preserve = NULL) { 
    tt <- terms(x) 
    attr(tt, "offset") <- if (length(preserve)) attr(tt, "offset")[preserve] 
    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 

Beachten Sie, dass, wenn Sie die preserve Argument nicht brauchen dann die Zeile, die die Offset-Attribut zappt kann vereinfacht werden:

attr(tt, "offset") <- NULL 
+0

Es ist mir nicht klar, ob das genau das Verhalten ist, das OP suchte. In einer Formel kann mehr als ein Offsetterm vorkommen, und diese Methode entfernt alle. Ich hatte den Eindruck, dass OP nur bestimmte Ausdrücke in der Formel entfernen wollte, wie zum Beispiel "offset (b)", was bedeuten würde, dass "offset (c)" an Ort und Stelle bleibt. Vielleicht kann @BenBolker kommentieren, welches Verhalten benötigt wird? – dww

+0

Nicht sicher, dass dies wichtig ist, aber die Funktion zu (1) und (2) hinzugefügt haben. –

4

Dies scheint von Entwurf zu sein. Aber eine einfache Abhilfe ist

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

Wenn Sie die Flexibilität brauchen Formeln zu akzeptieren, die tut umfassen offset(), zum Beispiel, wenn die Formel durch ein Paket Benutzer zur Verfügung gestellt wird, die von der Notwendigkeit nicht bewusst sein kann offset2 an seinem Platz zu verwenden von offset, fügen Sie dann sollten wir auch eine Linie alle Instanzen von offset() in der eingehenden Formel zu ändern:

f3 <- z ~ a + offset(b) 

f4 <- as.formula(gsub("offset\\(", "offset2(", deparse(f3))) 
f4 <- update(f4, . ~ . - offset2(b)) 

# finally, just in case there are any references to offset2 remaining, we should revert them back to offset 
f4 <- as.formula(gsub("offset2\\(", "offset(", deparse(f4))) 
# z ~ a 
+0

das ist in Ordnung für ben, aber wenn der Benutzer die Formel zu seinem Paket gibt, sag, dann müssten sie vorher über diesen Vorbehalt Bescheid wissen, richtig? – rawr

+0

@rawr - ja, wenn die beabsichtigte Verwendung ein Paket ist, wo andere Benutzer die Formel liefern, dann wäre dies ein Problem. Dann wäre es notwendig, die Formel zu entern und jede Instanz von offset mit offset2 in Bens Paket zu ersetzen. Beginnt, hässlich zu werden dann – dww

Verwandte Themen