data.table vs dplyr:一个能做好某件事,另一个能';还是做得不好? 概述
我比较熟悉data.table vs dplyr:一个能做好某件事,另一个能';还是做得不好? 概述,r,data.table,dplyr,R,Data.table,Dplyr,我比较熟悉data.table,而不是dplyr。我已经通读了一些突然出现的例子,到目前为止,我的结论是: 数据。表和dplyr在速度上具有可比性,但有许多(即>10-100K)组的情况除外,以及在某些其他情况下(见下文基准测试) dplyr具有更易访问的语法 dplyr摘要(或将摘要)潜在的数据库交互 存在一些细微的功能差异(请参见下面的“示例/用法”) 在我心目中2。没有太大的影响力,因为我对它非常熟悉data.table,尽管我知道,对于两个领域的新手来说,它将是一个重要因素。我想避免关于
data.table
,而不是dplyr
。我已经通读了一些突然出现的例子,到目前为止,我的结论是:
数据。表
和dplyr
在速度上具有可比性,但有许多(即>10-100K)组的情况除外,以及在某些其他情况下(见下文基准测试)dplyr
具有更易访问的语法dplyr
摘要(或将摘要)潜在的数据库交互data.table
,尽管我知道,对于两个领域的新手来说,它将是一个重要因素。我想避免关于哪个更直观的争论,因为这与我从熟悉data.table
的人的角度提出的具体问题无关。我还想避免讨论“更直观”如何导致更快的分析(当然是真的,但再一次,这不是我最感兴趣的)
问题:
我想知道的是:
dplyr
提供的功能远远超出了我在data.table
中所能提供的功能。以下是dplyr
解决方案(数据在Q末尾):
这比我尝试的data.table
解决方案要好得多。也就是说,很好的数据。table
解决方案也很好(感谢Jean-Robert,Arun,请注意,在这里我倾向于使用单一语句,而不是严格意义上的最佳解决方案):
后者的语法可能看起来非常深奥,但如果您习惯了data.table
(即不使用一些更深奥的技巧),它实际上非常简单
理想情况下,我想看到的是一些很好的例子,如dplyr
或数据。table
方法更简洁或性能更好
例子
用法
不允许返回任意行数的分组操作(从,注意:这看起来将在中实现,@初学者在回答@eddi的问题时显示了使用dplyr
的潜在解决方法)do
支持(感谢@dholstius)以及data.table
内部优化了data.table
或DT[col==value]
形式的表达式,通过使用二进制搜索的自动索引,同时使用相同的基本R语法,提高了速度。了解更多细节和一个小基准DT[col%in%values]
提供标准的函数评估版本(例如,dplyr
,regroup
),可以简化summary\u each
dplyr的编程使用(注意,
绝对是可能的,只是需要仔细考虑、替换/引用等,至少据我所知)数据的编程使用。表
- 我运行了并发现这两个包在“split-apply-combine”风格的分析中是可比较的,除非有非常多的组(>100K),此时
变得更快数据。table
- @Arun运行了一些,显示
数据。随着组数的增加,
表的伸缩性比dplyr
dplyr更好(更新了包中的最新增强功能和最新版本的R)。此外,尝试获取数据时的基准测试速度比
表快6倍
- (未验证)具有
在较大版本的组/应用/排序上快75%,而数据。表
在较小版本上快40%(,感谢danas)dplyr
- 马特是《数据表》的主要作者
- 具有
数据。表格的速度大约快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))
dat直接回答问题标题。。。
dplyr
肯定做了data.table
做不到的事情。
你的观点是#3
dplyr抽象(或将抽象)潜在的数据库交互
是对您自己问题的直接回答,但没有提升到足够高的级别。dplyr
确实是一个可扩展的前端,可扩展到多个数据存储机制,其中asdata.table
是对单个数据存储机制的扩展
将dplyr
视为后端不可知界面,所有目标都使用相同的语法,您可以在其中随意扩展目标和处理程序。数据。从dplyr
的角度来看,表是其中一个目标
您永远不会(我希望)看到有一天数据。table
试图转换您的查询以创建SQL语句,这些SQL语句可以在磁盘或网络数据存储中运行
dplyr
可能会处理数据。table
不会也可能不会处理。
基于内存工作的设计,data.table
将自身扩展到查询的并行处理可能比dplyr
困难得多
在回答体内问题时。。。
用法
对于熟悉软件包的人来说,是否有更容易用一个或另一个软件包进行编码的分析任务(i
setDT(dat)[,
.SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)],
by=list(id, job)
]
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))
# sub-assign by reference, updates 'y' in-place
DT[x >= 1L, y := NA]
# copies the entire 'y' column
ans <- DF %>% mutate(y = replace(y, which(x >= 1L), NA))
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.
}
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.
}
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
DT1[DT2, col := i.mul]
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])
# 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))
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"]
??
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))))
diamonds %>%
filter(cut != "Fair") %>%
group_by(cut) %>%
summarize(
AvgPrice = mean(price),
MedianPrice = as.numeric(median(price)),
Count = n()
) %>%
arrange(desc(Count))
diamondsDT <- data.table(diamonds)
diamondsDT[
cut != "Fair",
.(AvgPrice = mean(price),
MedianPrice = as.numeric(median(price)),
Count = .N
),
by = cut
][
order(-Count)
]
diamonds %>%
data.table() %>%
.[cut != "Fair",
.(AvgPrice = mean(price),
MedianPrice = as.numeric(median(price)),
Count = .N
),
by = cut
] %>%
.[order(-Count)]
new_table <- mtcars2 %>%
lazy_dt() %>%
filter(wt < 5) %>%
mutate(l100k = 235.21 / mpg) %>% # liters / 100 km
group_by(cyl) %>%
summarise(l100k = mean(l100k))
new_table
#> Source: local data table [?? x 2]
#> Call: `_DT1`[wt < 5][, `:=`(l100k = 235.21/mpg)][, .(l100k = mean(l100k)),
#> keyby = .(cyl)]
#>
#> cyl l100k
#> <dbl> <dbl>
#> 1 4 9.05
#> 2 6 12.0
#> 3 8 14.9
#>
#> # Use as.data.table()/as.data.frame()/as_tibble() to access results