总览
我相对熟悉data.table
,而不是那么熟悉dplyr
。我已经阅读了一些出现在SO上的dplyr
插图和示例,到目前为止,我的结论是:
data.table
且dplyr
速度可比,除非有很多(即> 10-100K)组,并且在某些其他情况下(请参见下面的基准)dplyr
具有更多可访问的语法dplyr
抽象(或将)潜在的数据库交互- 功能上有一些细微的差异(请参见下面的“示例/用法”)
我认为2.不太重要,因为我相当熟悉它data.table
,尽管我知道这对新手和老手来说都是一个很大的因素。我想避免争论哪个更直观,因为从我已经熟悉的人的角度来看,这与我提出的具体问题无关data.table
。我还想避免讨论“更直观”如何导致更快的分析(当然是正确的,但又不是我对此最感兴趣的内容)。
题
我想知道的是:
- 对于熟悉软件包的人来说,是否有分析任务更容易用一个或另一个软件包编写代码(即,要求的击键与要求的深奥程度的某种组合,每个击键都是一件好事)。
- 在一个程序包中,是否有比其他程序更有效地执行分析任务(即大于2倍)?
最近的一个SO问题使我对这个问题进行了更多的思考,因为到那时为止,我认为dplyr
提供的功能超出了我已经可以做得到的范围data.table
。这是dplyr
解决方案(Q末的数据):
dat %.%
group_by(name, job) %.%
filter(job != "Boss" | year == min(year)) %.%
mutate(cumu_job2 = cumsum(job2))
这比我尝试破解data.table
解决方案要好得多。也就是说,好的data.table
解决方案也相当不错(感谢Jean-Robert,Arun,并请注意,在这里,我赞成使用单一语句而不是严格的最佳解决方案):
setDT(dat)[,
.SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)],
by=list(id, job)
]
后者的语法可能看起来很深奥,但是如果您习惯了data.table
(例如,不使用一些更深奥的技巧),它实际上非常简单。
理想情况下,我希望看到的是一些很好的示例,它们的方式dplyr
或data.table
方法实质上更简洁,或性能更好。
例子
用法dplyr
不允许返回任意行的分组操作(从eddi的问题开始,请注意:这看起来将在dplyr 0.5中实现,而且@beginneR显示了do
在@eddi问题的答案中使用的一种可能的解决方法)。data.table
支持滚动连接(感谢@dholstius)以及重叠连接data.table
内部优化形式的表达式DT[col == value]
或DT[col %in% values]
对于速度通过自动索引,它使用二进制搜索,同时使用相同的基础R语法。有关更多详细信息和微小基准,请参见此处。dplyr
提供的功能(例如,标准的评估版regroup
,summarize_each_
),可以简化程序使用dplyr
(注意程序中使用的data.table
是绝对有可能的,只是需要一些认真思考,置换/报价,等等,至少据我所知)
- 我运行了自己的基准测试,发现这两个程序包在“拆分应用合并”样式分析中具有可比性,只是当有非常多的组(> 100K)时,这两个程序包
data.table
变得更快。 - @Arun 在联接上运行了一些基准测试,显示了伸缩性
data.table
要好于dplyr
组数量的增加(在软件包和最新版本的R中都进行了最新增强)。此外,试图获得在基准唯一值有data.table
〜6倍的速度更快。 - (未验证)
data.table
在较大版本的组/应用程序/排序上速度提高了75%,而在较小版本的组/应用程序/排序上dplyr
速度提高了40%(来自评论的另一个SO问题,感谢danas)。 - 马特,主要作者
data.table
,已经为基准的分组操作data.table
,dplyr
和Pythonpandas
上高达2个十亿行(〜100GB的RAM) 。 - 一对80K组旧的基准有
data.table
〜快8倍
数据
这是我在问题部分显示的第一个示例。
dat <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L,
2L, 2L, 2L, 2L, 2L, 2L), name = c("Jane", "Jane", "Jane", "Jane",
"Jane", "Jane", "Jane", "Jane", "Bob", "Bob", "Bob", "Bob", "Bob",
"Bob", "Bob", "Bob"), year = c(1980L, 1981L, 1982L, 1983L, 1984L,
1985L, 1986L, 1987L, 1985L, 1986L, 1987L, 1988L, 1989L, 1990L,
1991L, 1992L), job = c("Manager", "Manager", "Manager", "Manager",
"Manager", "Manager", "Boss", "Boss", "Manager", "Manager", "Manager",
"Boss", "Boss", "Boss", "Boss", "Boss"), job2 = c(1L, 1L, 1L,
1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 0L)), .Names = c("id",
"name", "year", "job", "job2"), class = "data.frame", row.names = c(NA,
-16L))
dplyr
,data.table
团队都在制定基准,因此有时会找到答案。#2(语法)imO严格来说是错误的,但这显然会进入观点领域,因此我也投票赞成关闭。
(d)plyr
措施为0
dplyr
和plyr
与关于语法和基本是为什么我不喜欢自己的语法的主要原因是,我要学习太多的(阅读超过1)附加功能(其名称即仍然对我来说没有意义),记住他们所做的事情,他们采取什么论据等等。
.SD
)。[认真]我认为这些是合理的设计差异,它们会吸引不同的人
dplyr
as.data.table(dat)[, .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], by = list(name, job)]