Программное регрессионное моделирование с использованием tidyeval

Я пытаюсь разобраться в программировании с помощью tidyeval.

Я хочу написать функцию для запуска моделей логистической регрессии для выбранных переменных результата:

library(tidyverse)
set.seed(1234)

df <- tibble(id = 1:1000,
             group = sample(c("Group 1", "Group 2", "Group 3"), 1000, replace = TRUE),
             died = sample(c(0,1), 1000, replace = TRUE))

myfunc <- function(data, outcome){

enquo_var <- enquo(outcome)

fit <- tidy(glm(!!enquo_var ~ group, data=data, 
                family = binomial(link = "logit")), 
                exponentiate = TRUE, conf.int=TRUE)

fit
}


myfunc(df, died)

Но получите:

Ошибка в! Enquo_outcome: недопустимый тип аргумента

(обратите внимание, что реальный сценарий включает более сложную функцию).

Это возможно?


person Peter MacPherson    schedule 27.09.2017    source источник


Ответы (2)


Нам нужно создать формулу для glm, чтобы это понять. Один вариант - paste

myfunc <- function(data, outcome){
  enquo_var <- enquo(outcome)
   fit <- tidy(glm(paste(quo_name(enquo_var), "group", sep="~"), data=data, 
                family = binomial(link = "logit")), 
                exponentiate = TRUE, conf.int=TRUE)

fit
}

myfunc(df, died)
#         term  estimate std.error  statistic    p.value  conf.low conf.high
#1  (Intercept) 0.8715084 0.1095300 -1.2556359 0.20924801 0.7026185  1.079852
#2 groupGroup 2 0.9253515 0.1550473 -0.5003736 0.61681204 0.6826512  1.253959
#3 groupGroup 3 1.3692735 0.1557241  2.0181864 0.04357185 1.0095739  1.859403

Если нам также нужно использовать функции tidyverse

myfunc <- function(data, outcome){

  quo_var <- quo_name(enquo(outcome))

   fit <- tidy(glm(rlang::expr(!! rlang::sym(quo_var) ~ group), data=data, 
            family = binomial(link = "logit")), 
            exponentiate = TRUE, conf.int=TRUE)

 fit
}

myfunc(df, died)
#           term  estimate std.error  statistic    p.value  conf.low conf.high
#1  (Intercept) 0.8715084 0.1095300 -1.2556359 0.20924801 0.7026185  1.079852
#2 groupGroup 2 0.9253515 0.1550473 -0.5003736 0.61681204 0.6826512  1.253959
#3 groupGroup 3 1.3692735 0.1557241  2.0181864 0.04357185 1.0095739  1.859403

Или как @lionel, упомянутый в комментариях, можно использовать get_expr

myfunc <- function(data, outcome){

  quo_var <- enquo(outcome)

   fit <- tidy(glm(rlang::expr(!! rlang::get_expr(quo_var) ~ group), data=data, 
            family = binomial(link = "logit")), 
            exponentiate = TRUE, conf.int=TRUE)

 fit
}

myfunc(df, died)
#         term  estimate std.error  statistic    p.value  conf.low conf.high
#1  (Intercept) 0.8715084 0.1095300 -1.2556359 0.20924801 0.7026185  1.079852
#2 groupGroup 2 0.9253515 0.1550473 -0.5003736 0.61681204 0.6826512  1.253959
#3 groupGroup 3 1.3692735 0.1557241  2.0181864 0.04357185 1.0095739  1.859403

Или более компактный подход, предложенный @lionel, который избегает преобразования enquo/quo_name/sym, вместо этого напрямую принимает аргумент в enexpr

 myfunc <- function(data, outcome){



   fit <- tidy(glm(rlang::expr(!! rlang::enexpr(outcome) ~ group), data=data, 
            family = binomial(link = "logit")), 
            exponentiate = TRUE, conf.int=TRUE)

 fit
}

myfunc(df, died)
#         term  estimate std.error  statistic    p.value  conf.low conf.high
#1  (Intercept) 0.8715084 0.1095300 -1.2556359 0.20924801 0.7026185  1.079852
#2 groupGroup 2 0.9253515 0.1550473 -0.5003736 0.61681204 0.6826512  1.253959
#3 groupGroup 3 1.3692735 0.1557241  2.0181864 0.04357185 1.0095739  1.859403
person akrun    schedule 27.09.2017
comment
Ах ... теперь это имеет смысл. Спасибо! - person Peter MacPherson; 27.09.2017
comment
@PeterMacPherson Я также добавил еще один вариант с tidyverse - person akrun; 27.09.2017
comment
Благодарю. Раньше я пробовал использовать rlang :: sym, но не получил части expr. Еще раз большое спасибо! - person Peter MacPherson; 27.09.2017
comment
Неидиоматично, что вы создаете запрос, а затем сериализуете его с помощью quo_name(). Я думаю, нам нужно гораздо яснее указать в документации, что quo_name() не предназначен для программирования, он предназначен только для маркировки, то есть присвоения имени по умолчанию в столбце фрейма данных и т.п. - person Lionel Henry; 27.09.2017
comment
@lionel OP принимает аргумент как строку без кавычек - person akrun; 27.09.2017
comment
Также во втором примере вы создаете предложение, превращаете его в строку, а затем снова преобразуете его обратно в символ. Это много взад и вперед. Я бы просто оставил это как вопрос и использовал get_expr(). В следующей версии rlang будет ensym() для захвата непосредственно в виде символа (и убедитесь, что пользователь действительно предоставил символ, а enquo() пользователь может предоставить что угодно). - person Lionel Henry; 27.09.2017
comment
что вы имеете в виду под OP принимает аргумент как строку без кавычек? - person Lionel Henry; 27.09.2017
comment
@lionet Я имел в виду, что аргумент не цитируется - person akrun; 27.09.2017
comment
@lionel Я пробовал использовать tidy(glm(rlang::get_expr(quo_var ~ group), data=data, family = binomial(link = "logit")), exponentiate = TRUE, conf.int=TRUE), но он не работает - person akrun; 27.09.2017
comment
Я думаю, как только у нас будет ensym() правильный способ сделать это будет as_string(ensym(x)). В любом случае спасибо за ваш вклад! - person Lionel Henry; 27.09.2017
comment
Я имел в виду get_expr() вместо sym(). get_expr() примет выражение вопроса. - person Lionel Henry; 27.09.2017
comment
Или вы можете просто использовать enexpr() вместо enquo(), поскольку здесь вам не нужны вопросы. Затем вы можете отменить его цитирование: expr(!! enexpr(x) ~ group). Я бы также проверил, что результат enexpr() действительно является символом. - person Lionel Henry; 27.09.2017
comment
Позвольте нам продолжить это обсуждение в чате. - person Lionel Henry; 27.09.2017
comment
@lionel Это новая функция. Я получаю Error: 'en_expr' is not an exported object from 'namespace:rlang' - person akrun; 27.09.2017

Базовый NSE, похоже, тоже работает:

library(broom)
myfunc <- function(data, outcome){
  outcome_subst <- substitute(outcome)
  fit <- tidy(glm(paste(as.name(outcome_subst), "group", sep="~"), data=data, 
                  family = binomial(link = "logit")), 
              exponentiate = TRUE, conf.int=TRUE)

  fit
}


myfunc(df, died)



         term  estimate std.error statistic    p.value  conf.low conf.high
1  (Intercept) 0.8238636 0.1121528 -1.727556 0.08406792 0.6606245  1.025838
2 groupGroup 2 1.2587484 0.1571734  1.464102 0.14316606 0.9253116  1.713937
3 groupGroup 3 1.2490778 0.1550546  1.434369 0.15146698 0.9220209  1.693699
person Sebastian Sauer    schedule 02.11.2017