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

У меня есть dataframe с несколькими столбцами. Для каждой строки в фрейме данных я хочу вызвать функцию в строке, а вход функции использует несколько столбцов из этой строки. Например, допустим, у меня есть эти данные и этот testFunc, который принимает два аргумента:

> df  df xyz 1 1 3 5 2 2 4 6 > testFunc <- function(a, b) a + b 

Предположим, я хочу применить этот testFunc к столбцам x и z. Итак, для строки 1 я хочу 1 + 5, а для строки 2 я хочу 2 + 6. Есть ли способ сделать это без написания цикла for, возможно, с помощью семейства функций применения?

Я попробовал это:

 > df[,c('x','z')] xz 1 1 5 2 2 6 > lapply(df[,c('x','z')], testFunc) Error in a + b : 'b' is missing 

Но получили ошибку, какие-то идеи?

EDIT: фактическая функция, которую я хочу назвать, – не простая сумма, но это power.t.test. Я использовал + b только для примера. Конечная цель – сделать что-то вроде этого (написанное в псевдокоде):

 df = data.frame( delta=c(delta_values), power=c(power_values), sig.level=c(sig.level_values) ) lapply(df, power.t.test(delta_from_each_row_of_df, power_from_each_row_of_df, sig.level_from_each_row_of_df )) 

где результат представляет собой вектор выходов для power.t.test для каждой строки df.

Вы можете применить apply к подмножеству исходных данных.

  dat <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6)) apply(dat[,c('x','z')], 1, function(x) sum(x) ) 

или если ваша функция является просто суммой, используйте векторизованную версию:

 rowSums(dat[,c('x','z')]) [1] 6 8 

Если вы хотите использовать testFunc

  testFunc <- function(a, b) a + b apply(dat[,c('x','z')], 1, function(x) testFunc(x[1],x[2])) 

РЕДАКТИРОВАТЬ Для доступа к столбцам по имени и не индексу вы можете сделать что-то вроде этого:

  testFunc <- function(a, b) a + b apply(dat[,c('x','z')], 1, function(y) testFunc(y['z'],y['x'])) 

data.frame – это list , поэтому …

Для векторизованных функций do.call обычно является хорошей ставкой. Но имена аргументов вступают в игру. Здесь ваш testFunc вызывается с args x и y вместо a и b. ... позволяет передавать несоответствующие аргументы без возникновения ошибки:

 do.call( function(x,z,...) testFunc(x,z), df ) 

Для не-векторизованных функций mapply будет работать, но вам нужно сопоставить порядок аргументов или явно называть их:

 mapply(testFunc, df$x, df$z) 

Иногда apply будет работать – как при всех аргументах одного типа, так что data.frame использование data.frame к матрице не вызывает проблем путем изменения типов данных. Ваш пример был такого.

Если ваша функция должна быть вызвана внутри другой функции, в которую все аргументы переданы, существует гораздо более тонкий метод, чем эти. Изучите первые строки тела lm() если вы хотите пройти этот маршрут.

Использовать mapply

 > df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6)) > df xyz 1 1 3 5 2 2 4 6 > mapply(function(x,y) x+y, df$x, df$z) [1] 6 8 > cbind(df,f = mapply(function(x,y) x+y, df$x, df$z) ) xyzf 1 1 3 5 6 2 2 4 6 8 

Новый ответ с пакетом dplyr

Если функция, которую вы хотите применить, векторизована, вы можете использовать функцию mutate из пакета dplyr :

 > library(dplyr) > myf <- function(tens, ones) { 10 * tens + ones } > x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6) > mutate(x, value = myf(tens, ones)) hundreds tens ones value 1 7 1 4 14 2 8 2 5 25 3 9 3 6 36 

Старый ответ с пакетом plyr

По моему скромному мнению, инструмент, наиболее подходящий для этой задачи, можно mdply в пакете plyr .

Пример:

 > library(plyr) > x <- data.frame(tens = 1:3, ones = 4:6) > mdply(x, function(tens, ones) { 10 * tens + ones }) tens ones V1 1 1 4 14 2 2 5 25 3 3 6 36 

К сожалению, как заметил Бертан Брукзема , этот подход терпит неудачу, если вы не используете все столбцы кадра данных в вызове mdply . Например,

 > library(plyr) > x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6) > mdply(x, function(tens, ones) { 10 * tens + ones }) Error in (function (tens, ones) : unused argument (hundreds = 7) 

Многие функции уже являются векторизацией, поэтому нет необходимости в каких-либо итерациях (ни for циклов, ни for функций *pply ). testFunc из таких примеров является ваш testFunc . Вы можете просто позвонить:

  testFunc(df[, "x"], df[, "z"]) 

В общем, я бы рекомендовал сначала попробовать такие подходы к векторизации и посмотреть, принесут ли вы ваши намеченные результаты.


Кроме того, если вам нужно передать несколько аргументов функции, которая не является векторизованной, mapply может быть тем, что вы ищете:

  mapply(power.t.test, df[, "x"], df[, "z"]) 

Другие правильно указали, что для этой цели используется mapply , но (для полноты) концептуально более простой метод – это просто использовать цикл for .

 for (row in 1:nrow(df)) { df$newvar[row] <- testFunc(df$x[row], df$z[row]) } 

Вот альтернативный подход. Это более интуитивно понятно.

Одним из ключевых аспектов я считаю, что некоторые из ответов не учитывали, что я указываю на потомство, apply () позволяет легко выполнять вычисления строк, но только для матричных (все числовых) данных

операции над столбцами возможны для данных:

 as.data.frame(lapply(df, myFunctionForColumn())) 

Чтобы работать с строками, сначала создаем транспонирование.

 tdf<-as.data.frame(t(df)) as.data.frame(lapply(tdf, myFunctionForRow())) 

Недостатком является то, что я считаю, что R сделает копию вашей таблицы данных. Что может быть проблемой памяти. (Это действительно печально, потому что для tdf просто программно просто быть iteratorом для исходного df, тем самым сохраняя память, но R не позволяет ссылаться на указатель или iterator.)

Кроме того, связанный с этим вопрос заключается в том, как работать с каждой отдельной ячейкой в ​​кадре данных.

 newdf <- as.data.frame(lapply(df, function(x) {sapply(x, myFunctionForEachCell()})) 

Я приехал сюда искать имя функции tidyverse, которое, как я знал, существовало. Добавляя это для (моей) будущей ссылки и для энтузиастов purrrlyr:invoke_rows ( purrr:invoke_rows в старых версиях).

При подключении к стандартным методам статистики, как и в исходном вопросе, пакет с метлой , вероятно, поможет.

@ user20877984 ответ отличный. Так как они суммировали его намного лучше, чем мой предыдущий ответ, вот моя (по-видимому, неряшливая) попытка применить концепцию:

Использование do.call в основном:

 powvalues <- list(power=0.9,delta=2) do.call(power.t.test,powvalues) 

Работа над полным набором данных:

 # get the example data df <- data.frame(delta=c(1,1,2,2), power=c(.90,.85,.75,.45)) #> df # delta power #1 1 0.90 #2 1 0.85 #3 2 0.75 #4 2 0.45 

lapply функция power.t.test для каждой из строк указанных значений:

 result <- lapply( split(df,1:nrow(df)), function(x) do.call(power.t.test,x) ) > str(result) List of 4 $ 1:List of 8 ..$ n : num 22 ..$ delta : num 1 ..$ sd : num 1 ..$ sig.level : num 0.05 ..$ power : num 0.9 ..$ alternative: chr "two.sided" ..$ note : chr "n is number in *each* group" ..$ method : chr "Two-sample t test power calculation" ..- attr(*, "class")= chr "power.htest" $ 2:List of 8 ..$ n : num 19 ..$ delta : num 1 ..$ sd : num 1 ..$ sig.level : num 0.05 ..$ power : num 0.85 ... ... 

Если столбцы data.frame являются разными типами, apply() имеет проблему. Тонкость итерации строк заключается в том, как apply(a.data.frame, 1, ...) делает неявное преобразование типов в типы символов, когда столбцы являются разными типами; например. числовой и числовой столбцы. Вот пример, используя коэффициент в одном столбце для изменения числового столбца:

 mean.height = list(BOY=69.5, GIRL=64.0) subjects = data.frame(gender = factor(c("BOY", "GIRL", "GIRL", "BOY")) , height = c(71.0, 59.3, 62.1, 62.1)) apply(height, 1, function(x) x[2] - mean.height[[x[1]]]) 

Вычитание не выполняется, потому что столбцы преобразуются в типы символов.

Одно исправление заключается в обратном преобразовании второго столбца в число:

 apply(subjects, 1, function(x) as.numeric(x[2]) - mean.height[[x[1]]]) 

Но конверсий можно избежать, оставив колонки отдельными и используя mapply() :

 mapply(function(x,y) y - mean.height[[x]], subjects$gender, subjects$height) 

mapply() необходимо, потому что [[ ]] не принимает векторный аргумент. Таким образом, итерация столбца может быть выполнена до вычитания путем передачи вектора в [] , немного более уродливым кодом:

 subjects$height - unlist(mean.height[subjects$gender]) 
  • Стэнфордский Wrangler идеален, но налагает ограничения, какие другие варианты?
  • Поиск обходного пути для файла gtable_add_grob, нарушенного ggplot 2.2.0
  • замените значение NA на значение группы
  • В чем преимущество импорта в пространстве имен в R?
  • Создание локального репозитория R-пакета
  • Автоматическое удаление файлов / папок
  • Объединить данные панели для получения данных балансной панели
  • Использование get () с функциями замены
  • Почему strsplit использует положительные взгляды, а утверждение lookbehind по-разному?
  • Фильтровать фрейм данных по имени столбца символа (в dplyr)
  • Прогнозирование временных рядов данных
  • Interesting Posts

    Как я могу отправить себе электронное письмо, которое становится текстовым файлом на моем жестком диске (или в Dropbox)?

    Ошибка запуска сервера MySql «Сервер завершает работу без обновления PID-файла»

    Как создать наложение изображений и добавить в MKMapView?

    Вперед объявить контейнер STL?

    Как установить размер текста текстового вида динамически для разных экранов

    MySQL LAST_INSERT_ID (), используемый с несколькими записями. Инструкция INSERT

    Использование знака звезды в grep

    Ошибка получения родительского элемента: не найден ресурс, который соответствует указанному имени ‘@android: style / TextAppearance.Holo.Widget.ActionBar.Title’

    Как подключить два локально связанных компьютера к WIFI с помощью адресации IPv4?

    Redshift. Преобразование значений, разделенных запятыми в строки

    Каков правильный провод датчика для входящей телефонной линии DSL на мой маршрутизатор?

    Когда я изменяю параметр внутри функции, он тоже изменяется для вызывающего?

    Есть ли декомпилятор Visual Basic 6?

    R: Пользовательская легенда для многослойного ggplot

    Могут ли клиенты IMAP читать мои контакты из Office 365?

    Давайте будем гением компьютера.