проблема определения области/нестандартной оценки в формуле glm в функции в R

#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. Большое вам спасибо за разъяснение этих моментов! Я думал, что обещание оценивалось только при необходимости (ленивая оценка). Разве это не относится к формулам?