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



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



> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
x y z
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')]
x z
1 1 5
2 2 6
> lapply(df[,c('x','z')], testFunc)
Error in a + b : 'b' is missing


но есть ошибка, есть идеи?



EDIT: фактическая функция, которую я хочу вызвать, - это не простая сумма, но это сила.Т.тест. Я использовал a+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
))


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

475   10  

10 ответов:

вы можете подать заявку 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]))

EDIT чтобы получить доступ к столбцам по имени, а не по индексу, вы можете сделать что-то вроде этого:

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

A data.frame это list, так ...

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

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

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

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

иногда apply будет работать-как когда все args имеют один и тот же тип так принуждая data.frame для матрицы не вызывает проблем при изменении типов данных. Ваш пример был такого рода.

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

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

> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
  x y z
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) )
  x y z f
1 1 3 5 6
2 2 4 6 8

новый ответ с 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

к сожалению, как Broeksema Бертян указал,, этот подход сбой, если не использовать все столбцы фрейма данных в 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 петли или *pply функции). Ваш 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 программно просто быть итератором к исходному df, таким образом экономя память, но R не позволяет ссылаться на указатель или итератор.)

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

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

Я пришел сюда в поисках tidyverse имя функции - которое я знал, что существует. Добавление этого для (моей) будущей ссылки и для 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 the 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
... ...

Если данных.столбцы фреймов бывают разных типов,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]]])

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

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

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])

Comments

    Ничего не найдено.