我有一个函数,它接受许多参数并返回一个数据框。我还有一个数据框,其中每一行都包含我想传递给我的函数的参数,我想将结果数据帧集存储在一个列表中。有效的方法是什么? (我假设这是一种apply
方法。)
例如,假设你有(无意义的)函数
myfunc<-function(dfRow){
return(data.frame(x=dfRow$x:dfRow$y,y=mean(dfRow$x,dfRow$y)))
}
和数据框
df<-data.frame(x=1:3,y=4:6)
df
x y
1 1 4
2 2 5
3 3 6
你可以运行
myfunc(df[1,])
x y
1 1 1
2 2 1
3 3 1
4 4 1
但是如何为数据框的每一行运行myfunc并将结果存储在列表中?我知道如何为此做一个基本的for循环,但我正在寻找能够更快运行的东西 - 一些矢量化的东西。
答案 0 :(得分:3)
如果你想要一个答案列表,为什么不传递一个参数列表呢?首先将您的数据框拆分为一个列表,然后lapply
您的函数:
listargs <- split(df,1:nrow(df))
lapply(listargs,myfunc)
$`1`
x y
1 1 1
2 2 1
3 3 1
4 4 1
$`2`
x y
1 2 2
2 3 2
3 4 2
4 5 2
$`3`
x y
1 3 3
2 4 3
3 5 3
4 6 3
答案 1 :(得分:3)
你的&#34;毫无意义&#34;函数需要具有apply
能够工作的一些含义。对于初学者,您无法使用$
,因为apply
会将每一行视为基本命名向量。
记住这一点,这里是一个重写(以及更多*意味着*的意思):
myfunc <- function(dfRow) {
data.frame(x = dfRow[1]:dfRow[2], y = mean(c(dfRow[1], dfRow[2])))
}
甚至:
myfunc <- function(dfRow) {
data.frame(x = dfRow["x"]:dfRow["y"], y = mean(c(dfRow["x"], dfRow["y"])))
}
这是我们从apply
获得的MARGIN = 1
(即按行应用函数):
apply(df, 1, myfunc)
# [[1]]
# x y
# 1 1 2.5
# 2 2 2.5
# 3 3 2.5
# 4 4 2.5
#
# [[2]]
# x y
# 1 2 3.5
# 2 3 3.5
# 3 4 3.5
# 4 5 3.5
#
# [[3]]
# x y
# 1 3 4.5
# 2 4 4.5
# 3 5 4.5
# 4 6 4.5
,不要总是快速注销for
个循环。 apply
已优化,但基本上隐藏了某处的for循环。
以下是一些速度比较:
## Function to use with `apply`
myfunc <- function(dfRow) {
data.frame(x = dfRow["y"]:dfRow["x"], y = mean(c(dfRow["x"], dfRow["y"])))
}
## Function to use with `lapply`
myfunc1<-function(dfRow){
return(data.frame(x=dfRow$x:dfRow$y,y=mean(dfRow$x,dfRow$y)))
}
## Sample data
set.seed(1)
df <- data.frame(x = sample(100, 100, TRUE),
y = sample(100, 100, TRUE))
以下是评估的功能:
fun1 <- function() apply(df, 1, myfunc)
fun2a <- function() {
listargs <- split(df,1:nrow(df))
}
fun3 <- function() {
out <- vector("list", nrow(df))
for (i in 1:nrow(df)) {
out[[i]] <- data.frame(x = df$x[i]:df$y[i], y = mean(c(df$x[i], df$y[i])))
}
out
}
以下是结果:
microbenchmark(fun2(), fun2(), fun3(), times = 20)
# Unit: milliseconds
# expr min lq median uq max neval
# fun1() 39.72704 39.99255 40.84243 43.77641 48.16284 20
# fun2() 74.92324 79.20913 82.15130 83.12488 100.51695 20
# fun3() 48.61772 49.59304 50.16654 56.17891 88.65290 20
答案 2 :(得分:1)
如果您愿意使用外部包,那么这里使用的是data.table
:
这是一个简化你的功能的版本:
require(data.table) ## 1.9.2+
fA <- function(x, y) {
data.frame(x = x:y, y = y:x)
}
dt = as.data.table(df)
result1 = dt[, list(ans = list(fA(x, y))), by=seq_len(nrow(dt))]
# seq_len ans
# 1: 1 <data.frame>
# 2: 2 <data.frame>
# 3: 3 <data.frame>
我们首先创建data.table
,然后使用dt
在每行上汇总by=.
,并在每行上传递相应的x
和y
fA
函数,并将结果包装在list
中。现在只需执行result1$ans
即可获得所需的结果。
如果你坚持不传递单个对象,那么你可以这样做:
require(data.table) ## 1.9.2+
fB <- function(dat) {
data.frame(x = dat$x:dat$y, y = dat$y:dat$x)
}
dt = as.data.table(df)
result2 = dt[, list(ans = list(fB(.SD))), by=seq_len(nrow(dt))]
# seq_len ans
# 1: 1 <data.frame>
# 2: 2 <data.frame>
# 3: 3 <data.frame>
在这里,我们传递数据子集,.SD
- 一个特殊变量,它携带属于每个组的数据,而不是函数fB
。再次做result2$ans
应该得到你的答案。
HTH
哦,顺便说一下,在你的代码中使用空格是可以的;费用不高:)。