#r #function #scope #glm #evaluation
Вопрос:
У меня есть функция, которая вычисляет таблицу и модель (и многое другое…):
fun lt;- function(x, y, formula = y ~ x, data = NULL) { out lt;- list() out$tab lt;- table(x, y) out$mod lt;- glm(formula = formula, family = binomial, data = data) out
}
В формуле мне нужно использовать x
и y
, как указано в вызове функции (например x = DF1$x
, и y = DF1$y
), и переменные из другого фрейма данных (например a
, и b
из DF2
). Это не удается с моей наивной функцией:
fun(x = DF1$x, y = DF1$y, formula = y ~ x a b, data = DF2) # Error in eval(predvars, data, env) : object 'y' not found
Как я могу выполнить поиск в glm x
и y
из среды функций? Я предполагаю, что эта проблема связана с нестандартной оценкой и/или областью охвата, но я понятия не имею, как это исправить.
Данные для примера:
smp lt;- function(x = c(TRUE, FALSE), size = 1e2) { sample(x = x, size = size, replace = TRUE) } DF1 lt;- data.frame(x = smp(), y = smp()) DF2 lt;- data.frame(a = smp(x = LETTERS), b = smp(x = LETTERS))
Ответ №1:
Почему бы просто не добавить x
и y
data
не включить в функцию?
fun lt;- function(x, y, formula = y ~ x, data = NULL) { if(length(x) != length(y) | length(x) != nrow(data) | length(y) != nrow(data))stop("x, y and data need to be the same length.n") data$x lt;- x data$y lt;- y out lt;- list() out$tab lt;- table(x, y) out$mod lt;- glm(formula = formula, family = binomial, data = data) out } fun(x = DF1$x, y = DF1$y, formula = y ~ x a b, data = DF2) # $tab # y # x FALSE TRUE # FALSE 27 29 # TRUE 21 23 # # $mod # Call: glm(formula = formula, family = binomial, data = data) # # Coefficients: # (Intercept) xTRUE aB aC aD aE aF aG aH aI aJ # 3.2761 -1.8197 0.3409 -93.9103 -2.0697 20.6813 -41.5963 -1.1078 18.5921 -1.0857 -36.5442 # aK aL aM aN aO aP aQ aR aS aT aU # -0.5730 -92.5513 -3.0672 22.8989 -53.6200 -0.9450 0.4626 -3.0672 0.3570 -22.8857 1.8867 # aV aW aX aY aZ bB bC bD bE bF bG # 2.5307 19.5447 -90.5693 -134.0656 -2.5943 -1.2333 20.7726 110.6790 17.1022 -0.5279 -1.2537 # bH bI bJ bK bL bM bN bO bP bQ bR # -21.7750 114.0199 20.3766 -42.5031 41.1757 -24.3553 -2.0310 -25.9223 -2.9145 51.2537 70.2707 # bS bT bU bV bW bX bY bZ # -4.7728 -3.7300 -2.0333 -0.3906 -0.5717 -4.0728 0.8155 -4.4021 # # Degrees of Freedom: 99 Total (i.e. Null); 48 Residual # Null Deviance: 138.5 # Residual Deviance: 57.73 AIC: 161.7 # # Warning message: # glm.fit: fitted probabilities numerically 0 or 1 occurred #
Комментарии:
1. Это предложение я тоже собирался сделать. Единственная причина, по которой вы не хотели бы этого делать, заключается в том, что переменная с именем
x
уже может существовать в среде формул или в фрейме данных, и вы хотите, чтобы она имела приоритет над той, которая является аргументом функции. Но я не думаю, что операция этого хочет.2. Согласен, но трудно представить ситуацию, соответствующую сообщению ОП, в которой была бы переменная
x
, вdata
которой вы хотели бы иметь приоритет в модели. На самом деле, я думаю, что в данной конкретной ситуации перезапись существующегоx
вdata
будет скорее особенностью, чем недостатком. В целом, однако, я думаю, что ваша осторожность по этому поводу вполне разумна.3. Просто и эффективно! Спасибо! Это также работает с
fun(DF1$x, DF1$y)
тем, чего я с тех пор не ожидалdata = NULL
.4. @user2554330 @DaveArmstrong Решение работает идеально. Однако, не могли бы вы, пожалуйста, объяснить, почему я вообще получил ошибку? Из
glm
документации следует, что данные-это «необязательный фрейм данных, список или среда (или объектas.data.frame
, приводимый к фрейму данных), содержащий переменные в модели. Если не найдено вdata
, переменные берутся изenvironment(formula)
, как правило, среды, из которойglm
вызывается » (выделено мной). Следовательно, почемуx
иy
не берутся из функциональной среды иa
иb
изdata
?5. @user2554330 @DaveArmstrong Чтобы избежать потенциальных проблем , если уже есть переменные с именами
x
иy
вdata
, я добавилstopifnot(! names(x = data) %in% c("x", "y"))
.
Ответ №2:
Ответ @DaveArmstrong, который уже был принят, является правильным. Этот ответ объясняет, почему в исходной версии кода произошла ошибка.
@Thomas процитировал документы в комментарии, в котором говорится
Если переменные не найдены в данных, они берутся из среды(формулы), обычно из среды, из которой вызывается glm.
Слово «обычно» здесь является ключевым. Точное правило состоит в том, что среда, присоединенная к формуле, является той, в которой выражение формулы сначала вычисляется, потому ~
что на самом деле это функция. Он присоединяет среду оценки к объекту формулы, и именно она остается с ним, когда вы передаете объект.
Если вы запускаете glm(y ~ x)
, формула вычисляется везде, где вы ее называете, так что это «типичный» случай.
В вашем примере вы создали объект формулы при вызове
fun(x = DF1$x, y = DF1$y, formula = y ~ x a b, data = DF2)
Это означает, что глобальная среда (в которой вы сделали этот вызов) привязана к формуле, а y
ее там нет, поэтому вы получили ошибку.
Если бы вы использовали значение по умолчанию formula = y ~ x
, позвонив
fun(x = DF1$x, y = DF1$y, data = DF2)
без formula
аргумента это сработало бы, потому что аргументы по умолчанию вычисляются в рамках оценки функции, которая их использует. Поскольку fun()
имеет локальные переменные x
и y
создается аргументами, это было бы прекрасно.
Вы также спросили, почему data = NULL
будет работать функция @DaveArmstrong. Он добавил x
и y
к этому, используя
data$x lt;- x data$y lt;- y
Если вы начнете с data = NULL
, первая строка изменит ее на список, содержащий x
, а вторая строка добавит y
компонент, так что в итоге вы получите список, содержащий x
и y
, и это нормально для data
in glm()
.
Комментарии:
1. Большое вам спасибо за разъяснение этих моментов! Я думал, что обещание оценивалось только при необходимости (ленивая оценка). Разве это не относится к формулам?