sapply с несколькими наборами аргументов для пользовательской функции

#r #dplyr #sapply

#r #dplyr #sapply

Вопрос:

У меня есть фрейм данных df и я хочу использовать функцию range_frac для выполнения операции.

 set.seed(137)
df <- data.frame(col1 = sample(LETTERS, 100, TRUE), 
                 col2 = sample(-75:75, 100, TRUE), 
                 col3 = sample(-75:75, 100, TRUE))

df$col2[c(23, 48, 78)] <- NA
df$col3[c(37, 68, 81)] <- NA


range_frac <- function(n, my_df, my_var) {

  len = sum(my_df[my_var] < n, na.rm = TRUE)
  len
}
  

Я хочу знать количество строк, удовлетворяющих указанному условию в col2 и col3 отдельно. Поскольку мне не удалось передать имя столбца, я передал индекс столбца ( 2 , 3 ). Однако, когда я пытаюсь передать вектор для my_var , он суммирует выходные данные из отдельных значений. Как это происходит?

 sapply(1:3, range_frac, my_df = df, my_var = 2) 
[1] 57 57 57

sapply(1:3, range_frac, my_df = df, my_var = 3) 
[1] 51 51 52

sapply(1:3, range_frac, my_df = df, my_var = 2:3) 
[1] 108 108 109
  

Может ли кто-нибудь предоставить объяснение результата третьей операции (т. Е., 57 51, 57 51, 57 52)?

(В принципе, я пытаюсь получить следующий результат dyplr summarise способом, но застрял на этом этапе и подумал, что проясню свое понимание этой концепции).

 n col2 col3
1 57 51
2 57 51
3 57 52
  

обновление: я задал неясный вопрос, поэтому обновляю его дополнительной информацией. Решение выглядит следующим образом:

для каждого n решение можно понимать как вычисление выражения sum(df[,2:3] < n, na.rm = TRUE) , а не отдельно для столбцов 2 amp; 3 .

Ответ №1:

Если вы вводите 2:3 в my_var , range_frac() фактически выполняется

 sum(df[2:3] < n, na.rm = TRUE)
  

для каждого n . Конечно, вы получаете количество элементов меньше, чем n во втором и третьем столбцах. Одним из решений является векторизация аргумента my_var , т.Е.

 sapply(1:3, Vectorize(range_frac, "my_var"), my_df = df, my_var = 2:3)

#      [,1] [,2] [,3]
# [1,]   48   48   48
# [2,]   49   51   51
  

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

1. Спасибо за ответ. Я мог бы избежать вопроса, если бы был немного более осторожен при проверке! Я просил объяснить вывод, который я обнаружил сейчас, так, как он оценивается в форме sum(df[,2:3] < 1, na.rm = TRUE) . Извините, что потратил ваше время.

2. @Prradep Я объяснил это в начале моего ответа, который совпадает с тем, что вы нашли.

3. Спасибо, я принял решение. Не могли бы вы также прокомментировать, как передавать имена столбцов вместо индексов. Это не новый вопрос, и я также упоминал в исходном вопросе (поскольку мне не удалось передать имя столбца, я передал индекс столбца ( 2 , 3 )).

4. @Prradep Я думаю, что замена my_var = 2:3 на my_var = c("col2", "col3") в порядке!

Ответ №2:

Давайте возьмем следующий код в качестве примера

 sapply(1:3, range_frac, my_df = df, my_var = 2:3) 
  

Здесь 1:3 передаются range_frac в качестве первого аргумента, что эквивалентно итерациям, например,

 for (i in 1:3) {
     range_frac(...)
}
  

Внутри sapply , my_df = df и my_var = 2:3 являются вторым и третьим аргументами, переданными range_frac . Таким образом, вся sapply строка может быть интерпретирована как

 res <- c()
for (i in 1:3) {
     res[i] <- range_frac(i, df, 2:3)
}
  

Некоторые обходные пути

 sapply(1:3,Vectorize(range_frac,"my_var"),my_df = df, my_var = 2:3)

sapply(1:3,function(k) sapply(2:3,function(v) range_frac(k,df,v)))
  

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

1. Не могли бы вы также указать, как my_var = 2:3 играет свою роль в выводе. Я думаю, это sum(df[,2:3] < 1, na.rm = TRUE) . Я мог бы проверить это правильно, извините.