Как использовать apply для функций, которым требуется «data $ varname» против функций, которым нужно просто «varname»

#r #apply #sapply

#r #применить #sapply

Вопрос:

Здесь относительно новый пользователь R, который пытается сделать код более эффективным для будущего использования, в основном пробуя функции из семейства apply.

Прямо сейчас у меня есть скрипт, в котором я извлекаю средства из большого количества переменных (вручную), создавая список имен переменных и передавая его в sapply.

Итак, это пример того, как я составил список имен переменных и как я передал его в sapply

 vars <- c("data$age", "data$gender", "data$PCLR")
means <- sapply(vars, fmean, data$group, na.rm=TRUE)
 

Однако теперь я хочу использовать функцию, которая использует формат аргумента function (varname, data), поэтому я не могу использовать тот список имен, который я создал. Что я пытаюсь сделать:

 krusk <- sapply(vars, function(x) kruskal.test(x ~ group, data))
 

Я чувствую, что есть способ передать имена моих переменных в функции, которые я полностью игнорировал, вместо того, чтобы вручную создавать списки. У кого-нибудь есть какие-либо предложения?

Комментарии:

1. используется reformulate для преобразования строк в формулу: kruskal.test(reformulate('group', x), data)

Ответ №1:

Это может работать с использованием iris dataset, data аналогично замечательному предложению от @deschen:

 #Vars
vars <- c("Sepal.Length", "Sepal.Width")
#Code
krusk <- sapply(vars, function(x) kruskal.test(iris[[x]] ~ iris[['Species']]))
 

Вывод:

 krusk
          Sepal.Length                     Sepal.Width                     
statistic 96.93744                         63.57115                        
parameter 2                                2                               
p.value   8.918734e-22                     1.569282e-14                    
method    "Kruskal-Wallis rank sum test"   "Kruskal-Wallis rank sum test"  
data.name "iris[[x]] by iris[["Species"]]" "iris[[x]] by iris[["Species"]]"
 

Комментарии:

1. Мой на самом деле не работал. Я также могу ошибаться, но я прочитал вопрос TOs таким образом, что требование к vars вектору по-прежнему будет заключаться в том, что он содержит объект данных имена, поэтому в вашем примере vars <- c("iris$Sepal.Length", "iris$Sepal.Width") .

Ответ №2:

Вы были очень близки! Вы можете сделать это, установив фрейм данных, который вы вводите sapply , используя свой vars вектор, и изменив формулу в kruskal.test :

 vars <- c("Sepal.Length", "Sepal.Width")
sapply(iris[, vars], function(x) kruskal.test(x ~ iris$Species))
 

Ответ №3:

R — очень разнообразный язык программирования, и в конечном итоге будет много способов сделать то же самое. Некоторые функции ожидают более стандартной оценки, в то время как другие могут использовать NSE (нестандартную оценку).

Однако вы, похоже, спрашиваете о функциях, которые ожидают только один вектор как вменяемый, в отличие от функций, у которых есть data аргумент, в котором вы используете variable в отличие от data$variable .

У меня есть несколько боковых панелей, прежде чем я дам несколько советов

Боковая панель 1 — Методы S3

Хотя это может быть не важно в отношении вопроса, функция kruskal.test имеет два метода.

 methods("kruskal.test")
#[1] kruskal.test.default* kruskal.test.formula*
#see '?methods' for accessing help and source code
 

Какой метод используется, зависит от класса первого аргумента в функции. В этом примере вы передаете выражение формулы, в котором data аргумент необходим, тогда как метод по умолчанию требует только x g аргументов и (которые вы, вероятно, могли бы использовать в своих оригинальных конвейерах).

Итак, если вы привыкли делать что-то одним способом, обязательно проверьте, что в документации функции есть разные методы отправки, которые будут работать для вас.

Боковая панель 2 — Фреймы данных

Фреймы данных на самом деле представляют собой просто набор векторов. Разница между f(data$variable) и f(x = variable, data = data) заключается в том, что в первом пользователь явно указывает R, где найти вектор, в то время как во втором функция f должна оцениваться x в контексте data . Я поднимаю этот вопрос из-за того, что я сказал в начале — есть много способов сделать то же самое. Так что, как правило, вам решать, каким должен быть ваш стандарт.

Если вы предпочитаете быть явным

 vars <- c("data$age", "data$gender", "data$PCLR")
means <- sapply(vars, fmean, data$group, na.rm=TRUE)
krusk <- sapply(vars, kruskal.test, g = data$group)
 

или вы можете написать свои функции, в которых они должны оцениваться внутри определенного data.frame объекта

 vars <- c("age", "gender", "PCLR")
means <- sapply(vars, function(x, id, data) fmean(data[[x]], id = data[[id]], na.rm=T), id = "group", data = data)
krusk <- sapply(vars, function(x, id, data) kruskal.test(data[[x]], data[[id]]), id = "group", data = data)
 

Мой совет

Я рекомендую изучить следующие пакеты dplyr , tidyr , purrr . Я уверен, что в этих пакетах есть несколько вещей, которые облегчат вашу жизнь.

Например, вы выразили необходимость вручную составить список перед выполнением sapply . В dplyr пакете вы можете обойти это, если есть условие для фильтрации.

 data %>%
    group_by(group) %>% #groups data
    summarise_if(is.numeric, mean, na.rm = TRUE) #applys mean to every column that is a numeric vector
 

И аналогично, мы можем обобщить результаты kurskal.test функции, если немного изменим данные.

 data %>% 
     group_by(group) %>% #grouping to retain column in the next select statement
     select_if(is.numeric) %>% # selecting all numeric columns
     pivot_longer(cols = -group) %>% # all columns except "group" will be reshaped. Column names are stored in `name`, and values are stored in `values`
     group_by(name) %>% #regroup on name variable (old numeric columns)
     summarise(krusk = list(kruskal.test(value ~ as.factor(group)))) #perform test
 

Я упомянул только purrr потому, что вы можете почти удалить и заменить все apply функции стиля их map вариантами. purrr очень последователен во всех вариантах функций с множеством опций для управления типом вывода.

Я надеюсь, что это поможет и желаю вам удачи в ваших приключениях с кодированием.

Комментарии:

1. Привет, Джастин, спасибо за этот ответ! Я не видел другого способа выполнения kruskal.test, так что это определенно поможет в том, как я структурировал свою переменную vars на этот раз. На самом деле я уже часто использую dplyr, но я вижу, что мне нужно больше ознакомиться с тем, как использовать связанные функции. На самом деле у меня есть немного кода, использующего «summarise (across(starts_with («Pect»)», поскольку все переменные, которые мне нужно просмотреть, начинаются с этих слов (поэтому не обязательно только числовые переменные). Но я буду продолжать копаться в этой функции ‘select_if’!

2. Сделал это! Конечный результат для моего кода был: krusk <- data_clean %>% group_by(cutoff_26) %>% select(starts_with(«Pect»)) %>% pivot_longer(cols =-cutoff_26) %>% group_by(name) %>% summarise(krusk = list(kruskal.test(значение ~ как .factor(cutoff_26)))) Большое спасибо за вашу помощь!