为数据框的每一行查找元素的列索引

Find the column index of an element for each row of a data frame

我有一个很大的数据框(~4.5m 行),每一行对应一个单独的入院。

在每次入院中,#7 到#26 列最多有 20 个诊断代码。此外,我将一个字段指定为 "main diagnosis"。我假设 "main diagnosis" 对应于 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 个诊断代码中的哪个与 "main" 匹配。

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)
    ))))

我想知道是否有更好的方法在不使用循环的情况下执行此操作,因为这确实非常慢。

提前谢谢你。

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

return 实际列名比依赖匹配位置等于诊断编号更安全。例如:

# 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

这会逐行比较三列与 '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'] )  )

因为你有很多行,使用 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

有 20 个诊断和 450 万患者,使用简单的列循环并搜索匹配项可能更有效:

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

如果不止一个诊断与主要匹配,您可能需要调整 return 第一个而不是最后一个(如上所述)诊断。也许,根据找到匹配项的时间减少每次迭代中检查的行数可能会更有效。

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")