由于这两个软件包的哲学在某些方面不同,所以没有简单直接的答案。因此,一些妥协是不可避免的。以下是您可能需要解决/考虑的一些问题。
涉及i
的操作(== dplyr 中的 filter()
和 slice()
)
假设DT
有10列。考虑下列data.table表达式:
DT[a > 1, .N]
DT[a > 1, mean(b), by=.(c, d)]
(1) 给出了在列a > 1
的DT
中行的数量。(2) 对于与(1)中相同的表达式,按c,d
分组返回mean(b)
。
常用的dplyr
表达式包括:
DT %>% filter(a > 1) %>% summarise(n()) ## --- (3)
DT %>% filter(a > 1) %>% group_by(c, d) %>% summarise(mean(b)) ## --- (4)
很明显,data.table 代码更加简短。此外,它们还更加内存高效1。为什么呢?因为在 (3) 和 (4) 中,filter()
首先返回所有10列的行,而在 (3) 中我们只需要行数,在 (4) 中我们只需要对列 b、c、d
进行连续操作。为了克服这个问题,我们需要事先select()
选择列:
DT %>% select(a) %>% filter(a > 1) %>% summarise(n()) ## --- (5)
DT %>% select(a,b,c,d) %>% filter(a > 1) %>% group_by(c,d) %>% summarise(mean(b)) ## --- (6)
两个包之间存在一种重要的哲学差异需要强调:
在data.table
中,我们喜欢将相关操作放在一起,并且这允许查看同一函数调用中的j-expression
并意识到不需要(1)中的任何列。计算i
中的表达式,并且.N
只是给出逻辑向量的总和,该向量给出了行数;整个子集从未实现。在(2)中,仅对列b,c,d
进行填充,在子集中忽略其他列。
但是在dplyr
中,哲学是让一个函数做好一件事情。如果在filter()
之后进行的操作需要过滤掉的所有列,目前至少没有办法告诉它。如果您希望高效执行此类任务,则需要提前考虑。我个人认为在这种情况下这是反直觉的。
请注意,在(5)和(6)中,我们仍然对我们不需要的列a
进行子集划分。但我不确定如何避免这种情况。如果filter()
函数有一个参数来选择返回的列,则可以避免此问题,但是这个函数将不会只做一个任务(这也是dplyr的设计选择)。
通过引用子分配
dplyr
永远不会按引用更新。这是两个包之间另一个巨大的(哲学性)差异。
例如,在data.table
中,您可以执行以下操作:
DT[a %in% some_vals, a := NA]
使用data.table更新符合条件的行中的列 a by reference。目前,dplyr会在内部深度复制整个data.table以添加新列。 @BrodieG已在他的回答中提到了这一点。
但是,当实现FR#617时,深拷贝可以被浅拷贝所取代。另外值得注意的是:dplyr:FR#614。请注意,仍然会始终复制您修改的列(因此速度较慢/内存效率较低)。没有办法通过引用来更新列。
其他功能
在data.table中,您可以同时汇总而不加入,并且这更容易理解,并且由于中间连接结果从未被具体化,因此内存效率更高。请查看此帖子以获取示例。您无法(目前)使用dplyr的data.table / data.frame语法执行此操作。
dplyr的语法也不支持data.table的rolling joins功能。
我们最近在data.table中实现了重叠连接以在区间范围内进行连接(这里是示例),它是一个单独的函数foverlaps()
,因此可以与pipe运算符(magrittr / pipeR?-从未尝试过)一起使用。但是,最终我们的目标是将其集成到[.data.table
中,以便我们可以利用其他功能,如分组、同时聚合等等。具有上述相同的限制。
自1.9.4以来,data.table使用辅助键实现了自动索引,以基于常规R语法进行快速二进制搜索子集。例如:DT[x == 1]
和DT[x%in%some_vals]
将在第一次运行时自动创建索引,然后将在从同一列进行的后续子集中使用二进制搜索来进行快速子集。该功能将继续发展。请查看此要点以了解此功能的简要概述。
从为data.tables实现的filter()
的方式来看,它无法利用此功能。
dplyr的一个功能是它还使用相同的语法提供与数据库的接口,而data.table目前没有这样做。
因此,你将需要权衡这些(可能还有其他)方面,并根据这些权衡来决定是否可以接受。
希望能帮到你。
(1)请注意,内存效率直接影响速度(特别是在数据变得更大时),因为在大多数情况下瓶颈是将数据从主内存移动到缓存(尽可能地利用缓存中的数据 - 减少缓存未命中 - 以便减少访问主内存)。这里不详细讨论。
dplyr
方法适用于数据表,但数据表也有自己可比较的方法。 - Rich Scrivendplyr
在data.frame
和相应的data.table
上的使用情况(以及其中的参考文献)。 - Henrik