使用每行的多个参数在dataframe的每一行上调用类似apply的函数

我有一个数据框与多个列。 对于数据框中的每一行,我想调用该行上的函数,并且该函数的input使用该行中的多个列。 例如,假设我有这个数据,而且这个testFunc接受两个参数:

> 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 > testFunc <- function(a, b) a + b 

假设我想将这个testFunc应用到列x和z。 所以,对于第1行我想要1 + 5,对于第2行我想要2 + 6.有没有办法做到这一点,而不写一个for循环,也许与应用function家族?

我试过这个:

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

但有错误,有什么想法?

编辑:我想要调用的实际function不是一个简单的总和,但它是power.t.test。 我只是为了举例而使用了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 )) 

其结果是每行df的power.t.test的输出向量。

您可以将申请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) ) 

或者如果你的函数只是sum使用vector化的版本:

 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 ,所以…

对于vector化函数 do.call通常是一个很好的select。 但是争论的名字起了作用。 这里你的testFunc是用args x和y代替a和b来调用的。 ...允许不相关的parameter passing,而不会导致错误:

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

对于非向量化的函数mapply将起作用,但是您需要匹配args的顺序或明确地命名它们:

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

有时候apply会起作用,因为所有参数都是相同types的,所以强制data.frame到matrix不会因为改变数据types而导致问题。 你的例子就是这样的。

如果你的函数是在另一个函数中被调用的,那么这个函数的参数都是被传递的,这个方法比这个方法要简单得多。 研究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软件包的新答案

如果要应用的函数是vector化的,那么可以使用dplyr包中的mutate函数:

 > 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

在我看来,最适合这个任务的工具是来自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 

不幸的是,正如Bertjan Broeksema所指出的那样,如果在mdply调用中不使用dataframe的所有列,这种方法就会失败。 例如,

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

许多函数已经是vector化了,所以不需要任何迭代(循环或者*pply函数)。 你的testFunc就是这样一个例子。 你可以简单地调用:

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

一般来说,我会build议先尝试这样的vector化方法,看看他们是否得到你想要的结果。


或者,如果您需要将多个parameter passing给未vector化的函数,则可能需要使用mapply

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

其他人已经正确地指出,为了这个目的而制定了应用程序,但是(为了完整起见),一个概念上更简单的方法就是使用for循环。

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

这是另一种方法。 这更直观。

我觉得一些关键的方面没有考虑到,我后面指出的是apply()让你可以很容易地进行行计算,但是只对matrix(所有数字)的数据

列上的操作仍然可以用于数据框:

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

要在行上操作,我们首先进行转置。

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

缺点是我相信R会复制你的数据表。 这可能是一个记忆问题。 (这真的很让人伤心,因为它在编程上很简单,只是将ddf作为原始df的迭代器,从而节省了内存,但是R不允许指针或迭代器引用。

另外,一个相关的问题是如何操作数据框中的每个单独的单元。

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

我来到这里寻找反转函数的名字 – 我知道它是存在的。 添加这个(我)未来的参考和泰迪爱好者: 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 

power.t.test函数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列是不同的types,则apply()有问题。 关于行迭代的细微之处在于,当列是不同types时apply(a.data.frame, 1, ...)如何apply(a.data.frame, 1, ...)字符types隐式转换为字符types; 例如。 一个因子和数字列。 下面是一个例子,在一列中使用一个因子来修改数字列:

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

由于列被转换为字符types,所以减法失败。

一种修复方法是将第二列反向转换为数字:

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