data.table vs dplyr:一个人可以做些什么,另一个不行或者做得不好?
概观
我对data.table
比较熟悉,与data.table
不太dplyr
。 我已经读过一些dplyr
小插曲和例子,到目前为止,我的结论是:
-
data.table
和dplyr
在速度上是可比较的,除非有很多(即> 10-100K)的组,在其他一些情况下(见下面的基准) -
dplyr
有更多可访问的语法 -
dplyr
抽象(或将)潜在的数据库交互 - 有一些次要的function差异(请参阅下面的“示例/用法”)
在我看来2.不会承受太多的重量,因为我对data.table
相当熟悉,但我明白,对于新用户来说这将是一个很大的因素。 我想避免一个关于哪一个更直观的论点,因为从已经熟悉data.table
的人的angular度来看,这与我提出的具体问题是无关的。 我也想避免讨论“更直观”如何导致更快的分析(当然,但是,不是我最感兴趣的是这里)。
题
我想知道的是:
- 是否有分析任务比熟悉软件包的人更容易使用一个或另一个软件包进行编码(即,所需的击键与所需的深奥级别的组合,其中每一个都是好事)。
- 是否有分析任务在一个包中比另一个更有效地执行(即超过2倍)。
最近的一个问题使我想到了这个问题 ,因为直到那时,我不认为dplyr
会提供比dplyr
更多的东西。 这里是dplyr
解决scheme(数据在Q末尾):
dat %.% group_by(name, job) %.% filter(job != "Boss" | year == min(year)) %.% mutate(cumu_job2 = cumsum(job2))
这比我在data.table
解决scheme上的尝试要好得多。 也就是说,良好的data.table
解决scheme也是相当不错的(感谢Jean-Robert,Arun,并且在这里我注意到我偏爱最严格的最佳解决scheme):
setDT(dat)[, .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], by=list(id, job) ]
后者的语法可能看起来很深奥,但是如果你习惯data.table
(即不使用一些更深奥的技巧),它实际上是非常简单的。
理想情况下,我想看到的是一些很好的例子, dplyr
或data.table
方式更加简洁或者performance更好。
例子
用法
-
dplyr
不允许返回任意数量的行的分组操作(从eddi的问题来看 ,注意:这看起来像是在dplyr 0.5中实现的,@beginneR在@ eddi的问题的答案中显示了一个潜在的解决方法) 。 -
data.table
支持滚动连接 (谢谢@dholstius)以及重叠连接 -
data.table
通过自动索引来DT[col %in% values]
内部优化表单DT[col == value]
或DT[col %in% values]
expression式,以便在使用相同基本R语法时使用二进制search 。 看到这里一些更多的细节和一个微小的基准。 -
dplyr
提供了可以简化dplyr
的程序化使用的标准评估版本的函数(例如dplyr
,dplyr
(注意dplyr
编程使用绝对是可能的,只需要一些小心,replace/引用等等,至less就我所知)
基准
- 我运行了我自己的基准testing ,发现这两个软件包在“拆分应用组合”风格分析中是可比的,除非有非常多的数据组(> 100K),
data.table
在这一点data.table
变得快得多。 - @Arun 在连接上运行了一些基准testing ,表明
data.table
比dplyr
更好地dplyr
(在最新版本的R和包中进行了更新)。 另外,当尝试获得唯一值时,基准testing的数据表速度快了6倍。 - (未validation)有
data.table
75%以上的较大版本的组/应用/sorting,而dplyr
则是40%以上的较小( 另一个SO问题来自意见 ,感谢danas)。 -
data.table
的主要作者Matt 对基于data.table
,dplyr
和pythonpandas
分组操作进行了基准testing , testing结果高达20亿行(RAM大约100GB) 。 - 80K组的老版本基准testing数据表的速度提高了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))
我们至less需要覆盖这些方面,以提供全面的答案/比较(没有特定的重要性顺序): Speed
, Memory usage
, Syntax
和Features
。
我的意图是尽可能清楚地从data.table的angular度来涵盖每一个。
注意:除非明确指出,否则通过引用dplyr,我们引用dplyr的data.frame接口,其内部使用Rcpp在C ++中。
data.table语法的forms是一致的 – DT[i, j, by]
。 保持i
, j
和一起是由devise。 通过将相关操作集中在一起,可以轻松优化操作的速度和更重要的内存使用情况 ,并提供一些强大的function ,同时保持语法的一致性。
1.速度
不less基准testing(尽pipe大部分是分组操作)已经被添加到已经显示data.table的问题中,比dplyr 更快 ,因为要增加分组的数量和/或行数,包括Matt将基准分组从1000万在100 – 1000万个组和不同的分组列上,有20亿行 (100GB RAM),这也比较了pandas
。
在基准testing中,涵盖这些剩余的方面也是很好的:
-
分组涉及行的子集的操作 – 即
DT[x > val, sum(y), by = z]
types的操作。 -
基准其他操作,如更新和连接 。
-
除了运行时之外,还为每个操作testing内存占用量 。
2.内存使用情况
-
涉及dplyr中的
filter()
或slice()
的操作可能是内存效率低下的(在data.frames和data.tables上)。 看到这个职位 。请注意, 哈德利的评论是关于速度的 (他对这个速度很快),而这里主要关心的是记忆 。
-
目前data.table接口允许通过引用来修改/更新列(注意,我们不需要将结果重新分配给variables)。
# sub-assign by reference, updates 'y' in-place DT[x >= 1L, y := NA]
但dplyr 将永远不会通过引用更新。 dplyr相当于(注意结果需要重新分配):
# copies the entire 'y' column ans <- DF %>% mutate(y = replace(y, which(x >= 1L), NA))
对此的关注是参照透明度 。 通过引用更新data.table对象,特别是在函数内更新可能并不总是可取的。 但这是一个令人难以置信的有用的function:看到这个和这个post的有趣的情况。 我们想保留它。
因此,我们正在努力在data.table中导出
shallow()
函数,这将为用户提供两种可能性 。 例如,如果希望不修改函数中的inputdata.table,则可以这样做:foo <- function(DT) { DT = shallow(DT) ## shallow copy DT DT[, newcol := 1L] ## does not affect the original DT DT[x > 2L, newcol := 2L] ## no need to copy (internally), as this column exists only in shallow copied DT DT[x > 2L, x := 3L] ## have to copy (like base R / dplyr does always); otherwise original DT will ## also get modified. }
通过不使用
shallow()
,旧的function被保留:bar <- function(DT) { DT[, newcol := 1L] ## old behaviour, original DT gets updated by reference DT[x > 2L, x := 3L] ## old behaviour, update column x in original DT. }
通过使用
shallow()
创build浅拷贝 ,我们明白你不想修改原始对象。 我们会在内部处理所有事情,以确保在确保只有在绝对必要时才复制要修改的列。 实施时,这应该完全解决参考透明度问题,同时为用户提供两种可能性。此外,一旦
shallow()
导出dplyr的data.table接口应该避免几乎所有的副本。 所以那些喜欢dplyr语法的人可以在data.tables中使用它。但是它仍然缺乏data.table提供的许多function,包括(子)引用分配。
-
join时汇总:
假设你有两个data.tables如下:
DT1 = data.table(x=c(1,1,1,1,2,2,2,2), y=c("a", "a", "b", "b"), z=1:8, key=c("x", "y")) # xyz # 1: 1 a 1 # 2: 1 a 2 # 3: 1 b 3 # 4: 1 b 4 # 5: 2 a 5 # 6: 2 a 6 # 7: 2 b 7 # 8: 2 b 8 DT2 = data.table(x=1:2, y=c("a", "b"), mul=4:3, key=c("x", "y")) # xy mul # 1: 1 a 4 # 2: 2 b 3
你想在
DT2
每一行获得sum(z) * mul
,同时按列x,y
。 我们可以:-
1)合计
DT1
以获得sum(z)
,2)执行连接,3)乘以(或)# data.table way DT1[, .(z = sum(z)), keyby = .(x,y)][DT2][, z := z*mul][] # dplyr equivalent DF1 %>% group_by(x, y) %>% summarise(z = sum(z)) %>% right_join(DF2) %>% mutate(z = z * mul)
-
2)一气呵成(使用
by = .EACHI
function):DT1[DT2, list(z=sum(z) * mul), by = .EACHI]
有什么优势?
-
我们不必为中间结果分配内存。
-
我们不必分组/散列两次(一个用于聚合,另一个用于join)。
-
更重要的是,通过查看(2)中的
j
,我们想要执行的操作是清楚的。
by = .EACHI
查看这个post的详细解释。 没有中间结果被实现,并且join +聚合一次完成。看看这个 , 这个和这个职位真正的使用场景。
在
dplyr
你将不得不首先join并聚集或聚合,然后join ,这两者在内存方面都不是那么有效率(这又转化为速度)。 -
-
更新和连接:
考虑下面显示的data.table代码:
DT1[DT2, col := i.mul]
在
DT2
的密钥列与DT1
匹配的那些行上添加/更新DT1
的列col
与来自DT2
mul
。 我不认为在dplyr
有这个操作的确切等价物,也就是说,在没有避免*_join
操作的情况下,它将不得不复制整个DT1
来添加一个新的列,这是不必要的。检查这个职位是一个真正的使用场景。
总而言之,认识到每一点优化都很重要。 正如格雷斯·霍珀(Grace Hopper)所说的那样, 介意你的纳秒 !
3.语法
现在让我们看看语法 。 哈德利在这里评论道:
数据表格速度非常快,但是我认为他们的简洁使得学习更难 ,使用它的代码在写完后更难阅读 。
我觉得这个评论毫无意义,因为它非常主观。 我们可以尝试的是比较句法的一致性 。 我们将并行比较data.table和dplyr语法。
我们将使用下面显示的虚拟数据:
DT = data.table(x=1:10, y=11:20, z=rep(1:2, each=5)) DF = as.data.frame(DT)
-
基本的聚合/更新操作。
# case (a) DT[, sum(y), by = z] ## data.table syntax DF %>% group_by(z) %>% summarise(sum(y)) ## dplyr syntax DT[, y := cumsum(y), by = z] ans <- DF %>% group_by(z) %>% mutate(y = cumsum(y)) # case (b) DT[x > 2, sum(y), by = z] DF %>% filter(x>2) %>% group_by(z) %>% summarise(sum(y)) DT[x > 2, y := cumsum(y), by = z] ans <- DF %>% group_by(z) %>% mutate(y = replace(y, which(x > 2), cumsum(y))) # case (c) DT[, if(any(x > 5L)) y[1L]-y[2L] else y[2L], by = z] DF %>% group_by(z) %>% summarise(if (any(x > 5L)) y[1L] - y[2L] else y[2L]) DT[, if(any(x > 5L)) y[1L] - y[2L], by = z] DF %>% group_by(z) %>% filter(any(x > 5L)) %>% summarise(y[1L] - y[2L])
-
data.table的语法是紧凑的,dplyr的相当详细。 (a)情况或多或less相同。
-
在(b)情况下,我们不得不在dplyr中使用
filter()
进行汇总 。 但是在更新时 ,我们必须移动mutate()
内的逻辑。 然而,在data.table中,我们使用相同的逻辑表示这两个操作 – 对x > 2
行进行操作,但是在第一种情况下,得到sum(y)
,而在第二种情况下,用累计和更新那些行。当我们说
DT[i, j, by]
表单是一致的DT[i, j, by]
这就是我们的意思。 -
同样在(c)的情况下,当我们有
if-else
条件时,我们可以在data.table和dplyr中expression逻辑“原样” 。 但是,如果我们只想返回if
条件满足的那些行,否则就不能直接使用summarise()
(AFAICT)。 我们必须首先filter()
,然后进行总结,因为summarise()
总是期望单个值 。虽然它返回相同的结果,但是在这里使用
filter()
会使实际操作变得不那么明显。在第一种情况下也可以使用
filter()
(对我来说看起来并不明显),但是我的观点是我们不应该这样做。
-
-
汇总/更新多列
# case (a) DT[, lapply(.SD, sum), by = z] ## data.table syntax DF %>% group_by(z) %>% summarise_each(funs(sum)) ## dplyr syntax DT[, (cols) := lapply(.SD, sum), by = z] ans <- DF %>% group_by(z) %>% mutate_each(funs(sum)) # case (b) DT[, c(lapply(.SD, sum), lapply(.SD, mean)), by = z] DF %>% group_by(z) %>% summarise_each(funs(sum, mean)) # case (c) DT[, c(.N, lapply(.SD, sum)), by = z] DF %>% group_by(z) %>% summarise_each(funs(n(), mean))
-
在(a)的情况下,代码或多或less是等同的。 data.table使用熟悉的基本函数
lapply()
,而dplyr
引入*_each()
以及一堆函数来funs()
。 -
data.table's
:=
需要提供列名,而dplyr会自动生成。 -
在情况(b)中,dplyr的语法相对简单。 在data.table的列表中改进多个函数的聚合/更新。
-
在(c)的情况下,dplyr会返回
n()
次数为许多列,而不是一次。 在data.table中,我们所要做的就是返回j
一个列表。 列表中的每个元素都将成为结果中的一列。 因此,我们可以再次使用熟悉的基本函数c()
将.N
连接到返回list
。
注意:再一次在data.table中,我们所要做的就是返回
j
一个列表。 列表中的每个元素都将成为结果中的一列。 你可以使用c()
,as.list()
,lapply()
,list()
等…基本函数来完成这个任务,而不必学习任何新的函数。您至less需要学习特殊variables –
.N
和.SD
。 dplyr中的等价物是n()
和.
-
-
加盟
dplyr为每种types的连接提供单独的函数,data.table允许使用相同的语法
DT[i, j, by]
(和reason)进行连接。 它还提供了一个等效的merge.data.table()
函数作为替代。setkey(DT1, x, y) # 1. normal join DT1[DT2] ## data.table syntax left_join(DT2, DT1) ## dplyr syntax # 2. select columns while join DT1[DT2, .(z, i.mul)] left_join(select(DT2, x, y, mul), select(DT1, x, y, z)) # 3. aggregate while join DT1[DT2, .(sum(z) * i.mul), by = .EACHI] DF1 %>% group_by(x, y) %>% summarise(z = sum(z)) %>% inner_join(DF2) %>% mutate(z = z*mul) %>% select(-mul) # 4. update while join DT1[DT2, z := cumsum(z) * i.mul, by = .EACHI] ?? # 5. rolling join DT1[DT2, roll = -Inf] ?? # 6. other arguments to control output DT1[DT2, mult = "first"] ??
-
有些人可能会发现一个单独的函数为每个连接更好(左,右,内,反,半等),而其他人可能喜欢data.table的
DT[i, j, by]
或merge()
R. -
然而,dplyrjoin这样做。 而已。 没有什么。
-
data.tables可以在join(2)时select列,而在dplyr中,如上所示,您需要先
select()
两个data.frames才能join。 否则,你会用不必要的列重新join连接,以后再删除它们,这样效率不高。 -
data.tables可以在join (3) 时join ,也可以在join (4)时更新 ,使用
by = .EACHI
特性。 为什么把整个连接结果重新添加/更新一些列? -
data.table能够滚动连接 (5) – 前滚,LOCF , 后退,NOCB , 最近 。
-
data.table也有
mult =
参数select第一 , 最后或全部匹配(6)。 -
data.table有
allow.cartesian = TRUE
参数来防止意外的无效连接。
-
再一次,语法与
DT[i, j, by]
一致DT[i, j, by]
带有额外的参数,可以进一步控制输出。
-
do()
…dplyr的总结是专门为返回单个值的函数而devise的。 如果你的函数返回多个/不等值,你将不得不求助于
do()
。 你必须事先知道你所有的函数返回值。DT[, list(x[1], y[1]), by = z] ## data.table syntax DF %>% group_by(z) %>% summarise(x[1], y[1]) ## dplyr syntax DT[, list(x[1:2], y[1]), by = z] DF %>% group_by(z) %>% do(data.frame(.$x[1:2], .$y[1])) DT[, quantile(x, 0.25), by = z] DF %>% group_by(z) %>% summarise(quantile(x, 0.25)) DT[, quantile(x, c(0.25, 0.75)), by = z] DF %>% group_by(z) %>% do(data.frame(quantile(.$x, c(0.25, 0.75)))) DT[, as.list(summary(x)), by = z] DF %>% group_by(z) %>% do(data.frame(as.list(summary(.$x))))
-
.SD
的等价物是.
-
在data.table中,你可以抛出几乎任何东西在
j
– 唯一要记住的是它返回一个列表,以便列表中的每个元素被转换为列。 -
在dplyr中,不能这样做。 不得不求助于
do()
这取决于你是如何肯定你的函数是否总是返回一个单一的值。 而且速度很慢。
-
data.table的语法再一次与
DT[i, j, by]
。 我们可以继续用j
expression式,而不必担心这些事情。
看看这个SO问题和这个 。 我想知道是否可以使用dplyr的语法直接expression答案…
总而言之,我特别强调了 dplyr的语法效率低下,受限制或无法使操作直接进行的几个例子。 这尤其是因为data.table对于“难以阅读/学习”的语法(如上面粘贴/链接的那个)有相当的反感。 大多数涵盖dplyr的post都是关于最直接的操作。 这很好。 但是,要明白其语法和function的限制也是非常重要的,而且我还没有看到一个post。
data.table也有它的怪癖(其中一些我已经指出,我们正试图修复)。 我们也试图改善data.table的连接,正如我在这里强调的那样。
但是也应该考虑dplyr与data.table相比所缺less的特性的数量。
4.特点
我已经指出了这里和这篇文章中的大部分function。 此外:
-
fread – 快速的文件阅读器已经有很长一段时间了。
-
fwrite – 在当前版本v1.9.7中新增了一个并行快速文件编写器。 看到这个职位的详细解释实施和#1664跟踪进一步的发展。
-
自动索引 – 另一个方便的function,可以在内部优化基本R语法。
-
临时分组 :
dplyr
在summarise()
期间通过对variables进行分组dplyr
自动分类结果,这可能并不总是令人满意的。 -
上面提到的data.table连接(速度/内存效率和语法)有许多优点。
-
非 Equi 连接 :是v1.9.7 +中的新function。 它允许使用其他运算符
<=, <, >, >=
的连接以及data.table连接的所有其他优点。 -
最近在data.table中实现了重叠范围连接 。 查看这篇文章的基准testing概述。
-
setorder()
函数允许通过引用真正快速地重新sortingdata.tables。 -
dplyr使用相同的语法为数据库提供接口 ,data.table目前不在。
-
data.table
提供了从v1.9.7 +(由Jan Gorecki编写) –fsetdiff
,fintersect
,funion
和fsetequal
以及其他all
参数(如在SQL中)的快速等价操作 。 -
data.table加载干净,没有掩码警告,并有一个机制在这里描述
[.data.frame
兼容性时,传递给任何R包。 dplyr更改基本函数filter
,lag
和[
这可能会导致问题; 例如在这里和这里 。
最后:
-
在数据库上 – 没有理由为什么data.table不能提供类似的接口,但这不是现在的优先事项。 如果用户非常喜欢这个function,它可能会变得很糟糕..不确定。
-
在并行性上 – 一切都很困难,直到有人前进去做。 当然,这将花费精力(线程安全)。
- 目前正在取得进展(在v1.9.7开发中),将已知耗时的部件并行化,以便使用
OpenMP
获得增量性能提升。
- 目前正在取得进展(在v1.9.7开发中),将已知耗时的部件并行化,以便使用
以下是阿伦的回答的大致轮廓(但是根据不同的优先顺序进行了一些重新排列),我试图从双面的angular度作出全面的回答。
句法
语法有一些主观性,但是我坚持认为data.table的简洁使得学习和难于阅读变得更加困难。 这部分是因为dplyr正在解决一个更容易的问题!
dplyr为你做的一件非常重要的事是它限制了你的select。 我声称大多数单表问题都可以用五个关键动词过滤,select,变异,排列和总结,并与“分组”副词一起解决。 当你学习数据操作时,这个约束是很有帮助的,因为这有助于你对问题的思考。 在dplyr中,这些动词中的每一个都映射到一个函数。 每个function都有一项工作,并且很容易孤立地理解。
通过将这些简单的操作与%>%
一起pipe道,可以创build复杂性。 以下是Arun 链接到的其中一个post的示例:
diamonds %>% filter(cut != "Fair") %>% group_by(cut) %>% summarize( AvgPrice = mean(price), MedianPrice = as.numeric(median(price)), Count = n() ) %>% arrange(desc(Count))
即使你以前从未见过dplyr(甚至是R!),你仍然可以得到所发生的事情,因为这些function都是英语动词。 英语动词的缺点是,他们需要更多的打字, [
但我认为可以通过更好的自动完成缓解。
这里是等价的data.table代码:
diamondsDT <- data.table(diamonds) diamondsDT[ cut != "Fair", .(AvgPrice = mean(price), MedianPrice = as.numeric(median(price)), Count = .N ), by = cut ][ order(-Count) ]
除非您已经熟悉data.table,否则很难遵循这些代码。 (我也无法弄清楚如何缩小重复[
以我眼中看起来不错的方式)。 就个人而言,当我查看6个月前写的代码时,就像查看一个陌生人编写的代码,所以我宁愿直接select,如果是冗长的代码。
我认为还有两个小小的因素会使可读性略有下降:
-
由于几乎每个数据表操作都使用
[
你需要额外的上下文来弄清楚发生了什么事情。 例如,x[y]
连接两个数据表还是从数据框中提取列? 这只是一个小问题,因为在写得很好的代码中,variables名应该提示发生了什么。 -
我喜欢那个
group_by()
是在dplyr中的一个单独的操作。 它从根本上改变了计算,所以我认为在略读代码时应该是明显的,并且比[.data.table
by
参数更容易findgroup_by()
。
我也喜欢这个pipe道不仅限于一个包装。 你可以先用tidyr整理你的数据,然后用ggvis的情节来完成 。 而且您不仅限于我编写的软件包 – 任何人都可以编写一个function,构成数据操作pipe道的无缝部分。 实际上,我更喜欢用%>%
重写以前的data.table代码:
diamonds %>% data.table() %>% .[cut != "Fair", .(AvgPrice = mean(price), MedianPrice = as.numeric(median(price)), Count = .N ), by = cut ] %>% .[order(-Count)]
而%>%
pipe道的想法并不仅限于数据框架,而且很容易推广到其他环境: 交互式Webgraphics , 网页抓取 , 要点 , 运行时合同等)
内存和性能
我把它们混在一起,因为对我来说,它们并不重要。 大多数R用户的数据量低于100万行,dplyr足够快,足以处理那些您不知道处理时间的数据大小。 我们在媒体数据上优化dplyrperformance力; 随意使用data.table来获取更大数据的原始速度。
dplyr的灵活性也意味着您可以使用相同的语法轻松调整性能特征。 如果dataframe后端的dplyr性能不够好,则可以使用data.table后端(虽然function稍有限制)。 如果您正在使用的数据不适合内存,则可以使用数据库后端。
所有这一切,长远来看,dplyr的performance将会变得更好。 我们肯定会实现data.table的一些很棒的想法,比如基数sorting和对连接和filter使用相同的索引。 我们也在进行并行处理,所以我们可以利用多个内核。
特征
我们计划在2015年开展一些工作:
-
fastread软件包,使得从磁盘和内存中获取文件变得容易,与
fread()
分开。 -
更灵活的连接,包括对非连接的支持。
-
更为灵活的组合,如bootstrap样本,汇总和更多
我也在投入时间来改进R的数据库连接器 ,与networkingapis交谈的能力,并使得更容易刮取html页面 。
直接回答问题标题 …
dplyr
肯定做data.table
不能的东西。
你的观点#3
dplyr抽象(或将)潜在的数据库交互
是直接回答你自己的问题,但没有提升到足够高的水平。 dplyr
是真正的多种数据存储机制的可扩展前端, data.table
是一个扩展。
将dplyr
看作后端不可知界面,所有目标都使用相同的语法,您可以随意扩展目标和处理程序。 从dplyr
angular度来看, data.table
是其中的一个目标。
你永远不会(我希望)看到一天data.table
试图翻译你的查询来创buildSQL语句,在磁盘或networking数据存储操作。
dplyr
可能会做的事情data.table
不会也可能不会做。
基于在内存中工作的devise, data.table
可能会比dplyr
更难将其本身扩展为查询的并行处理。
为了回应身体上的问题
用法
是否有分析任务比熟悉软件包的人更容易使用一个或另一个软件包进行编码(即,所需的击键与所需的深奥级别的组合,其中每一个都是好事)。
This may seem like a punt but the real answer is no. People familiar with tools seem to use the either the one most familiar to them or the one that is actually the right one for the job at hand. With that being said, sometimes you want to present a particular readability, sometimes a level of performance, and when you have need for a high enough level of both you may just need another tool to go along with what you already have to make clearer abstractions.
性能
Are there analytical tasks that are performed substantially (ie more than 2x) more efficiently in one package vs. another.
Again, no. data.table
excels at being efficient in everything it does where dplyr
gets the burden of being limited in some respects to the underlying data store and registered handlers.
This means when you run into a performance issue with data.table
you can be pretty sure it is in your query function and if it is actually a bottleneck with data.table
then you've won yourself the joy of filing a report. This is also true when dplyr
is using data.table
as the back-end; you may see some overhead from dplyr
but odds are it is your query.
When dplyr
has performance issues with back-ends you can get around them by registering a function for hybrid evaluation or (in the case of databases) manipulating the generated query prior to execution.
Also see the accepted answer to when is plyr better than data.table?