Как отфильтровать мою data.table по условию и по группе? - PullRequest
2 голосов
/ 07 ноября 2019

Проблема

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

Решение

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

Я нашел этот умный способ сделать это с помощью data.table.

DATA[j  = .N, 
     by = .(ID, SEX)][i = base::order(-N), 
     j = .(SEX = SEX[1L]), 
     keyby = ID]

Проблема в том, что когда у пациента несколько режимов, он просто сохраняет один. Таким образом, пациент, который на 50% является мужчиной и 50% женщин, будет считаться мужчиной, что в итоге приведет к смещению. Я хотел бы закодировать их как NA.

Единственный способ исправить это, которое я основал, это использовать dplyr

DATA[j  = .N, 
     by = .(ID, SEX)] %>% 
     group_by(ID) %>% 
     filter(N == max(N))

, а затем заменить значение SEX на NA при дублировании. Но это занимает намного больше времени, чем data.table, он не очень оптимизирован, и у меня есть большой набор данных с большим количеством переменных, которые также необходимо исправить.

Resume

Как взять режим переменной у пациента и заменить его на NA, если он не уникален?

Пример

ID <- c(rep(x = "1", 6), rep(x = "2", 6))
SEX <- c("M","M","M","M","F","M","M","F","M","F","F","M")

require(data.table)
DATA <- data.table(ID, SEX)

# First method (doesn't work)
DATA[j  = .N, 
     by = .(ID, SEX)][i = base::order(-N), 
     j = .(SEX = SEX[1L]), 
     keyby = ID]

# Second method (work with dplyr)
require(dplyr)
DATA[j  = .N, 
     by = .(ID, SEX)] %>% 
     group_by(ID) %>% 
     filter(N == max(N)) %>%
     mutate(SEX = if_else(condition = duplicated(ID) == TRUE,
                          true = "NA",
                          false = SEX)) %>%
     filter(row_number() == n())

# Applied to my data it took 84.288 seconds

Обновление

Решение, предложенное @Cole basedпо идее @Sindri_baldur:

DATA <- data.table(
 ID = c(rep(x = "1", 6), rep(x = "2", 6)),
 SEX = c("M","M","M","M","F","M","M","F","M","F","F",NA),
 V1 = c("a", NA, "a", "a", "b", "a", "b", "b", "b", "c", "b", "c")
)

our_mode_fac <- function(x) {
  freq <- tabulate(x)
       if (length(freq) == 0 || sum(freq == max(freq)) > 1 ) {NA}
       else {levels(x)[which.max(freq)]}
  }

vars <- c("SEX", "V1")

DATA[j = paste0(vars) := lapply(.SD, as.factor), 
     .SDcols = vars][j = vars := lapply(.SD, our_mode_fac),
                     .SDcols = vars, 
                     by = ID]

Работает отлично. Он взял режим, даже когда имеется больше NA, чем факторов, и заменил значения на NA, когда существует более 1 режима.

Теперь он также очень быстрый: 11 секунд для наблюдений 3M + и пациентов 1M + (117секунд с ответом @Sindri_baldur). Большое спасибо вам обоим, я очень благодарен!

1 Ответ

2 голосов
/ 07 ноября 2019
our_mode <- function(x) {
  freq <- table(x)
  if (length(freq) == 0 || sum(freq == max(freq)) > 1 ) {
    NA
  } else {
    names(freq)[which.max(freq)]
  }
}

vars <- c("SEX", "V1")
DATA[, paste0(vars, "_corrected") := lapply(.SD, our_mode), .SDcols = vars, by = ID]

    ID  SEX   V1 SEX_corrected V1_corrected
 1:  1    M    a             M            a
 2:  1    M <NA>             M            a
 3:  1    M    a             M            a
 4:  1    M    a             M            a
 5:  1    F    b             M            a
 6:  1    M    a             M            a
 7:  2    M    b             F            b
 8:  2    F    b             F            b
 9:  2    M    b             F            b
10:  2    F    c             F            b
11:  2    F    b             F            b
12:  2 <NA>    c             F            b

Воспроизводимые данные

DATA <- data.table(
 ID = c(rep(x = "1", 6), rep(x = "2", 6)),
 SEX = c("M","M","M","M","F","M","M","F","M","F","F",NA),
 V1 = c("a", NA, "a", "a", "b", "a", "b", "b", "b", "c", "b", "c")
)

Обратите внимание, что our_mode() не оптимизирован для скорости. См. Предложения Коул для улучшения скорости в комментариях.

...