r - 有效计算列表的出现次数
问题描述
我有一个包含几百万个列表的列表,这些子列表有几个不同的可能值,可能是 10 到 100。
我想计算这些值的出现次数。
下面的代码有效,但速度很慢。我们可以更快地做到这一点吗?
count_by_list <- function(lst, var_nm = as.character(substitute(lst)), count_nm = "n"){
unique_lst <- unique(lst)
res <- tibble::tibble(!!var_nm := unique_lst, !!count_nm := NA)
for(i in seq_along(unique_lst)){
res[[count_nm]][[i]] <- sum(lst %in% res[[var_nm]][i])
}
res
}
x <- list(
list(a=1, b=2),
list(a=1, b=2),
list(b=3),
list(b=3, c=4))
count_by_list(x)
#> # A tibble: 3 x 2
#> x n
#> <list> <int>
#> 1 <named list [2]> 2
#> 2 <named list [1]> 1
#> 3 <named list [2]> 1
由reprex 包(v0.3.0)于 2019-11-29 创建
我尝试使用库进行散列,digest
但它实际上更慢,并且随着 n 的增加变得更糟:
library(digest)
count_by_list2 <- function(lst, var_nm = as.character(substitute(lst)), count_nm = "n"){
unique_lst <- unique(lst)
digested <- vapply(lst, digest, character(1))
res <- as.data.frame(table(digested))
names(res) <- c(var_nm, count_nm)
res[[1]] <- unique_lst
res
}
如果您需要进行基准测试,您可以使用x_big <- unlist(replicate(10000 ,x, F), recursive = FALSE)
.
我添加了标签rcpp
,parallel processing
因为这些可能会有所帮助,这些并不是对答案的限制。
解决方案
此data.table方法比示例中的 OP 原始循环快 30 倍x_big
。一个值得注意的预防措施是,如果子列表的任何元素包含多个记录,则此方法将失败。
library(data.table)
molten_lst <- rbindlist(x, fill = T)
cnt_lst <- molten_lst[, .N, names(molten_lst)]
tibble(x = cnt_lst[,
list(apply(.SD, 1, function(x) as.list(na.omit(x)))),
.SDcols = names(molten_lst),
by = .(seq_len(nrow(cnt_lst)))]$V1,
n = cnt_lst[['N']])
这里有两种备份方法。我遇到了 NSE / quasi-quotation 问题,所以!!var_nam
被简化了。第一种方法是对您的原始功能进行一些调整 - 主要是通过lst
在循环期间过滤。
enhanced_loop <- function(lst, var_nm = as.character(substitute(lst)), count_nm = "n"){
unique_lst <- unique(lst)
cnts <- vector('integer', length(unique_lst))
for (i in seq_along(unique_lst)[-length(unique_lst)]){
ind <- lst %in% unique_lst[i]
lst <- lst[!ind]
cnts[i] <- sum(ind)
}
cnts[length(unique_lst)] <- length(lst)
tibble::tibble(x := unique_lst, !!count_nm := cnts)
}
这使循环得出合乎逻辑的结论——使用match()
而不是%in%
这样的努力不会重复:
tabulate_match <- function(lst, var_nm = as.character(substitute(lst)), count_nm = "n"){
unique_lst <- unique(lst)
cnts <- tabulate(match(lst, unique_lst))
tibble::tibble(x := unique_lst, !!count_nm := cnts)
}
表现:
# A tibble: 7 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr
<bch:expr> <bch> <bch:t> <dbl> <bch:byt> <dbl> <int>
1 molten_dt 25ms 25.1ms 39.7 2.71MB 0 5
2 tabulate_match(x_big) 237ms 247.2ms 3.41 1.42MB 2.05 5
3 enhanced_loop(x_big) 344ms 352.6ms 2.82 2.83MB 1.69 5
4 table_sapply 381ms 384.9ms 2.59 3.76MB 7.77 5
5 vapply_tab_match(x_big) 412ms 429.3ms 2.14 4.21MB 3.85 5
6 dt_thing(x_big) 442ms 464.6ms 2.15 2.83MB 7.31 5
7 count_by_list(x_big) 759ms 768.4ms 1.24 3.4MB 2.23 5
推荐阅读
- r - nTrials 必须更大......关于联合设计的问题
- sql - SQL左连接相同的列和表
- python - tkinter 将文本从标签发送到输入框
- android - 膨胀类 ImageButton 时出错(Android KitKat)
- r - 如何用R中同一行中的前一个值替换数据框中的任何NA
- docker - 将 Elasticsearch 日志迁移到不同的集群
- reactjs - 功能组件中渲染之间的数据持久性
- sql-server - SSMS“此版本的 SQL Server 不支持 Windows 登录”
- awk - 将字符串与多个数组连接
- python - 如何在我正在编写的许多大型项目中跟踪和重用我编写的小项目?