- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
概述
我对 data.table
比较熟悉,对 dplyr
不太熟悉。我已经阅读了一些 dplyr
vignettes 和出现在 SO 上的例子,到目前为止我的结论是:
data.table
和 dplyr
在速度上是相当的,除非有很多(即 >10-100K)组,以及在其他一些情况下(参见下面的基准测试)1314239dplyr
有更易访问的语法 dplyr
抽象(或将)潜在的数据库交互 data.table
相当熟悉,但我知道对于这两个新用户来说,这将是一个重要因素。我想避免争论哪个更直观,因为这与我从已经熟悉
data.table
的人的角度提出的具体问题无关。我也想避免讨论“更直观”如何导致更快的分析(当然是真的,但同样,这不是我在这里最感兴趣的)。
dplyr
会提供超出我在
data.table
中已经可以做的更多。这是
dplyr
解决方案(Q 末尾的数据):
dat %.%
group_by(name, job) %.%
filter(job != "Boss" | year == min(year)) %.%
mutate(cumu_job2 = cumsum(job2))
data.table
解决方案的 hack 尝试要好得多。也就是说,好的
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's question ,注意操作:这看起来将在 dplyr 0.5 ,也@beginneR显示在回答用do
潜在的变通@实现eddi 的问题)。 data.table
支撑 rolling joins (感谢@dholstius)以及 overlap joins data.table
内部优化了 DT[col == value]
或 DT[col %in% values]
形式的表达式,以通过使用二进制搜索的自动索引来提高速度,同时使用相同的基本 R 语法。 See here 了解更多细节和一个小基准。 dplyr
提供的功能标准评估版本(例如regroup
,summarize_each_
),可以简化程序中使用的dplyr
(注意程序中使用的data.table
是绝对有可能,只是需要一些认真思考,置换/报价,等等,至少据我所知) data.table
变得基本上快除外。 data.table
鳞比dplyr
更好,因为群体数量的增加(更新在两个包的最新增强和R最近的版本)。此外,尝试获得 unique values 时的基准测试速度快 data.table
~6x。 data.table
75%/应用/排序而dplyr
是在较小的( another SO question from comments ,由于达纳斯)快40%。 data.table
的主要作者,有 benchmarked grouping operations on data.table
, dplyr
and python pandas
on up to 2 billion rows (~100GB in RAM) 。 data.table
~823 快 823 10 4 0dat <- 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))
最佳答案
我们至少需要涵盖这些方面以提供全面的答案/比较(没有特别的重要性顺序): Speed
、 Memory usage
、 Syntax
和 0x251812221313
我的目的是从 data.table 的角度尽可能清楚地涵盖每一个。
Note: unless explicitly mentioned otherwise, by referring to dplyr, we refer to dplyr's data.frame interface whose internals are in C++ using Rcpp.
Features
。将
DT[i, j, by]
、
i
和
j
保持在一起是设计使然。通过将相关操作放在一起,它可以轻松优化操作的速度,更重要的是内存使用,还提供一些强大的功能,同时保持语法的一致性。
by
。另见
updated benchmarks ,其中包括
pandas
和
Spark
。
pydatatable
类型操作。DT[x > val, sum(y), by = z]
或 filter()
的操作可能是内存效率低下的(在 data.frames 和 data.tables 上)。 See this post。Note that Hadley's comment talks about speed (that dplyr is plentiful fast for him), whereas the major concern here is memory.
# 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))
对此的一个关注是 referential transparency 。通过引用更新 data.table 对象,尤其是在函数内可能并不总是可取的。但这是一个非常有用的功能:有关有趣的案例,请参见 this 和 this 帖子。我们想保留它。slice()
函数,这将为用户提供两种可能性。例如,如果不想修改函数内的输入 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()
创建浅拷贝,我们了解到您不想修改原始对象。我们在内部处理所有事情,以确保同时确保仅在绝对必要时复制您修改的列。实现后,这应该完全解决引用透明度问题,同时为用户提供两种可能性。Also, once
shallow()
is exported dplyr's data.table interface should avoid almost all copies. So those who prefer dplyr's syntax can use it with data.tables.
But it will still lack many features that data.table provides, including (sub)-assignment by reference.
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
并且您想在 shallow()
中的每一行中获得 sum(z) * mul
,同时按列 DT2
加入。我们可以:x,y
得到 DT1
,2)执行连接和 3)乘(或)sum(z)
功能):by = .EACHI
,我们想要执行的操作就很清楚了。j
的详细说明。没有中间结果被具体化,join+aggregate 是一次性执行的。
by = .EACHI
中,您必须使用
join and aggregate or aggregate first and then join ,就内存而言(这反过来又转化为速度),这两种方法都没有效率。
DT1[DT2, col := i.mul]
添加/更新 dplyr
的列 DT1
与 col
来自 mul
的那些行,其中 0x251813121313141313131313131313131413143 列匹配。我认为 DT2
中没有与此操作完全等效的操作,即,在不避免 DT2
操作的情况下,它必须复制整个 DT1
只是为了向其中添加一个新列,这是不必要的。To summarise, it is important to realise that every bit of optimisation matters. As Grace Hopper would say, Mind your nanoseconds!
Data tables are extremely fast but I think their concision makes it harder to learn and code that uses it is harder to read after you have written it ...
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])
dplyr
。但是在更新时,我们不得不将逻辑移到 *_join
中。然而,在 data.table 中,我们用相同的逻辑表达两个操作 - 对 DT1
的行进行操作,但在第一种情况下,得到 filter()
,而在第二种情况下,用其累积和更新这些行的 mutate()
。x > 2
形式一致时的意思。sum(y)
条件时,我们能够在 data.table 和 dplyr 中“按原样”表达逻辑。但是,如果我们只想返回满足 y
条件的那些行,否则就跳过,我们不能直接使用 DT[i, j, by]
(AFAICT)。我们必须先 if-else
然后总结,因为 if
总是期望一个值。summarise()
会使实际操作不那么明显。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))
summarise()
,而 filter()
引入了 filter()
和一堆函数到 lapply()
。dplyr
需要提供列名,而 dplyr 会自动生成它。*_each()
的列数,而不是一次。在 data.table 中,我们需要做的就是返回 funs()
中的列表。列表的每个元素都将成为结果中的一列。因此,我们可以再次使用熟悉的基函数 :=
将 n()
连接到 j
,后者返回 c()
。Note: Once again, in data.table, all we need to do is return a list in
j
. Each element of the list will become a column in result. You can usec()
,as.list()
,lapply()
,list()
etc... base functions to accomplish this, without having to learn any new functions.
You will need to learn just the special variables -
.N
and.SD
at least. The equivalent in dplyr aren()
and.
.N
(并有理由)进行连接。它还提供等效的 list
函数作为替代。 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"]
??
list
或 DT[i, j, by]
类似于 base R.merge.data.table()
才能加入,如上所示。否则,您会使用不必要的列来实现连接,只是为了稍后删除它们,这是低效的。DT[i, j, by]
特性 (3) 并在加入时更新 (4)。为什么将整个连接结果物化为仅添加/更新几列?merge()
参数,它选择第一个、最后一个或所有匹配项 (6)。select()
参数来防止意外的无效连接。Once again, the syntax is consistent with
DT[i, j, by]
with additional arguments allowing for controlling the output further.
by = .EACHI
...mult =
。您必须事先了解所有函数的返回值。 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))))
allow.cartesian = TRUE
的等价物是 do()
do()
中抛出任何内容 - 唯一要记住的是它返回一个列表,以便列表中的每个元素都转换为一列。.SD
,具体取决于您对函数是否始终返回单个值的确定程度。而且速度很慢。Once again, data.table's syntax is consistent with
DT[i, j, by]
. We can just keep throwing expressions inj
without having to worry about these things.
To summarise, I have particularly highlighted several instances where dplyr's syntax is either inefficient, limited or fails to make operations straightforward. This is particularly because data.table gets quite a bit of backlash about "harder to read/learn" syntax (like the one pasted/linked above). Most posts that cover dplyr talk about most straightforward operations. And that is great. But it is important to realise its syntax and feature limitations as well, and I am yet to see a post on it.
data.table has its quirks as well (some of which I have pointed out that we are attempting to fix). We are also attempting to improve data.table's joins as I have highlighted here.
But one should also consider the number of features that dplyr lacks in comparison to data.table.
.
通过对 0x231341 期间的变量进行分组来自动对结果进行排序,这可能并不总是可取的j
以及 data.table 连接的所有其他优点进行连接。do()
在 data.table 中的函数允许通过引用真正快速地重新排序 data.tables。dplyr
提供的一组操作更快当量(由Jan Gorecki写入) - summarise()
,<=, <, >, >=
,setorder()
和data.table
用额外fsetdiff
参数(如SQL)。fintersect
兼容性。 dplyr 更改基本函数 funion
、 fsetequal
和 all
,这可能会导致问题;例如here 和 here 。[.data.frame
并行化已知的耗时部分以提高性能。 关于r - 数据表与 dplyr : can one do something well the other can't or does poorly?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21435339/
我有以下数据框: library(dplyr) df % rename_all(funs(stringr::str_replace_all(., "gh", "v"))) 我想结合使用 renam
我有以下数据框: library(dplyr) df % rename_all(funs(stringr::str_replace_all(., "gh", "v"))) 我想结合使用 renam
我有一个数据( df_1 ): df_1 % select_at(.vars = 'var_1') var_1 1 99.47262 10 25.91552 没关系。但: df_1
我正在尝试安装dplyr软件包,但收到一条错误消息,提示“库(dplyr)中存在错误:没有名为dplyr的软件包”。我正在使用窗口系统和Ri386 3.5.2。我尝试按照其他人的建议使用代码insta
假设我想以并行方式申请 myfunction到 myDataFrame 的每一行.假设 otherDataFrame是一个包含两列的数据框:COLUNM1_odf和 COLUMN2_odf出于某些原因
我目前正在构建一个包,我想知道是否有办法调用 %>%来自 dplyr 的操作符,而无需实际附加 dplyr 包。例如,对于从包中导出的任何函数,您可以使用双冒号 ( :: ) 调用它。所以如果我想使用
library(dplyr) mtcars %>% group_by(vs) %>% do(tt=t.test(mpg~am, data=.)) %>% mutate(t=tt$statist
我正在尝试为一组标准曲线构建一系列线性模型。 目前这段代码正在产生我想要的输出(每个线性模型的截距和斜率): slopes % group_by(plate, col, row, conc_ug_mL
我正在寻找替换我的一些使用 dplyr::do 的 R 代码,因为这个函数很快就会被弃用。我的很多工作都需要创建分层 CDF 图。使用 dply:do 时,我分层的变量作为变量传递给结果数据框,然后我
问题 我正在尝试使用 dplyr::mutate()和 dplyr::case_when()在数据框中创建新的数据列,该列使用存储在另一个对象(“查找列表”)中的数据填充,并基于数据框中列中的信息。
最近我发现了很棒的 dplyr.spark.hive启用 dplyr 的软件包前端操作 spark或 hive后端。 在包的 README 中有关于如何安装此包的信息: options(repos =
我正在尝试在 dplyr 链中使用 data.frame 两次。这是一个给出错误的简单示例 df % group_by(Type) %>% summarize(X=n()) %>% mu
当我浏览答案时 here , 我找到了 this solution与 data.frame 完全符合预期. library(dplyr) # dplyr_0.4.3 library(data.tab
我的数据来自一个数据库,根据我运行 SQL 查询的时间,该数据库可能包含一周到另一周不同的 POS 值。 不知道哪些值将在变量中使得自动创建报告变得非常困难。 我的数据如下所示: sample % p
我想定义与“扫帚”包中类似的功能 library(dplyr) library(broom) mtcars %>% group_by(am) %>% do(model = lm(mpg ~ w
set.seed(123) df % group_by(id) %>% mutate(roll.sum = c(x[1:4], zoo::rollapply(x, 5, sum))) # Groups
先来个样本数据 set.seed(123) dat 1 -4 2 6 3 -2 4
我有一个带列的数据框 x1, x2, group我想生成一个带有额外列的新数据框 rank表示x1的顺序在其组中。 有相关问题here ,但已接受的答案似乎不再有效。 到这里为止,很好: librar
我有一个示例 df,如下所示: d% group_by(CaseNo) %>% arrange(desc(Submissiondate)) %>% dplyr::mutate(rank = row_n
我有一个数据框,其中包含一些数据输入错误。 我希望将每组的这些异常值替换为每组最常见的值。 我的数据如下: df % group_by(CODE) %>% mutate(across(c(DOSAGE
我是一名优秀的程序员,十分优秀!