【问题标题】:What would be the best way to improve calcul performance in a big data.table?在大数据表中提高计算性能的最佳方法是什么?
【发布时间】:2023-03-24 19:46:01
【问题描述】:

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

一步一步地,我设法执行了所有我需要的计算(根据我在 data.table 中的知识),但我真正的挑战是性能。我的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 ]

非常感谢您的帮助或建议。

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

【问题讨论】:

  • 我认为你不应该在 set(...) 中使用 :=
  • 这个问题可能很适合Code Review

标签: r performance data.table


【解决方案1】:
  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's,在你的真实数据和时间上尝试代码,因为结果(时间)会有所不同,而且data.tables 的一些函数所具有的开销将是微不足道。

【讨论】:

    猜你喜欢
    • 2010-09-09
    • 2015-03-09
    • 1970-01-01
    • 2015-11-27
    • 1970-01-01
    • 1970-01-01
    • 2013-06-01
    • 1970-01-01
    • 2019-12-30
    相关资源
    最近更新 更多