我需要将myfun
应用于数据框的子集,并将结果作为返回的数据框中的新列包含在内。在过去,我使用ddply
。但是在dplyr
中,我相信summarise
用于此,就像这样:
myfun<- function(x,y) {
df<- data.frame( a= mean(x)*mean(y), b= mean(x)-mean(y) )
return (df)
}
mtcars %>%
group_by(cyl) %>%
summarise(a = myfun(cyl,disp)$a, b = myfun(cyl,disp)$b)
以上代码有效,但我将使用的myfun
计算成本非常高,所以我希望它只被称为一次而不是{{1} }和a
列。有没有办法在b
中执行此操作?
答案 0 :(得分:3)
由于您的函数返回一个数据框,您可以在group_by %>% do
内调用您的函数,该函数将函数应用于每个单独的组并且 rbind 将返回的数据框组合在一起:
mtcars %>% group_by(cyl) %>% do(myfun(.$cyl, .$disp))
# A tibble: 3 x 3
# Groups: cyl [3]
# cyl a b
# <dbl> <dbl> <dbl>
#1 4 420.5455 -101.1364
#2 6 1099.8857 -177.3143
#3 8 2824.8000 -345.1000
答案 1 :(得分:3)
do
不一定会提高速度。在这篇文章中,我将介绍一种设计执行相同任务的函数的方法,然后进行基准测试以比较每种方法的性能。
这是定义函数的另一种方法。
myfun2 <- function(dt, x, y){
x <- enquo(x)
y <- enquo(y)
dt2 <- dt %>%
summarise(a = mean(!!x) * mean(!!y), b = mean(!!x) - mean(!!y))
return(dt2)
}
请注意myfun2
的第一个参数是dt
,它是输入数据框。通过这样做,myfun2
可以成功实现作为管道操作的一部分。
mtcars %>%
group_by(cyl) %>%
myfun2(x = cyl, y = disp)
# A tibble: 3 x 3
cyl a b
<dbl> <dbl> <dbl>
1 4 420.5455 -101.1364
2 6 1099.8857 -177.3143
3 8 2824.8000 -345.1000
通过这样做,我们每次想要创建新列时都不必调用my_fun
。所以这种方法可能比my_fun
更有效。
以下是使用microbenchmark
的效果比较。我比较的方法如下所列。我运行了1000次模拟。
m1: OP's original way to apply `myfun`
m2: Psidom's method, using `do`to apply `myfun`.
m3: My approach, using `myfun2`
m4: Using `do` to apply `myfun2`
m5: Z.Lin's suggestion, directly calculating the values without defining a function.
m6: akrun's `data.table` approach with `myfun`
以下是基准测试的代码。
microbenchmark(m1 = (mtcars %>%
group_by(cyl) %>%
summarise(a = myfun(cyl, disp)$a, b = myfun(cyl, disp)$b)),
m2 = (mtcars %>%
group_by(cyl) %>%
do(myfun(.$cyl, .$disp))),
m3 = (mtcars %>%
group_by(cyl) %>%
myfun2(x = cyl, y = disp)),
m4 = (mtcars %>%
group_by(cyl) %>%
do(myfun2(., x = cyl, y = disp))),
m5 = (mtcars %>%
group_by(cyl) %>%
summarise(a = mean(cyl) * mean(disp), b = mean(cyl) - mean(disp))),
m6 = (as.data.table(mtcars)[, myfun(cyl, disp), cyl]),
times = 1000)
这是基准测试的结果。
Unit: milliseconds
expr min lq mean median uq max neval
m1 7.058227 7.692654 9.429765 8.375190 10.570663 28.730059 1000
m2 8.559296 9.381996 11.643645 10.500100 13.229285 27.585654 1000
m3 6.817031 7.445683 9.423832 8.085241 10.415104 193.878337 1000
m4 21.787298 23.995279 28.920262 26.922683 31.673820 177.004151 1000
m5 5.337132 5.785528 7.120589 6.223339 7.810686 23.231274 1000
m6 1.320812 1.540199 1.919222 1.640270 1.935352 7.622732 1000
结果显示,do
方法(m2
和m4
)实际上比其对应方m1
和m3
慢。在这种情况下,应用myfun
(m1
)和myfun2
(m3
)比使用do
更快。 myfun2
(m3
)比myfun
(m1
)快得多。但是,没有定义任何函数(m5
)实际上比所有函数定义的方法(m1
到m4
)更快,这表明对于这种特殊情况,实际上没有必要定义一个功能。最后,如果没有必要留在tidyverse
,或者数据集的大小是巨大的。我们可以考虑使用data.table
方法(m6
),这比此处列出的所有tidyverse
解决方案快得多。
答案 2 :(得分:1)
我们可以使用data.table
library(data.table)
# as.data.table(mtcars)[, myfun(cyl, disp), cyl]
# cyl a b
#1: 6 1099.8857 -177.3143
#2: 4 420.5455 -101.1364
#3: 8 2824.8000 -345.1000