此answer的question (Unique sorted rows single column from R data.table)提出了三种不同的方法来从data.table
获取已排序的唯一值的向量:
# 1
sort(salesdt[, unique(company)])
#2
sort(unique(salesdt$company))
#3
salesdt[order(company), unique(company)]
另一个answer提出了除字典顺序之外的其他排序选项:
salesdt[, .N, by = company][order(-N), company]
salesdt[, sum(sales), by = company][order(-V1), company]
data.table
由
library(data.table)
company <- c("A", "S", "W", "L", "T", "T", "W", "A", "T", "W")
item <- c("Thingy", "Thingy", "Widget", "Thingy", "Grommit",
"Thingy", "Grommit", "Thingy", "Widget", "Thingy")
sales <- c(120, 140, 160, 180, 200, 120, 140, 160, 180, 200)
salesdt <- data.table(company,item,sales)
与往常一样,如果有不同的选项可供选择,我开始想知道最佳解决方案是什么,特别是如果data.table
会更大。我已经搜索了一下,但到目前为止还没有找到一个特别的答案。
答案 0 :(得分:6)
对于基准测试,使用1.000.000行创建更大的data.table
:
n <- 1e6
set.seed(1234) # to reproduce the data
salesdt <- data.table(company = sample(company, n, TRUE),
item = sample(item, n, TRUE),
sales = sample(sales, n, TRUE))
为了完整起见,还有变体
# 4
unique(sort(salesdt$company))
# 5
unique(salesdt[,sort(company)])
将进行基准测试,虽然看起来很明显排序唯一值应该比其他方式更快。</ p>
此外,还包括此answer中的其他两个排序选项:
# 6
salesdt[, .N, by = company][order(-N), company]
# 7
salesdt[, sum(sales), by = company][order(-V1), company]
编辑:根据弗兰克的评论,我已经包含了他的建议:
# 8
salesdt[,logical(1), keyby = company]$company
基准测试是在microbenchmark
包的帮助下完成的:
timings <- microbenchmark::microbenchmark(
sort(salesdt[, unique(company)]),
sort(unique(salesdt$company)),
salesdt[order(company), unique(company)],
unique(sort(salesdt$company)),
unique(salesdt[,sort(company)]),
salesdt[, .N, by = company][order(-N), company],
salesdt[, sum(sales), by = company][order(-V1), company],
salesdt[,logical(1), keyby = company]$company
)
时间以
显示ggplot2::autoplot(timings)
请注意图表中的反向顺序(底部#1,顶部#8)。
正如预期的那样,变体#4和#5(排序后唯一)非常慢。 编辑#8是最快确认弗兰克评论的。
对我来说有点意外的是变种#3。尽管data.table
的快速基数排序,它的效率低于#1和#2。它似乎首先排序,然后提取唯一值。
company
受此观察的启发,我使用data.table
键入的company
重复了基准。
setkeyv(salesdt, "company")
时间显示(请不要时间轴的规模变化)#4和#5通过键控显着加速。它们比#3更快。 请注意,变体#8的时间安排包含在下一部分中。
变体#3仍包含order(company)
,如果已由company
键入,则不需要order
。所以,我从#3和#5中删除了对sort
和timings <- microbenchmark::microbenchmark(
sort(salesdt[, unique(company)]),
sort(unique(salesdt$company)),
salesdt[, unique(company)],
unique(salesdt$company),
unique(salesdt[, company]),
salesdt[, .N, by = company][order(-N), company],
salesdt[, sum(sales), by = company][order(-V1), company],
salesdt[,logical(1), keyby = company]$company
)
的不必要的调用:
data.table v.1.9.7
时间现在显示同一级别的变体#1到#4。 编辑:再次,#8(弗兰克的解决方案)是紧固件。
警告:基准测试基于原始数据,仅包含5个不同的字母作为公司名称。对于大量不同的公司名称,结果可能会有所不同。结果是使用file_put_contents()
获得的。
答案 1 :(得分:1)
或者您可以执行以下操作:
ViewResolver
Warning: No mapping found in DispatcherServlet with name 'dispatcher'
提供的信息不如library(data.table)
n <- 1e6
salesdt <- data.table(company = sample(company, n, TRUE),
item = sample(item, n, TRUE),
sales = sample(sales, n, TRUE))
ptm <- proc.time()
sort(salesdt[, unique(company)])
proc.time() - ptm
ptm <- proc.time()
sort(unique(salesdt$company))
proc.time() - ptm
ptm <- proc.time()
salesdt[order(company), unique(company)]
proc.time() - ptm
详尽,但更简单。
以上输出为:
proc.time
用户时间与代码执行有关,CPU的系统时间和经过的时间是启动秒表后的差异(如果代码完全运行,则等于用户和系统时间的总和)。 (取自http://www.ats.ucla.edu/stat/r/faq/timing_code.htm)
答案 2 :(得分:0)
使用 funique
中的 kit
可能是一种获得排序的唯一值向量的快速方法。
还应该考虑使用了多少胎面。在 data.table 中使用一个核心:
setDTthreads(1)
bench::mark(
x[,logical(1), keyby = company]$company,
sort(unique(x$company)),
sort(funique(x$company))
)
# expression min median `itr/sec` mem_alloc
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt>
#1 x[, logical(1), keyby = company]$company 125.8ms 126.2ms 7.90 51.21MB
#2 sort(unique(x$company)) 146.6ms 146.6ms 6.82 166.17MB
#3 sort(funique(x$company)) 17.3ms 17.6ms 56.7 2.32KB
在 data.table 中使用四个核心:
setDTthreads(4)
bench::mark(
x[,logical(1), keyby = company]$company,
sort(unique(x$company)),
sort(funique(x$company))
)
# expression min median `itr/sec` mem_alloc
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt>
#1 x[, logical(1), keyby = company]$company 50.4ms 51.3ms 19.5 49.6MB
#2 sort(unique(x$company)) 145.9ms 145.9ms 6.85 166.1MB
#3 sort(funique(x$company)) 17.3ms 17.5ms 56.9 0B
当使用system.time
时,每个核心使用的时间相加:
setDTthreads(1)
system.time(x[,logical(1), keyby = company]$company)
# User System verstrichen
# 0.122 0.004 0.126
setDTthreads(4)
system.time(x[,logical(1), keyby = company]$company)
# User System verstrichen
# 0.150 0.028 0.052
system.time(funique(x$company))
# User System verstrichen
# 0.018 0.000 0.018
在使用密钥时,还应考虑创建密钥的时间:
system.time(setkeyv(x, "company"))
# User System verstrichen
# 0.241 0.012 0.253
看起来 kit::funique
目前是最快的。使用一个线程 base::unique
比使用 data.table
慢一点。
数据和图书馆:
set.seed(42)
n <- 1e7
company <- c("A", "S", "W", "L", "T", "T", "W", "A", "T", "W")
item <- c("Thingy", "Thingy", "Widget", "Thingy", "Grommit",
"Thingy", "Grommit", "Thingy", "Widget", "Thingy")
sales <- c(120, 140, 160, 180, 200, 120, 140, 160, 180, 200)
library(data.table)
x <- data.table(company = sample(company, n, TRUE),
item = sample(item, n, TRUE),
sales = sample(sales, n, TRUE))
library(kit)