Я пытаюсь написать функцию R, которая возвращает таблицу сводной статистики (на основе функции get_summary stats из пакета rstatix) и вычисляет критерий Уилкоксона. Это будет выполняться для нескольких переменных Y с интересующей постоянной переменной X (которая будет основой для таблицы сводной статистики).
Вот как функция выглядит сейчас:
summary_stats_and_test <- function(df, xvar, yvars){
df_res <- df %>%
group_by({{ xvar }}) %>%
select({{ yvars }}, {{ xvar }}) %>%
get_summary_stats(type = "mean_sd") %>%
arrange(variable) %>%
mutate(across(where(is.numeric), ~ num(., digits = 1)))
stat_test <- yvars %>%
paste(" ~ ", xvar) %>%
map(as.formula) %>%
map_df(~wilcox_test(.x, data = df))
return(list(table = df_res, test = stat_test))
}
Я «обнял» xvar и yvars внутри куска df_res.
Однако я не могу понять, как указать значение xvar внутри функции вставки для правильной работы.
Это, например, код:
weight_wc_deltas_vars = c("weight_1", "weight_2", "weight_3", "WC_1", "WC_2", "WC_3")
df %>%
mutate(has_weight = if_else(is.na(col_1), "no", "yes")) %>%
summary_stats_and_test(xvar = has_weight,
yvars=weight_wc_deltas_vars[1:3])
Если has_weight в функции стоит без кавычек, я получаю ошибку: Error: object 'has_weight' not found. Если оно в кавычках, я не получаю ошибки, но функция group_by внутри моей функции не имеет никакого эффекта.
В более общем плане мне не совсем ясно, лучше ли писать аргумент моей функции в кавычках или без них в R. Концепция охвата в контексте tidyverse ясна, но здесь я застрял на та же проблема.





В вашем коде вам необходимо преобразовать имя, на которое указывает xvar, в строку. Вы можете сделать это через
deparse(substitute(xvar), backtick = TRUE)
Обратите внимание на параметр backtick = TRUE! Это важно для правильной обработки несинтаксических имен (например, `foo bar`), передаваемых в функцию.
Но ваш код также неправильно обрабатывает такие имена при передаче через yvars, и вам нужно это исправить. Один из способов — преобразовать yvars в имена (с помощью map(yvars, rlang::sym) или lapply(yvars, as.name)), а затем обратно в строки, как указано выше. Но то, что вы сейчас делаете, в любом случае является обходным путем. Гораздо проще создавать формулы напрямую, а не обходным путем построения строк. Таким образом, вам также не придется преобразовывать xvar в строку.
stat_test <- yvars %>%
map(rlang::sym) %>%
map(\(y) rlang::inject(!! y ~ !! rlang::ensym(xvar))) %>%
map_df(~ wilcox_test(.x, data = df))
@user23485480 В основном это объясняется на этой странице: rlang.r-lib.org/reference/topic-defuse.html — rlang::ensym() обезвреживает параметр, чтобы он не оценивался впоследствии. !! интерполирует эту обезвреженную переменную в выражение через rlang::expr(). Если вы используете только !!, !! попытается вычислить xvar, но это не удастся («объект «foo» не найден», если вы вызвали свою функцию с помощью xvar = foo). Если вы используете только rlang::ensym(), rlang::expr() вообще не будет оценивать подвыражение, и в итоге вы получите формулу y ~ lang::ensym(xvar).
Привет, просто чтобы продолжить эту тему, я попробовал функцию, как вы предложили, и получил следующую ошибку: Ошибка в map(): ℹ В индексе: 1. Вызвано ошибкой в x$terms %||% attr(x, "terms") %||% stop("no terms component nor attribute"): ! нет компонента терминов или атрибута. Я пробовал вызвать rlang::ensym на xvar перед функцией, но это ничего не меняет. Не слишком понимаю, что происходит, честно говоря.
@user23485480 user23485480 Да, я допустил ошибку: конечно rlang::expr() создает неоцененный языковой объект. Нам нужен оцениваемый объект (формула). Мы могли бы либо обернуть результат rlang::expr() в as.formula(), либо в eval(). Или, что гораздо лучше, мы можем использовать rlang::inject() вместо rlang::expr(): эта функция принимает выражение и выполняет интерполяцию через !! и {{…}}, но в остальном выражение вычисляется обычным образом.
Спасибо! Не могли бы вы подробнее рассказать, почему вы используете оба !! а функции rlang в третьей строке?