data.table: Создать новый символьный столбец на основе значений и имен столбцов индикатора - PullRequest
4 голосов
/ 04 апреля 2020

У меня есть data.table с записями 1.6x10 ^ 8, и я хочу создать новый символьный столбец на основе имен столбцов индикатора, для которых есть значение 1.

Например,

library(data.table)
DT <- data.table::data.table(ID=c("a","a","a","b","b"),
                             drugA=c(1,1,1,0,0),
                             drugB=c(0,1,1,1,0),
                             drugC=c(0,0,1,0,1))

   ID drugA drugB drugC
1:  a     1     0     0
2:  a     1     1     0
3:  a     1     1     1
4:  b     0     1     0
5:  b     0     0     1

### NOTE: I know the paste0(...,collapse) argument might be helpful in concatenating the drug names as an intermediate step
   ID drugA drugB drugC          exposure
1:  a     1     0     0             drugA
2:  a     1     1     0       drugA+drugB
3:  a     1     1     1 drugA+drugB+drugC
4:  b     0     1     0             drugB
5:  b     0     0     1             drugC

Я хочу, чтобы это было как можно более надежным и основывалось исключительно на синтаксисе data.table и / или других полезных пакетах / функциях (например, dcast); Я хочу отказаться от создания обширной пользовательской функции, потому что, учитывая размер моего data.table, запуск займет очень много времени.

Я просматривал другие сообщения, но не смог найти что-то похожее в моей ситуации и желаемого результата.

Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

3 голосов
/ 04 апреля 2020
library('data.table')
DT[, id := .I]
df <- melt(DT, id.vars = 'id', measure.vars = c("drugA", "drugB", "drugC"))
df[value == 1, expose := 'exposure']
df[value == 0, expose := 'no_exposure'][, value := NULL]
df <- dcast(df, id ~ expose, fun.aggregate = function(x) paste0(x, collapse = "+"), value.var = 'variable')
DT[df, on = 'id'][, id := NULL][]
#    ID drugA drugB drugC          exposure no_exposure
# 1:  a     1     0     0             drugA drugB+drugC
# 2:  a     1     1     0       drugA+drugB       drugC
# 3:  a     1     1     1 drugA+drugB+drugC            
# 4:  b     0     1     0             drugB drugA+drugC
# 5:  b     0     0     1             drugC drugA+drugB
3 голосов
/ 04 апреля 2020

Мы можем сделать группировку по последовательности строк, указать .SDcols в качестве столбца 'drug', преобразовать Подмножество Data.table (.SD) в logical, использовать его для подстановки имен столбцов и paste их вместе

library(data.table)
DT[,  exposure := paste(names(.SD)[as.logical(.SD)], collapse= '+'), 
       1:nrow(DT), .SDcols = drugA:drugC]
DT
#   ID drugA drugB drugC          exposure
#1:  a     1     0     0             drugA
#2:  a     1     1     0       drugA+drugB
#3:  a     1     1     1 drugA+drugB+drugC
#4:  b     0     1     0             drugB
#5:  b     0     0     1             drugC

Или вместо группировки по строкам мы можем l oop по столбцам, изменить значения на имена столбцов, а затем paste с помощью do.call и удалить элементы NA с помощью gsub

DT[, exposure := gsub("NA\\+|\\+NA", "", do.call(paste, 
   c(Map(function(x, y) names(.SD)[(NA^!x) * y], .SD, 
   seq_along(.SD)), sep="+"))), .SDcols = drugA:drugC]
...