r语言 - 在大数据表中提高计算性能的最佳方法是什么?



在单个data.table中,我有许多计算要执行。简单,但结合了许多配置:从Y个其他变量创建X个变量,基于X个不同的变量创建组,等等…

一步一步地,我设法执行我需要的所有计算(利用我在data.table中的知识),但我真正的挑战是所谓的PERFORMANCE。我的data.table包含数百万行,计算是在几十列上进行的。

我想知道的:

  • 是否有更好的方法来编写此代码以提高性能?
  • 我的一些选项不工作(1.3和2.2,标记与KO):好方法?怎么写?
  • 我的microbenchmark似乎告诉我最好的选择取决于行数?对吧?

这是我的代码与一个代表:

library(data.table)
library(stringr)
library(microbenchmark)
n.row <- 1e5
foo <- data.table(id = 101:(101+n.row-1), 
crit = rep(c('fr', 'ca', 'al', 'se', 'is'), 5),
val_1 = round(runif(n.row, 0.5, 50), digits = 2),
val_2 = round(runif(n.row, 1, 20), digits = 0),
val_3 = round(runif(n.row, 1, 5), digits = 0),
eff = 28500,
num = sample(0:1,n.row, replace = TRUE),
num_2 = round(runif(n.row, 1, 10), digits = 1),
num_17 = round(runif(n.row, 1, 10), digits = 1),
num_69 = round(runif(n.row, 0, 1), digits = 2),
num_5 = round(runif(n.row, 10, 20), digits = 0),
cof = round(runif(n.row, 0.1, 2), digits = 5),
ToDo = rep(1, n.row),
grp_1 = sample(LETTERS[c(1,3)], n.row, replace = TRUE))

foo[, c("grp_2", "grp_3") := {
grp_2 = fcase(grp_1 %in% LETTERS[c(1)], sample(LETTERS[c(5,8,9)], n.row, replace = TRUE),
grp_1 %in% LETTERS[c(3)], sample(LETTERS[c(14,16)], n.row, replace = TRUE))

grp_3 = fcase(grp_1 %in% LETTERS[c(1)], sample(LETTERS[c(20:23)], n.row, replace = TRUE),
grp_1 %in% LETTERS[c(3)], sample(LETTERS[c(24:26)], n.row, replace = TRUE))

list(grp_2, grp_3)
}]
# Calcul sd and qa
foo[, sd := (val_1 * cof)]
foo[num == 1, qa := (val_2 * cof)]
foo[num != 1, qa := (val_3 * cof)]
foo1 <- copy(foo)
foo2 <- copy(foo)
foo3 <- copy(foo)
# calcul of qa_X 
var.calc <- names(foo)[str_which(names(foo), "^num.\d+$")]
# 1.1
for (j in var.calc){
foo1[, paste0("qa_", str_extract(j, "\d+$")) := qa * get(j)]
}
# 1.2
setDT(foo2)[, paste0("qa_", str_extract(var.calc, "\d+$")) := lapply(.SD, function(x) x * qa), .SDcols = var.calc ]
# 1.3 KO
for (j in var.calc){ set(foo3, paste0("qa_", str_extract(j, "\d+$")) := qa * get(j)) }
# comparaison
mbm <- microbenchmark(
Test.for = for (j in var.calc){ foo1[, paste0("qa_", str_extract(j, "\d+$")) := qa * get(j)] },
Test.set = setDT(foo2)[, paste0("qa_", str_extract(var.calc, "\d+$")) := lapply(.SD, function(x) x * qa), .SDcols = var.calc ],
times = 10
)
mbm
# calcul by groups
var.grp <- names(foo)[grepl("^grp.\d+$", names(foo))]
# 2.1
for (j in var.grp) {
foo1[, paste0("s.sd.", j) := sum(sd, na.rm = TRUE), by = get(j)]
foo1[, paste0("s.qa.", j) := sum(qa, na.rm = TRUE), by = get(j)]
}
# 2.2 KO 
setDT(foo2)[, paste0("s.sd.", var.grp) := lapply(.SD, function(x) sum(x)), .SDcols = var.calc, by = .SD ]

非常感谢你的帮助和建议。

(如果我必须拆分我的请求,我会的)。

  1. 问题:我会用:

    for (j in var.calc) set(foo3, j = paste0("qa_", str_extract(j, "\d+$")), value = foo3$qa * foo3[[j]])
    

(修复1.3的例子)

  1. 问题:2.1接缝好

指出:

  • 你不需要经常使用setDT(foo2)
  • 读取data.table的文档!有很多有用的例子,等等:https://rdatatable.gitlab.io/data.table/
  • 不要看microbenchmark的,在你的真实数据和时间上尝试代码,因为结果(时间)将是不同的,开销,一些data.table的函数有,将是微不足道的。

最新更新