简单测一下dplyr
R 语言处理数据的一大优势就是它自身有非常多方便的函数可以对 data.frame 进行各种变换。
这其中 Hadley 的 plyr 包可谓一大神器。很多时候一个 ddply
函数加上 subset
, merge
就可以搞定大多数需求。
不过这些操作的性能问题也一直被人诟病。 好消息是 Hadley 新的 dplyr 包在这方面有了巨大的提升, 大量底层操作用 C++ 改写。这里简单测一下常用的 groupby 和 join(merge) 功能,并顺便跟 python 的 pandas 做对比。
group by
测试取的数据集(下文中的S)大概5000多万行,1G大小。
plyr 包在 group by 分组数量较小时还能忍受,但还是比 dplyr 慢很多。
(用 microbenchmark 包测多次的结果也是类似的,那个输出比较长,以下只贴 system.time
的结果)
system.time(A <- ddply(S, .(status), summarise, cnt = length(uid)))
user system elapsed
9.348 5.652 15.034
system.time(S %>% group_by(status) %>% summarise(cnt = n()) -> A)
user system elapsed
1.928 0.500 2.502
这里 status
列的不同值只有个位数。如果对于用户 id 或条目 id 这种变量进行 group by 的话,plyr 会非常非常慢。
在测试数据集上直接运行到500多秒报错。下面是 dplyr 跟 pandas 的对比结果。
system.time(S %>% group_by(eid) %>% summarise(cnt = n()) -> A)
user system elapsed
5.268 0.424 5.704
In [41]: %timeit A = S.groupby('eid').uid.count()
1 loops, best of 3: 3.22 s per loop
可以看到 pandas 还是要快一些。不过如果对多个变量进行 group by,两者的差距就可以忽略不计了,甚至 R 还要快一点。
system.time(S %>% group_by(eid, status) %>% summarise(cnt = n()) -> B)
user system elapsed
8.168 0.288 8.470
In [42]: %timeit B = S.groupby(['eid', 'status']).uid.count()
1 loops, best of 3: 8.88 s per loop
merge
另外取了一个数据集来测试 merge 功能(下文中的M),文件大致有200万行。
因为 R 自身的 merge 对于大数据确实比较慢,这里就只测 dplyr 和 pandas:
system.time(R1 <- inner_join(S, M))
Joining by: "uid"
user system elapsed
18.616 0.812 19.487
In [20]: %timeit R = pd.merge(S, M, on = 'uid')
1 loops, best of 3: 21.1 s per loop
可以看到在 merge 功能上 dplyr 有时也还是要比 pandas 快那么一点点。
顺便提一句,dplyr 中新加入的 semi_join
函数非常实用。
原先需要通过向量化 index 方法实现的一些抽取过滤现在可以一句话搞定了。
其他
更多的功能测试有空再试吧。可能身边的 python 党们更习惯直接过渡到 pandas,但是个人的使用感觉还是 R 更简便。 主要是语法和功能上,至于运行效率,我个人接触过的例子,到了分析这步,数据量往往都是在 10G 甚至 1G 以下, 这个时候服务器端的 R 已经可以比较好地完成任务了,而且随着 dplyr 等包的发展,跟 pandas 比劣势已经很小了。 加上 R 自身完整的统计生态环境,和 ggplot2, shiny, knitr, recharts 等利器, 搞个可视化也好,搭个网页 app 做带交互的 demo 搞 presentation 也好,写可重复分析报告也好,都是分分钟的事。