使用每行的多个参数在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])