2016-03-04 2 views
2

У меня есть большой фрейм данных (строки ~ 4,5 м), каждая строка соответствует отдельному поступлению в больницу.Найти индекс столбца элемента для каждой строки фрейма данных

В пределах допуска допускаются до 20 кодов диагностики в столбцах № 7 до № 26. Кроме того, у меня есть поле, назначенное как «основной диагноз». Это было мое предположение, что «главный диагноз» соответствовал первому из 20 диагностических кодов. Это неверно - иногда это 1-й, другие 2-й, 3-й и т. Д. Меня интересует это распределение.

ID  MainDiagCode Diag_1 Diag_2 Diag_3 ... 
Patient1 J123   J123 R343 S753 
Patient2 G456   F119 E159 G456 
Patient3 T789   L292 T789 W474 

Я хотел бы добавить столбец к моей кадр данных, который говорит мне, какие из 20 диагностических кодов соответствует к «основной» один.

ID  MainDiagCode Diag_1 Diag_2 Diag_3 ... NewColumn 
Patient1 J123   J123 R343 S753  1 
Patient2 G456   F119 E159 G456  3 
Patient3 T789   L292 T789 W474  2 

Я был в состоянии получить ход цикла:

df$NewColumn[i] <- 
    unname(which(apply(df[i, 7:26], 2, function(x) 
    any(
     grepl(df$MainDiagCode[i], x) 
    )))) 

мне интересно, если есть лучший способ сделать это без использования цикла, так как это очень медленно, действительно.

Заранее спасибо.

ответ

3
df$NewColumn = apply(df, 1, function(x) match(x["MainDiagCode"], x[-c(1,2)])) 

df 

     ID MainDiagCode Diag_1 Diag_2 Diag_3 NewColumn 
1 Patient1   J123 J123 R343 S753   1 
2 Patient2   G456 F119 E159 G456   3 
3 Patient3   T789 L292 T789 W474   2 

Безопаснее вернуть фактическое имя столбца, а не полагаться на позицию соответствия, чтобы он был равен номеру диагноза. Например:

# Get the names of the diagnosis columns 
diag.cols = names(df)[grep("^Diag", names(df))] 

Извлечение имя столбца согласованного столбца:

apply(df, 1, function(x) { 
     names(df[,diag.cols])[match(x["MainDiagCode"], x[diag.cols])] 
}) 
[1] "Diag_1" "Diag_3" "Diag_2" 

извлечь число в конце имени согласованного столбца:

library(stringr) 

apply(df, 1, function(x) { 
    as.numeric(
    str_extract(
     names(df[,diag.cols])[match(x["MainDiagCode"], x[diag.cols])], "[0-9]{1,2}$") 
    ) 
    }) 

[1] 1 3 2 
+0

Это огромная помощь, спасибо! Чтобы уточнить выражение «x [-c (1,2)]» после «match [...]» ограничивает, где «match [...]» работает в кадре данных? В этой ситуации сопоставление не вычисляется по столбцам 1 и 2, вместо этого выполняется только на оставшихся столбцах кадра данных? – tfmunkey

+0

Правильно. Для любого фрейма данных 'df [, -c (1,2)]' удаляет первые два столбца. Аналогично, для вектора 'x [-c (1,2)]' исключаются первые два элемента.Мы используем векторную нотацию с 'apply', потому что она работает по каждой строке отдельно, поэтому каждая строка является по существу вектором данных. В вашем фактическом производственном коде будет безопаснее исключать столбцы на основе их имен, а не их индексные позиции. – eipi10

2

Это делает row- сравнение по строкам трех столбцов с «MainDiagCode»:

apply(dat[-1], 1, function(x) which(x[-1] == x['MainDiagCode']) ) 
[1] 1 3 2 

Итак:

dat$NewColumn <- apply(dat[-1], 1, function(x) which(x[-1] == x['MainDiagCode']) ) 
0

Как много строк, используя data.table может улучшить производительность

library(data.table) 
DT <- data.table(PatientID = paste0("Patient", 1:3), 
       MainDiagCode = c("J123", "G456", "T789"), 
       Diag_1 = c("J123", "F119", "L292"), 
       Diag_2 = c("R343", "E159", "T789"), 
       Diag_3 = c("S753", "G456", "W474") 
) 

DT[, NewColumn := match(MainDiagCode, .SD[, -1, with = F]), by = PatientID] 
DT 
#> PatientID MainDiagCode Diag_1 Diag_2 Diag_3 NewColumn 
#> 1: Patient1   J123 J123 R343 S753   1 
#> 2: Patient2   G456 F119 E159 G456   3 
#> 3: Patient3   T789 L292 T789 W474   2 
+0

Я проверил это с помощью «microbenchmark», и он прошел с 1/20 скоростью решения apply/match. Это меня действительно удивило, так как я ожидаю, что data.table будет намного быстрее, чем база R. – eipi10

+0

Ok. Возможно, я что-то пропустил – cderv

3

С 20 диагнозов и 4,5 млн пациентов может быть более эффективно использовать простой цикл по колонкам и поиск совпадений:

ff = function(main, diags) 
{ 
    ans = rep_len(NA_integer_, length(main)) 
    for(i in seq_along(diags)) ans[main == diags[[i]]] = i  
    return(ans) 
} 
ff(as.character(dat$MainDiagCode), lapply(dat[-(1:2)], as.character)) 
#[1] 1 3 2 

Если более одного диагноза совпадает с основным, вам могут потребоваться корректировки для возврата первого а не последний (как указано выше) диагноз. Возможно, было бы еще эффективнее сократить количество строк, проверенных на каждой итерации, в зависимости от того, когда найдено совпадение.

dat = structure(list(PatientID = structure(1:3, .Label = c("Patient1", 
"Patient2", "Patient3"), class = "factor"), MainDiagCode = structure(c(2L, 
1L, 3L), .Label = c("G456", "J123", "T789"), class = "factor"), 
    Diag_1 = structure(c(2L, 1L, 3L), .Label = c("F119", "J123", 
    "L292"), class = "factor"), Diag_2 = structure(c(2L, 1L, 
    3L), .Label = c("E159", "R343", "T789"), class = "factor"), 
    Diag_3 = structure(c(2L, 1L, 3L), .Label = c("G456", "S753", 
    "W474"), class = "factor")), .Names = c("PatientID", "MainDiagCode", 
"Diag_1", "Diag_2", "Diag_3"), row.names = c(NA, -3L), class = "data.frame") 
+0

На моей машине это работает на 45% быстрее, чем решение apply/match. – eipi10

Смежные вопросы