提问



概述



我对data.table比较熟悉,而不是dplyr。我已经阅读了一些dplyr小插曲和SO上出现的例子,到目前为止,我的结论是:[191]



  1. data.tabledplyr的速度相当,除非有许多(即> 10-100K)组,并且在某些其他情况下(见下面的基准)

  2. dplyr具有更易于理解的语法

  3. dplyr摘要(或将会)潜在的数据库交互

  4. 存在一些细微的功能差异(请参阅下面的示例/用法)



在我看来2.没有多大的重量,因为我对它很熟悉data.table,虽然我明白对于那些对这两者都不熟悉的用户来说这将是一个很重要的因素。我想避免争论哪个是更直观,因为这与我已经熟悉data.table的人的观点提出的具体问题无关。我也想避免讨论更直观如何导致更快的分析(当然是真的,但又一次,而不是我最感兴趣的是这里。


问题



我想知道的是:



  1. 对于那些熟悉软件包的人来说,是否需要使用一个或另一个软件包进行编码的分析任务更容易(即需要按键的一些组合与所需的深奥水平相结合,其中每个类别都少,这是一件好事)。

  2. 是否在一个包装与另一个包装中更有效地执行分析任务(即超过2倍)。



最近的一个问题让我想到了这个问题,因为直到那时我才认为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(即不使用一些更深奥的技巧),它实际上非常简单。


理想情况下,我希望看到的是一些很好的例子,dplyrdata.table方式实质上更简洁或表现更好。


实施例



用法


  • dplyr不允许返回任意行数的分组操作(来自 eddi的问题,注意:这看起来好像将在 dplyr 0.5中实现,同样,@ beginneR在回答@eddi的问题时使用do显示了潜在的解决方法。

  • data.table支持滚动连接(感谢@dholstius)以及重叠连接

  • data.table通过使用二进制文件的自动索引在内部优化 speed 形式DT[col == value]DT[col %in% values]的表达式使用相同的基本R语法搜索。请参阅此处了解更多详情和一个小小的基准。

  • dplyr提供功能的标准评估版本(例如regroupsummarize_each_),可简化dplyr的程序化使用(注意程序化使用data.table绝对是可能的,只需要一些仔细的思考,替换/引用等,至少据我所知)



基准


  • 我运行我自己的基准测试,发现两个软件包在拆分应用组合样式分析中具有可比性,除非有非常多的组(> 100K),此时data.table]]变得快得多。

  • @Arun在连接上运行了一些基准测试,显示随着组数量的增加data.tabledplyr更好地扩展(更新了包和最近版本中的最新增强功能) R)。此外,尝试获取唯一值时的基准data.table快6倍。

  • (未验证)[[/52]]在较大版本的群组/应用/排序上的速度提高75%,而dplyr在较小版本的群体上速度提高40%(来自评论的另一个SO问题,谢谢danas)。

  • Matt,data.table的主要作者,在data.tabledplyr和python pandas上进行了基准分组操作,最多可达20亿行(~100GB)在RAM)

  • 80K群体的较旧基准 data.table快8倍



数据



这是我在问题部分中展示的第一个例子。 [194] [197] [198] [199] [202] [203]


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))

最佳参考


我们需要至少涵盖这些方面,以提供全面的答案/比较(没有特别重要的顺序):SpeedMemory usageSyntaxFeatures


我的目的是从data.table的角度尽可能清楚地涵盖其中的每一个。



  注意:除非另有明确说明,否则通过引用dplyr,我们引用dplyr的data.frame接口,其内部使用Rcpp在C ++中。






data.table语法的形式一致 - DT[i, j, by]。保持ijby在一起是设计的。通过将相关操作保持在一起,它允许轻松优化速度的操作,更重要的是内存使用,并提供一些强大的功能,同时保持语法的一致性。


1。速度



相当多的基准测试(尽管主要是分组操作)已经添加到已经显示data.table的问题中,比dplyr更快 ,因为要分组的组和/或行的数量增加,包括基准测试Matt在 100 - 1000万组和不同的分组列上从 1000万行(RAM中100GB)进行分组,这也比较pandas。[[[204]


在基准测试中,覆盖这些剩余方面也很棒:



  • 对涉及行子集的分组操作 - 即DT[x > val, sum(y), by = z]类型的操作。

  • 对其他操作进行基准测试,例如更新和加入。

  • 除了运行时,还为每个操作基准内存占用。



2。内存使用




  1. dplyr中涉及filter()slice()的操作可能内存效率低(在data.frames和data.tables上)。看这篇文章。



      请注意,Hadley的评论谈论速度(dplyr对他来说很快),而主要关注的是记忆。


  2. data.table接口允许用户通过引用修改/更新列(请注意,我们不需要将结果重新分配回变量)。


    # 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对象,尤其是在函数内,可能并不总是需要。但这是一个非常有用的功能:看到这个和这个帖子的有趣案例。我们想保留它。[207]


    因此,我们正努力在data.table中导出shallow()函数,该函数将为用户提供两种可能性。例如,如果希望不修改函数中的输入data.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(),保留旧功能:


    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()创建浅拷贝,我们知道您不想修改原始对象。我们在内部处理所有内容以确保同时确保复制您修改的列仅在绝对必要时。实施后,这应该完全解决引用透明度问题,同时为用户提供两种可能性。



      此外,一旦shallow()被导出,dplyr的data.table接口应该避免几乎所有的副本。所以那些喜欢dplyr语法的人可以将它与data.tables一起使用。

      
      但它仍然缺少data.table提供的许多功能,包括通过引用的(子)分配。


  3. 加入时聚合:


    假设您有两个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"))
    #    x y z
    # 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"))
    #    x y mul
    # 1: 1 a   4
    # 2: 2 b   3
    


    并且你希望DT2DT2中的每一行加入x,y。我们可以:



    • 1)聚合DT1sum(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功能):


      DT1[DT2, list(z=sum(z) * mul), by = .EACHI]
      



    有什么好处?



    • 我们不必为中间结果分配内存。

    • 我们不必分组/哈希两次(一次用于聚合,另一次用于加入)。

    • 更重要的是,通过查看(2)中的j,我们想要执行的操作是明确的。



    查看这篇文章,了解by = .EACHI的详细说明。没有实现中间结果,并且一次性执行join + aggregate。


    看看这个,这个和这个帖子的实际使用场景。


    dplyr中,你必须首先加入并聚合或聚合然后加入,这两者在内存方面都不是那么有效(反过来又转化为速度)。[214]

  4. 更新并加入:


    考虑下面显示的data.table代码:


    DT1[DT2, col := i.mul]
    


    DT2的关键列匹配DT1的那些行上添加/更新DT1colmulDT2。我不认为dplyr中有这个操作的确切等价,即没有避免*_join操作,只需要复制整个DT1以添加新列对它来说,这是不必要的。


    查看此帖子了解真实的使用场景。




  总而言之,重要的是要意识到每一点优化都很重要。正如 Grace Hopper 会说,注意你的纳秒![216] [217]



3。语法



现在让我们看一下语法。哈德利在这里评论说:[218]



  数据表非常快,但我认为他们的简洁使得更难学习和使用它的代码在你编写后很难阅读 ...



我发现这句话毫无意义,因为它非常主观。我们可以尝试的是对比语法中的一致性。我们将并排比较data.table和dplyr语法。


我们将使用下面显示的虚拟数据:


DT = data.table(x=1:10, y=11:20, z=rep(1:2, each=5))
DF = as.data.frame(DT)



  1. 基本聚合/更新操作。


    # 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)的情况下,事情或多或少是等价的。

    • 在情况(b)中,我们必须在dplyr中使用filter()而总结。但是当更新时,我们不得不在mutate()中移动逻辑。然而,在data.table中,我们用相同的逻辑表示两个操作 - 在x > 2的行上操作,但在第一种情况下,得到sum(y),而在第二种情况下,为y更新那些行]]累计金额。


      当我们说DT[i, j, by]形式是一致的时,这就是我们的意思。

    • 类似于情况(c),当我们有if-else条件时,我们能够在data.table和dplyr中表达逻辑as-is。但是,如果我们只想返回if条件满足的那些行,否则我们不能直接使用summarise()(AFAICT)。我们必须首先filter()然后总结,因为summarise()总是期望单值。


      虽然它返回相同的结果,但在这里使用filter()会使实际操作不那么明显。


      在第一种情况下也很可能使用filter()(对我来说似乎不太明显),但我的观点是我们不应该这样做。


  2. 多列的聚合/更新


    # 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)中,代码或多或少相等。 data.table使用熟悉的基函数lapply(),而dplyr引入*_each()以及funs()的一堆函数。

    • data.tables :=需要提供列名,而dplyr会自动生成列名。

    • 在情况(b)中,dplyr的语法相对简单。改进多个函数的聚合/更新是在data.table的列表中。

    • 但是在(c)的情况下,dplyr会返回n()列的次数,而不是只返回一次。在data.table中,我们需要做的就是在j中返回一个列表。列表的每个元素都将成为结果中的一列。因此,我们可以再次使用熟悉的基函数c()来连接.Nlist,它返回list




      注意:再次,在data.table中,我们需要做的就是在j中返回一个列表。列表的每个元素都将成为结果中的列。您可以使用c()as.list()lapply()list()等...基本功能来完成此操作,而无需学习任何新功能。

      
      您至少需要学习特殊变量 - .N.SD。 dplyr中的等价物是n().


  3. 加入


    dplyr为每种类型的连接提供单独的函数,其中data.table允许使用相同的语法DT[i, j, by](和有原因)连接。它还提供等效的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.tableDT[i, j, by]merge()类似于基地R.

    • 然而dplyr加入就是这么做的。而已。没什么。

    • data.tables可以在加入(2)时选择列,而在dplyr中,如上所示,在加入之前,您需要首先在两个data.frames上select()。否则,您将仅使用不必要的列来实现连接,以便稍后删除它们,这是低效的。

    • data.tables可以在加入(3)时进行汇总,并在加入(4)时使用by = .EACHI功能进行更新。为什么要将整个连接结果添加/更新几列?

    • data.table能够滚动连接(5) - 前滚,LOCF,后滚,NOCB,最近。

    • data.table还有mult =参数,它选择 first , last 或 all 匹配(6)。

    • data.table有allow.cartesian = TRUE参数来防止意外的无效连接。





  再一次,语法与DT[i, j, by]一致,其他参数允许进一步控制输出。




  1. do() ...


    dplyr的摘要是专门为返回单个值的函数而设计的。如果你的函数返回多个/不相等的值,你将不得不诉诸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中抛出表达式,而不必担心这些事情。



看看这个问题和这个问题。我想知道是否有可能使用dplyr的语法表达答案直截了当[[...



  总而言之,我特别强调了几个实例,其中dplyr的语法效率低,有限或无法使操作变得简单。这尤其是因为data.table对更难的强烈反对阅读/学习语法(就像上面粘贴/链接的那样)。大多数关于dplyr的帖子都谈到了最简单的操作。这很棒。但是重要的是要实现它的语法和功能限制,我还没有看到它上面的帖子。

  
  data.table也有它的怪癖(其中一些我已经指出我们正试图修复)。我们也试图改进data.table的连接,正如我在这里强调的那样。

  
  但是,与data.table相比,还应该考虑dplyr缺少的功能数量。



4。功能



我在这里以及本文中都指出了大部分功能。此外:



  • fread - 快速文件阅读器已经有很长一段时间了。

  • fwrite - 当前开发版v1.9.7中的新功能,现在可以使用并行化快速文件编写器。有关实施的详细说明,请参阅此文章;有关进一步发展的信息,请参阅#1664。[227] [228]

  • 自动索引 - 在内部优化基本R语法的另一个便利功能。[229]

  • Ad-hoc分组:dplyr通过在summarise()期间对变量进行分组来自动对结果进行排序,这可能并不总是令人满意。

  • 上面提到的data.table连接(用于速度/内存效率和语法)的许多优点。

  • 非等联接:是v1.9.7 +中的新功能。它允许使用其他运算符<=, <, >, >=的连接以及data.table连接的所有其他优点。

  • 最近在data.table中实现了重叠范围连接。查看此帖子以获取基准测试的概述。 [230]

  • setorder()函数在data.table中,允许通过引用真正快速重新排序data.tables。

  • dplyr使用相同的语法为数据库提供接口,而data.table目前不提供。[232]

  • data.table从v1.9.7 +(由Jan Gorecki编写)提供更快的等效集合操作 - fsetdifffintersectfunionfsetequal附加all参数(如在SQL中)。

  • data.table干净地加载,没有屏蔽警告,并且在传递给任何R软件包时具有[.data.frame兼容性的此处所述的机制。 dplyr改变了可能导致问题的基函数filterlag[;例如这里和这里。






最后:



  • 在数据库上 - 没有理由说data.table不能提供类似的接口,但现在这不是优先事项。如果用户非常喜欢这个功能,它可能会被提升......不确定。

  • 关于并行性 - 一切都很困难,直到有人继续前进并做到这一点。当然需要付出努力(线程安全)。



    • 目前正在取得进展(在v1.9.7开发中),使用OpenMP来平衡已知耗时部分以实现增量性能增益。



其它参考1


这是我从dplyr角度全面回答的尝试,
遵循阿伦回答的大致轮廓(但有些重新排列
根据不同的优先事项)。


语法



语法有一些主观性,但我支持我的陈述
data.table的简洁使得学习更难,更难阅读。
这部分是因为dplyr解决了一个更容易的问题!


dplyr为你做的一件非常重要的事就是它
约束您的选择。我声称大多数单表问题都可以
只用五个关键动词过滤,选择,变异,排列和解决
总结,以及按组副词。这种约束是一个很大的帮助
当你在学习数据操作时,因为它有助于你的订单
想着这个问题。在dplyr中,每个动词都映射到a
单一功能。每个功能都可以完成一项工作,并且易于理解
处于隔离状态。


通过将这些简单的操作连接在一起,您可以创建复
%>%。这是一个例如Arun链接的一个帖子
到:


diamonds %>%
  filter(cut != "Fair") %>%
  group_by(cut) %>%
  summarize(
    AvgPrice = mean(price),
    MedianPrice = as.numeric(median(price)),
    Count = n()
  ) %>%
  arrange(desc(Count))


即使你以前从未见过dplyr(甚至是R!),你仍然可以得到
因为功能都是英语的,所发生的事情的要点
动词。英语动词的缺点是它们需要更多的打字
[,但我认为通过更好的自动完成可以在很大程度上减轻这种影响。


这是等效的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个月前写道,这就像看一个陌生人写的代码,
因此,如果详细,我会更喜欢直截了当的代码。


我认为其他两个小因素会略微降低可读性:



  • 由于几乎每个数据表操作都使用[,因此您需要额外的操作
    上下文来弄清楚发生了什么。例如,是x[y]
    连接两个数据表或从数据框中提取列?
    这只是一个小问题,因为在编写良好的代码中
    变量名称应该表明发生了什么。

  • 我喜欢group_by()是dplyr中的一个单独操作。它
    从根本上改变计算,所以我认为应该是显而易见的
    在略读代码时,更容易发现group_by()
    [.data.tableby参数。



我也喜欢管道
并不仅限于一个包装。你可以先整理你的包装
数据用
tidyr,和
完成ggvis中的情节。而你
不限于我写的包 - 任何人都可以写一个函数
它构成了数据操作管道的无缝部分。事实上,我
而不是更喜欢用%>%重写的先前data.table代码:[237] [238] [239]


diamonds %>% 
  data.table() %>% 
  .[cut != "Fair", 
    .(AvgPrice = mean(price),
      MedianPrice = as.numeric(median(price)),
      Count = .N
    ), 
    by = cut
  ] %>% 
  .[order(-Count)]


%>%管道的想法不仅限于数据框架和
很容易推广到其他环境:互动网络
图形,网络
刮痧,
gists,运行时
合同,......)[240] [241] [242] [243]


记忆和表现



我把它们混在一起,因为对我来说,它们并不那么重要。
大多数R用户的工作量低于100万行,dplyr就是
足够快,足以满足您不了解的数据大小
处理时间。我们优化dplyr以表达对中等数据的表达;
随时可以使用data.table获取更大数据的原始速度。


dplyr的灵活性也意味着您可以轻松调整性能
使用相同语法的特征。如果用dplyr的性能
数据帧后端对你来说不够好,你可以使用
data.table后端(虽然功能有限)。
如果您正在使用的数据不适合内存,那么您可以使用
数据库后端。


总而言之,dplyr的表现会在长期内变得更好。好
肯定会实现像radix这样的data.table的一些好主意
为连接排序和使用相同的索引过滤器。也是
致力于并行化,因此我们可以利用多个核心。


功能



我们计划在2015年开展的一些工作:



  • readr包,以便轻松地从磁盘和磁盘中获取文件
    记忆,类似于fread()

  • 更灵活的连接,包括对非等连接的支持。

  • 更灵活的分组,如bootstrap样本,汇总等



我也投入时间来改进R的数据库
连接器,与之交谈的能力
web apis,让它更容易
刮HTML页面。[244] [245] [246]

其它参考2


直接回应问题标题 ...



dplyr 绝对做data.table不能做的事情。



你的观点#3



  dplyr抽象(或将)潜在的DB交互



是对你自己的问题的直接回答,但没有提升到足够高的水平。dplyr确实是多个数据存储机制的可扩展前端,其中data.table是单个数据存储机制的扩展。


dplyr视为后端不可知接口,所有目标使用相同的语法,您可以随意扩展目标和处理程序。从dplyr的角度来看,data.table是其中一个目标。


你永远不会(我希望)看到data.table尝试翻译你的查询以创建与磁盘或网络数据存储一起运行的SQL语句。


dplyr可能会做data.table不会或不会做的事情。



基于内存工作的设计,data.table可能比dplyr更难以将自身扩展到并行处理查询。





回应身体内问题......



用法




  对于熟悉软件包的人来说,是否需要使用一个或另一个软件包进行编码的分析任务更容易(例如,需要按键的一些组合与所需的神秘程度,其中每个都是好事)。



这可能看起来像是一个平底船,但真正的答案是否定的。熟悉熟悉工具的人似乎使用了他们最熟悉的人或者实际上正确的工作人员。话虽如此,有时候你想要提供一个特定的可读性,有时候是一个性能水平,当你需要足够高的两者时,你可能只需要另一个工具来配合你已经拥有的更清晰的抽象。 。


性能




  是否存在在一个封装中相对于另一个封装中更有效地执行分析任务(即,大于2倍)的分析任务。



再一次,没有。 data.table​​]]擅长提高效率 it 所做的事情dplyr在某些方面受到限制,无法承担底层数据存储和注册处理程序的负担。


这意味着当你遇到data.table的性能问题时,你可以非常肯定它在你的查询函数中,如果 实际上是data.table的瓶颈那么你就是赢得报告的快乐。当dplyr使用data.table作为后端时也是如此;你可以看一些来自dplyr的开销,但可能是你的查询。


dplyr存在后端性能问题时,您可以通过注册混合评估函数或(在数据库的情况下)在执行之前操作生成的查询来绕过它们。


另请参阅plyr何时比data.table更好的接受答案?