我有一个包含多列的数据框.对于数据框中的每一行,我想调用该行上的函数,并且该函数的输入使用该行中的多个列.例如,假设我有这个数据,这个testFunc接受两个args:
> 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
Run Code Online (Sandbox Code Playgroud)
假设我想将此testFunc应用于列x和z.因此,对于第1行,我想要1 + 5,对于第2行,我想要2 + 6.有没有办法在不编写for循环的情况下执行此操作,也许使用apply函数系列?
我试过这个:
> 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
Run Code Online (Sandbox Code Playgroud)
但有错误,任何想法?
编辑:我想要调用的实际函数不是一个简单的总和,但它是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
))
Run Code Online (Sandbox Code Playgroud)
其中结果是每行df的power.t.test的输出向量.
ags*_*udy 123
您可以应用于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) )
Run Code Online (Sandbox Code Playgroud)
或者如果你的函数只是sum,请使用矢量化版本:
rowSums(dat[,c('x','z')])
[1] 6 8
Run Code Online (Sandbox Code Playgroud)
如果你想使用 testFunc
testFunc <- function(a, b) a + b
apply(dat[,c('x','z')], 1, function(x) testFunc(x[1],x[2]))
Run Code Online (Sandbox Code Playgroud)
编辑要按名称而不是索引访问列,您可以执行以下操作:
testFunc <- function(a, b) a + b
apply(dat[,c('x','z')], 1, function(y) testFunc(y['z'],y['x']))
Run Code Online (Sandbox Code Playgroud)
小智 102
A data.frame是a list,所以......
对于矢量化函数 do.call通常是一个不错的选择.但争论的名称开始发挥作用.在这里testFunc用args x和y代替a和b调用.在...允许无关的参数传递给没有导致错误进行传递:
do.call( function(x,z,...) testFunc(x,z), df )
Run Code Online (Sandbox Code Playgroud)
对于非向量化函数,mapply将起作用,但您需要匹配args的顺序或明确命名它们:
mapply(testFunc, df$x, df$z)
Run Code Online (Sandbox Code Playgroud)
有时apply会起作用 - 因为当所有args属于同一类型时,强制data.frame转换为矩阵不会因更改数据类型而导致问题.你的例子是这样的.
如果要在其中传递参数的另一个函数中调用函数,则有一个比这些更明智的方法.研究身体的第一行,lm()如果你想走那条路.
Chi*_*til 30
使用 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
Run Code Online (Sandbox Code Playgroud)
I L*_*ode 17
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
不幸的是,正如Bertjan 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)
Run Code Online (Sandbox Code Playgroud)
Ric*_*rta 10
许多函数已经是向量化,因此不需要任何迭代(for循环或*pply函数).你testFunc就是这样一个例子.你可以简单地打电话:
testFunc(df[, "x"], df[, "z"])
Run Code Online (Sandbox Code Playgroud)
一般来说,我建议先尝试这种矢量化方法,看看它们是否能为您提供预期的结果.
或者,如果您需要将多个参数传递给未向量化的函数,则mapply可能是您要查找的内容:
mapply(power.t.test, df[, "x"], df[, "z"])
Run Code Online (Sandbox Code Playgroud)
rso*_*ren 10
其他人已经正确地指出这mapply是为了这个目的,但是(为了完整起见)概念上更简单的方法就是使用for循环.
for (row in 1:nrow(df)) {
df$newvar[row] <- testFunc(df$x[row], df$z[row])
}
Run Code Online (Sandbox Code Playgroud)
这是另一种方法。它更直观。
我觉得有些答案没有考虑到的一个关键方面,我为后代指出的,是 apply() 可以让您轻松进行行计算,但仅适用于矩阵(所有数字)数据
对于数据帧,仍然可以对列进行操作:
as.data.frame(lapply(df, myFunctionForColumn()))
Run Code Online (Sandbox Code Playgroud)
为了对行进行操作,我们首先进行转置。
tdf<-as.data.frame(t(df))
as.data.frame(lapply(tdf, myFunctionForRow()))
Run Code Online (Sandbox Code Playgroud)
缺点是我相信 R 会复制你的数据表。这可能是内存问题。(这真的很可悲,因为 tdf 在编程上很简单,它只是原始 df 的迭代器,从而节省内存,但 R 不允许指针或迭代器引用。)
此外,一个相关的问题是如何对数据帧中的每个单独的单元格进行操作。
newdf <- as.data.frame(lapply(df, function(x) {sapply(x, myFunctionForEachCell()}))
Run Code Online (Sandbox Code Playgroud)
data.table 也有一种非常直观的方式来做到这一点:
library(data.table)
sample_fxn = function(x,y,z){
return((x+y)*z)
}
df = data.table(A = 1:5,B=seq(2,10,2),C = 6:10)
> df
A B C
1: 1 2 6
2: 2 4 7
3: 3 6 8
4: 4 8 9
5: 5 10 10
Run Code Online (Sandbox Code Playgroud)
该:=操作员可以在括号内被称为使用功能来添加一个新列
df[,new_column := sample_fxn(A,B,C)]
> df
A B C new_column
1: 1 2 6 18
2: 2 4 7 42
3: 3 6 8 72
4: 4 8 9 108
5: 5 10 10 150
Run Code Online (Sandbox Code Playgroud)
使用此方法也很容易接受常量作为参数:
df[,new_column2 := sample_fxn(A,B,2)]
> df
A B C new_column new_column2
1: 1 2 6 18 6
2: 2 4 7 42 12
3: 3 6 8 72 18
4: 4 8 9 108 24
5: 5 10 10 150 30
Run Code Online (Sandbox Code Playgroud)