使用data.table中的函数非常慢

时间:2014-08-08 21:33:37

标签: r optimization data.table

显然我做得不对。这个方法有效,但实际上并不实用。请记住,我正在使用的示例数据运行得很快,但如果您要在400k行+的DT上尝试此操作,那么您将等待一段时间。

我最初是这样做的,这样我就可以用一个名字将日期转换为财政周(周五到周四)。我希望能够查看DT中的每个日期,测试它落入哪个范围,然后返回周名称。它就像一个魅力!但是,就像我说的那样,非常缓慢。

然后我认为这对于我可能需要做的任何逐行调整都是一种很好的技巧,但除非我能以某种方式对其进行优化,否则我可能需要重新思考...

因此,让我们使用mtcars数据集,假设我们想看看每行中的cyl是什么,然后将该数字转换为它的单词。

cyl.word <- function(c) {
  r <- "Huh?"
  if(c==4) {r <- "Four"}
  if(c==6) {r <- "Six"}
  if(c==8) {r <- "Eight"}
  return(r)
}

按照您的预期工作,如果您键入cyl.word(4),则会得到“四”。大。让我们将它应用于DT ......

cars <- data.table(mtcars)

cars[, Word:=cyl.word(cars$cyl[.I]), by=cyl]
head(cars)

    mpg cyl disp  hp drat    wt  qsec vs am gear carb  Word
1: 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4   Six
2: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4   Six
3: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1  Four
4: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1   Six
5: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2 Eight
6: 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1   Six

我认为减速显然是由[.I]引起的,但如果没有它,它似乎抓住整个cyl的向量并且只使用第一个用于函数,它们都返回“六”:

cars[, Word:=cyl.word(cars$cyl), by=cyl]
head(cars)

    mpg cyl disp  hp drat    wt  qsec vs am gear carb Word
1: 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4  Six
2: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4  Six
3: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1  Six
4: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1  Six
5: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2  Six
6: 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1  Six

它还给了我这个错误信息:

Warning messages:
1: In if (c == 4) { :
  the condition has length > 1 and only the first element will be used
2: In if (c == 6) { :
  the condition has length > 1 and only the first element will be used
3: In if (c == 8) { :
  the condition has length > 1 and only the first element will be used

这就是为什么我使用.I变量来告诉函数看哪个迭代......

2 个答案:

答案 0 :(得分:6)

使用向量化函数match()可以大大加快计算速度:

library(data.table)
cars <- data.table(mtcars)

cyl.word <- function(x) {
    c("Four", "Six", "Eight", "Huh?")[match(x, c(4,6,8), nomatch=4)]
}
cars[, Word:=cyl.word(cyl)]

head(cars)
#     mpg cyl disp  hp drat    wt  qsec vs am gear carb  Word
# 1: 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4   Six
# 2: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4   Six
# 3: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1  Four
# 4: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1   Six
# 5: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2 Eight
# 6: 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1   Six

答案 1 :(得分:2)

您也可以加入另一个data.table,这应该非常快。 &#34;咦&#34;将是NA

library(data.table)
cars <- data.table(mtcars)
cars[32]$cyl <- 10
dt2 <- data.table(cyl = c(4,6,8), word = c("Four", "Six", "Eight"))
setkey(cars, cyl)
setkey(dt2, cyl)
cars <- dt2[cars, nomatch = NA]
tail(cars)

#       cyl  word  mpg disp  hp drat    wt  qsec vs am gear carb
#    1:   8 Eight 15.2  304 150 3.15 3.435 17.30  0  0    3    2
#    2:   8 Eight 13.3  350 245 3.73 3.840 15.41  0  0    3    4
#    3:   8 Eight 19.2  400 175 3.08 3.845 17.05  0  0    3    2
#    4:   8 Eight 15.8  351 264 4.22 3.170 14.50  0  1    5    4
#    5:   8 Eight 15.0  301 335 3.54 3.570 14.60  0  1    5    8
#    6:  10    NA 21.4  121 109 4.11 2.780 18.60  1  1    4    2

按键加入matchdata.table非常快。以下是更大数据集的基准。

library(microbenchmark)
N  <- 50000
cars  <- data.table(mtcars[rep(seq_len(nrow(mtcars)), N), ])
dim(cars)
#[1] 1600000     11
microbenchmark(
  MATCH = {cyl.word <- function(x) {
            c("Four", "Six", "Eight", "Huh?")[match(x, c(4,6,8), nomatch=4)]}
            cars[, match_word:=cyl.word(cyl)]}, 
  DTJOIN = {dt2 <- data.table(cyl = c(4,6,8), word = c("Four", "Six", "Eight"))
            setkey(cars, cyl)
            setkey(dt2, cyl)
            new_cars <- dt2[cars, nomatch = NA]})

#Unit: milliseconds
#   expr      min       lq   median       uq      max neval
#  MATCH 36.73572 41.16291 50.89132 63.22235 271.3654   100
#  DTJOIN 29.56963 33.72217 39.51063 61.92716 268.3304   100