соображения производительности get () в data.table - PullRequest
2 голосов
/ 08 апреля 2020

Я использовал get () в al oop для манипулирования столбцом j с помощью i со ссылкой на несколько других столбцов.

Интересно, есть ли более быстрый / более эффективный способ? Какие-либо соображения производительности?

Вот минимальный пример типа операции, которую я имею в виду:

require(data.table) # version 1.12.8
dt = data.table(v1=c(1,2,NA),v2=c(0,0,1),v3=c(0,0,0))
for (i in 1:2){
     dt[ is.na(get(paste0('v',i))), (paste0('v',i)):= get(paste0('v',i+1))+2 ][]
}

Реальные таблицы, с которыми я делаю это, намного больше (~ 5 миллионов строк, ~ 300 столбцов).

Буду очень признателен за любые мысли.

Ответы [ 2 ]

3 голосов
/ 08 апреля 2020

Мы можем использовать set, который назначит вместо

library(data.table)
for(j in 1:2) {
    i1 <- which(is.na(dt[[j]]))
    set(dt, i = i1, j = j, value = dt[[j+1]][i1]+ 2)
 }

dt
#   v1 v2 v3
#1:  1  0  0
#2:  2  0  0
#3:  3  1  0

Нет большой разницы между for l oop или lapply, если оба используют get. Для улучшения производительности лучше использовать set


В base R, мы также можем сделать

setDF(dt)
i1 <- is.na(dt[-length(dt)])
dt[-length(dt)][i1] <- dt[-1][i1] + 2
dt
#  v1 v2 v3
#1  1  0  0
#2  2  0  0
#3  3  1  0
2 голосов
/ 08 апреля 2020

Да, ваш for l oop значительно замедляет вас. Даже простой lapply (и, возможно, есть более элегантные способы) приносит вам значительный прирост производительности:

library(data.table)
dt <- data.table(v1 = rnorm(100), v2 = sample(c(NA,1:5)), v3 = sample(c(NA,1:5)), v4 = sample(c(NA,1:5)))
dt2 <- copy(dt)
dt3 <- copy(dt)
dt4 <- copy(dt)

microbenchmark::microbenchmark(
  for (i in 1:2){
    dt[ is.na(get(paste0('v',i))), (paste0('v',i)):= get(paste0('v',i+1))+2 ]
  },
  for (i in 1:2){ 
    dt2[ is.na(get(paste0('v',i))), (paste0('v',i)):= get(paste0('v',i+1))+2 ][]
  },
  lapply(1:2, function(i) dt3[ is.na(get(paste0('v',i))), (paste0('v',i)):= get(paste0('v',i+1))+2 ]),
  for(j in 1:2) {
    i1 <- which(is.na(dt4[[j]]))
    set(dt4, i = i1, j = j, value = dt[[j+1]][i1]+ 2)
  }  
)

Unit: milliseconds
                                                                                                                   expr      min       lq      mean   median
    for (i in 1:2) {     dt[is.na(get(paste0("v", i))), `:=`((paste0("v", i)), get(paste0("v",          i + 1)) + 2)] } 8.439924 8.651308 10.257670 8.900500
 for (i in 1:2) {     dt2[is.na(get(paste0("v", i))), `:=`((paste0("v", i)), get(paste0("v",          i + 1)) + 2)][] } 8.902435 9.098875 10.469305 9.262659
     lapply(1:2, function(i) dt3[is.na(get(paste0("v", i))), `:=`((paste0("v",      i)), get(paste0("v", i + 1)) + 2)]) 1.032788 1.144117  1.561741 1.224858
           for (j in 1:2) {     i1 <- which(is.na(dt4[[j]]))     set(dt4, i = i1, j = j, value = dt[[j + 1]][i1] + 2) } 6.216452 6.392754  7.970074 6.502356
       uq       max neval
 9.588571 35.259060   100
 9.729876 23.245224   100
 1.349337  9.467026   100
 7.046646 30.857044   100

Проверка результатов эквивалентна:

identical(dt,dt2)
# [1] TRUE
identical(dt,dt3)
# [1] TRUE
identical(dt,dt4)
# [1] TRUE

Возможно, более элегантный способ сделать это, но деление на 10 среднего времени вычислений для чего-то, что заняло всего несколько секунд, является хорошим выходом;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...