назначить новый столбец, используя переменную в data.table

#r #data.table

#r #data.table

Вопрос:

У меня есть data.table, для которого я хотел бы выполнить некоторую обработку. В качестве начального шага я хотел бы установить новый data.table для столбцов.
Я создаю цикл для интересующих столбцов и пытаюсь назначить NA /0, который завершается неудачей или имеет проблемы, как описано ниже.

 library(data.table)    
 
input_allele <- data.table(FID= paste0("gid",1:10),IID=paste0("IID",11:20),PAT=c(1:10),MAT=c(rep(0,10)),SEX=c(rep(1,10)),PHENOTYPE =c(rep(1,10)),
SNP1=(c(rep(1,5), rep(0,5))),SNP2=(c(rep(1,6),rep(0,3),NA)),SNP3=(c(rep(NA,6),rep(1,4))),SNP4=(c(rep(NA,6),rep(0,4))),SNP5=(c(rep(1,6),rep(0,4)))  )


multiplied_value<-input_allele[,c(1:6)]

for(temp_snp in (colnames(input_allele[,.SD,.SDcols=c(7:11)]))){
temp_snpquote<-quote(temp_snp)
multiplied_value[,(temp_snpquote):=0]
}
 

Я получаю сообщение об ошибке:

Ошибка в [.data.table (multiplied_value, , := ((temp_snpquote), 0)) : LHS of := должно быть символом или атомарным вектором (имена столбцов или позиции).

Если я использую eval , я сталкиваюсь со странным поведением: после завершения цикла мне приходится вводить multiplied_value дважды, прежде чем data.table будет напечатан на консоли. Это поразительно для меня.

 for(temp_snp in (colnames(input_allele[,.SD,.SDcols=c(7:11)]))){

temp_snpquote<-quote(temp_snp)
multiplied_value[,eval(temp_snpquote):=0]
}
 

Я хотел бы понять: 1) как установить новый столбец как NA или 0. 2) почему при использовании eval я вводил multiplied_value data.table дважды, когда он печатается.

R version 4.0.0 (2020-04-24), data.table_1.13.4 Дистрибутив Unix debian

Комментарии:

1. Я бы использовал set для этого, а не := . Что-то вроде: for (i in colnames(input_allele[,.SD,.SDcols=c(7:11)])) set(multiplied_value, j = i, value = 0); multiplied_value[] .

2. Но вы также могли бы сделать: for(temp_snp in (colnames(input_allele[,.SD,.SDcols=c(7:11)]))) multiplied_value[, (temp_snp):= 0]; multiplied_value[] .

3. Я понимаю. Я должен был использовать [] , где я должен ввести имя переменной дважды. for(temp_snp in (colnames(input_allele[,.SD,.SDcols=c(7:11)]))) multiplied_value[, (temp_snp):= 0][] Я не смог понять ваш первый фрагмент кода из-за переменных (j и i). Как то, что там установлено?

4. Если вы посмотрите на страницу справки ?set (где := также показано), ближе к концу вы увидите тайминги для разных способов добавления нескольких столбцов в a data.table .

5. Последнее [] — печатать после использования любых изменений на месте.

Ответ №1:

Консолидация некоторых комментариев в ответ здесь…

Из ?set , вы можете обнаружить, что накладные [.data.table расходы на повторный вызов могут складываться. В этих случаях вы можете попробовать set вместо этого.

Кроме того, любые set* функции должны сопровождаться [] выводом на печать.

При этом вот две альтернативы:

 copy1 <- copy2 <- copy3 <- input_allele[,c(1:6)]
new <- colnames(input_allele[,.SD,.SDcols=c(7:11)])

## Using `set` :

for (i in new) {
  set(copy1, j = i, value = 0)[]
}
head(copy1)
##     FID   IID PAT MAT SEX PHENOTYPE SNP1 SNP2 SNP3 SNP4 SNP5
## 1: gid1 IID11   1   0   1         1    0    0    0    0    0
## 2: gid2 IID12   2   0   1         1    0    0    0    0    0
## 3: gid3 IID13   3   0   1         1    0    0    0    0    0
## 4: gid4 IID14   4   0   1         1    0    0    0    0    0
## 5: gid5 IID15   5   0   1         1    0    0    0    0    0
## 6: gid6 IID16   6   0   1         1    0    0    0    0    0
   
## Using `:=` :

for (i in new) {
  copy2[, (i) := 0][]
}
head(copy2)
##     FID   IID PAT MAT SEX PHENOTYPE SNP1 SNP2 SNP3 SNP4 SNP5
## 1: gid1 IID11   1   0   1         1    0    0    0    0    0
## 2: gid2 IID12   2   0   1         1    0    0    0    0    0
## 3: gid3 IID13   3   0   1         1    0    0    0    0    0
## 4: gid4 IID14   4   0   1         1    0    0    0    0    0
## 5: gid5 IID15   5   0   1         1    0    0    0    0    0
## 6: gid6 IID16   6   0   1         1    0    0    0    0    0
 

Вы также можете избежать цикла:

 copy3[, (new) := as.list(rep(0, length(new)))][]
##       FID   IID PAT MAT SEX PHENOTYPE SNP1 SNP2 SNP3 SNP4 SNP5
##  1:  gid1 IID11   1   0   1         1    0    0    0    0    0
##  2:  gid2 IID12   2   0   1         1    0    0    0    0    0
##  3:  gid3 IID13   3   0   1         1    0    0    0    0    0
##  4:  gid4 IID14   4   0   1         1    0    0    0    0    0
##  5:  gid5 IID15   5   0   1         1    0    0    0    0    0
##  6:  gid6 IID16   6   0   1         1    0    0    0    0    0
##  7:  gid7 IID17   7   0   1         1    0    0    0    0    0
##  8:  gid8 IID18   8   0   1         1    0    0    0    0    0
##  9:  gid9 IID19   9   0   1         1    0    0    0    0    0
## 10: gid10 IID20  10   0   1         1    0    0    0    0    0
 

Обратите внимание, что quote и eval для них не нужны.

Даже при таком небольшом наборе данных разница в производительности между set циклом и использованием := в цикле измерима:

 fun1 <- function() { for (i in new) { set(copy1, j = i, value = 0)[] }; copy1 }
fun2 <- function() { for (i in new) { copy2[, (i) := 0][] } ; copy2 }
fun3 <- function() copy3[, (new) := as.list(rep(0, length(new)))][]

bench::mark(fun1(), fun2(), fun3())
## # A tibble: 3 x 13
##   expression     min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc
##   <bch:expr> <bch:t> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>
## 1 fun1()      64.9µs  69.63µs    13932.        0B     4.17  6689     2
## 2 fun2()       993µs   1.07ms      910.   377.6KB     4.23   430     2
## 3 fun3()     241.9µs 255.12µs     3793.    16.4KB     4.30  1763     2
## # … with 5 more variables: total_time <bch:tm>, result <list>, memory <list>,
## #   time <list>, gc <list>