Извлекать переменные в формуле из кадра данных

У меня есть формула, содержащая некоторые термины и кадр данных (вывод более раннего вызова model.frame()), который содержит все эти термины и еще несколько. Я хочу, чтобы подмножество рамки модели содержало только переменные, которые фигурируют в формуле.

ff <- log(Reaction) ~ log(1+Days) + x + y
fr <- data.frame(`log(Reaction)`=1:4,
                 `log(1+Days)`=1:4,
                 x=1:4,
                 y=1:4,
                 z=1:4,
                 check.names=FALSE)

Желаемый результат fr минус столбец z (fr[,1:4] изменяет - мне нужно программное решение...)

Некоторые стратегии, которые не работают:

fr[all.vars(ff)]
## Error in `[.data.frame`(fr, all.vars(ff)) : undefined columns selected

(потому что all.vars() получает "Reaction", а не log("Reaction"))

stripwhite <- function(x) gsub("(^ +| +$)","",x)
vars <- stripwhite(unlist(strsplit(as.character(ff)[-1],"\\+")))
fr[vars]
## Error in `[.data.frame`(fr, vars) : undefined columns selected

(поскольку расщепление на + ложно разбивает член log(1+Days)).

Я думал о том, чтобы спуститься по дереву разбора формулы:

ff[[3]]       ## log(1 + Days) + x + y
ff[[3]][[1]]  ## `+`
ff[[3]][[2]]  ## log(1 + Days) + x

но у меня нет решения, и кажется, что я иду по кроличьей дыре. Идеи?

Ответ 1

Это должно работать:

> fr[gsub(" ","",rownames(attr(terms.formula(ff), "factors")))]
  log(Reaction) log(1+Days) x y
1             1           1 1 1
2             2           2 2 2
3             3           3 3 3
4             4           4 4 4

И реквизит Романа Луштрика для указания меня в правильном направлении.

Edit: Похоже, вы могли бы отключить его и от атрибута "variables":

fr[gsub(" ","",attr(terms(ff),"variables")[-1])]

Изменить 2: найти первый проблемный случай с участием I() или offset():

ff <- I(log(Reaction)) ~ I(log(1+Days)) + x + y
fr[gsub(" ","",attr(terms(ff),"variables")[-1])]

Тем не менее, это можно было бы легко исправить с помощью регулярного выражения. НО, если бы у вас были ситуации, подобные в вопросе, где вызывается переменная, например, log(x) и используется в формуле рядом с чем-то вроде I(log(y)) для переменной y, это будет действительно беспорядочно.

Ответ 2

Мне кажется, что единственная проблема заключается в отсутствии пробела во имя второго столбца fr. Переименуйте его в пространстве и вытащите столбцы следующим образом:

ff <- log(Reaction) ~ log(1+Days) + x + y
fr <- data.frame(`log(Reaction)`=1:4,
                 `log(1 + Days)`=1:4,
                 x=1:4,
                 y=1:4,
                 z=1:4,
                 check.names=FALSE)


fr[labels(terms(ff))]

Если вы считаете, что единственная разница между ними всегда будет заключаться в том, что имена fr имеют пробелы, где имена в ff не совпадают, то указанное выше решение выполняется. Мне нравится labels(terms(x)) немного больше, хотя, потому что это кажется немного более абстрактным.

fr[gsub(pattern = ' ', replacement = '', x = labels(terms(ff)))]