Создать новый столбец на основе нескольких условий в нескольких столбцах - PullRequest
1 голос
/ 31 марта 2020

ОБНОВЛЕНО для включения нескольких параметров в новые переменные:

Я работаю с грязным файлом пациента с большими данными (> 40 миллионов строк). Каждый пациент (id) имеет несколько рядов. Каждая строка (приблизительно) представляет одну консультацию с кодом симптома / заболевания (icpc). Я добавляю новый столбец с категориями для пациентов с конкретными c условиями (на основе столбцов icpc и icpc2).

Мой необработанный data.frame (df) выглядит примерно так (это это сфабрикованные данные, id намного длиннее в моем наборе данных, и я пропустил нерелевантные столбцы, которые мне нравится отбрасывать):

    id icpc icpc2 reg.date 
1:  123 D95 F15   19JUN2015 
2:  123 F85       15AUG2016 
3:  332 A01       16MAR2010 
4:  332 A04       20JAN2018
5:  332 K20       20FEB2017
6:  100 B10       01JUN2017 
7:  100 A04       11JAN2008
8:  113 T08       18MAR2018
9:  113 P28       19JAN2017 
10: 113 D95 A01   16JAN2013
11: 113 A04       01MAY2009
12: 551 B12 A01   03APR2011
13: 551 D95       09MAY2015

Скажем, я хочу классифицировать пациентов с D95 и / или A01 как да, в новом столбце с именем «condit» (на основе двух столбцов icpc и icpc2). Работает следующее:

cond1 <- c("D95", "A01")
setDT(df)[, condit := ifelse(any(icpc %in% cond1 | icpc2 %in% cond1), "yes","no"), by=id]
df

Но теперь я хочу классифицировать несколько кодов из icpc и icpc2 в новом столбце condit. Так, например, D95 и / или A01 от icpc или icpc2 как A, A04 и / или T08 как B, B10 как C в condit. ПРИМЕЧАНИЕ: A должен перезаписать B (см. Строки 4, 8 и 11), B должен перезаписать C et c (так как возможно, id может иметь несколько категорий).

Это data.frame (df) Я хочу:

    id icpc icpc2 reg.date  condit
1:  123 D95 F15   19JUN2015 A
2:  123 F85       15AUG2016 A
3:  332 A01       16MAR2010 A
4:  332 A04       20JAN2018 A
5:  332 K20       20FEB2017 A
6:  100 B10       01JUN2017 C
7:  100 A04       11JAN2008 C
8:  113 T08       18MAR2018 A
9:  113 P28       19JAN2017 A
10: 113 D95 A01   16JAN2013 A
11: 113 A04       01MAY2009 A
12: 551 B12 A01   03APR2011 A
13: 551 D90       09MAY2015 A

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

Ответы [ 4 ]

2 голосов
/ 31 марта 2020

Думаю, вам следует написать:

df.cat <- df %>%
  group_by(id) %>%
  mutate(condit = 
           if_else((icpc %in% c("D95", "A01")) | (icpc2 %in% c("D95", "A01")) ~ 'yes', 'no'))

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

1 голос
/ 31 марта 2020

Проверьте, есть ли какой-либо элемент c(icpc, icpc2) в нужных кодах. Обратите внимание, что результатом является простая строка символов yes или no, поэтому мы можем просто использовать if вместо if_else.

DF %>%
  group_by(id) %>%
  mutate(condit = if (any(c(icpc, icpc2) %in% c("D95", "A01"))) "yes" else "no") %>%
  ungroup

Второй подход заключается в преобразовании данных в длинную форму в В этом случае будет только один столбец icp c (называемый value), установите condit и затем преобразуйте обратно в широкую форму (или вы можете просто оставить его в длинной форме). select в конце приводит к изменению порядка столбцов в том же порядке, что и для ввода, т. Е. Сначала столбец id, второй столбец icp *, et c.

library(tidyr)
DF %>%
  pivot_longer(starts_with("icpc")) %>%
  filter(name != "") %>%
  group_by(id) %>%
  mutate(condit = if (any(value %in% c("D95", "A01"))) "yes" else "no") %>%
  pivot_wider %>%
  select(names(DF))

Примечание

Вход в воспроизводимом виде предполагается:

Lines <- "id icpc icpc2 reg.date 
123 D95 F15   19JUN2015 
123 F85       15AUG2016 
332 A01       16MAR2010 
332 A04       20JAN2018
332 K20       20FEB2017
100 B10       01JUN2017 
100 A04       11JAN2008
113 T08       18MAR2018
113 P28       19JAN2017 
113 D95 A01   16JAN2013
113 A04       01MAY2009
551 B12 A01   03APR2011
551 D95       09MAY2015"
L <- readLines(textConnection(Lines))
L <- sub(" (\\S+) ", ",\\1,", L)
L <- sub(" +", ",", L)
DF <- read.csv(text = L, check.names = FALSE, as.is = TRUE, strip.white = TRUE)
1 голос
/ 31 марта 2020

Вам нужно использовать any, как вы уже обнаружили, так как вы хотите присвоить значения целым группам.

library(dplyr)
df %>%
  group_by(id) %>%
  mutate(condit = if(any(icpc %in% c("D95", "A01") | icpc2 %in% c("D95", "A01"))) 
                     "yes" else "no")

Или более быстрый вариант без if / else будет

df %>%
   group_by(id) %>%
   mutate(condit = c("no", "yes")[(any(icpc %in% c("D95", "A01") | 
                                        icpc2 %in% c("D95", "A01"))) + 1]) 
1 голос
/ 31 марта 2020

При большом наборе данных (> 40 миллионов строк) пакет data.table может быть хорошим выбором:

library(data.table)

cond1 <- c("D95", "A01")
setDT(df)[, condit := ifelse(any(icpc %in% cond1 | icpc2 %in% cond1), "yes","no"), by=id]
df

     id icpc icpc2  reg.date condit
 1: 123  D95   F15 19JUN2015    yes
 2: 123  F85       15AUG2016    yes
 3: 332  A01       16MAR2010    yes
 4: 332  A04       20JAN2018    yes
 5: 332  K20       20FEB2017    yes
 6: 100  B10       01JUN2017     no
 7: 100  A04       11JAN2008     no
 8: 113  T08       18MAR2018    yes
 9: 113  P28       19JAN2017    yes
10: 113  D95   A01 16JAN2013    yes
11: 113  A04       01MAY2009    yes
12: 551  B12   A01 03APR2011    yes
13: 551  D95       09MAY2015    yes

Данные :

df <- structure(list(id = c(123L, 123L, 332L, 332L, 332L, 100L, 100L, 
113L, 113L, 113L, 113L, 551L, 551L), icpc = c("D95", "F85", "A01", 
"A04", "K20", "B10", "A04", "T08", "P28", "D95", "A04", "B12", 
"D95"), icpc2 = c("F15", "", "", "", "", "", "", "", "", "A01", 
"", "A01", ""), reg.date = c("19JUN2015", "15AUG2016", "16MAR2010", 
"20JAN2018", "20FEB2017", "01JUN2017", "11JAN2008", "18MAR2018", 
"19JAN2017", "16JAN2013", "01MAY2009", "03APR2011", "09MAY2015"
)), class = "data.frame", row.names = c(NA, -13L))

Редактировать : для нескольких условий:

cond1 <- c("D95", "A01") # A
cond2 <- c("A04", "T08") # B
cond3 <- "B10"           # C

setDT(df)[, condit := if(any(icpc %in% cond1 | icpc2 %in% cond1)) "A" else 
                         if(any(icpc %in% cond2 | icpc2 %in% cond2)) "B" else
                            if(any(icpc %in% cond3 | icpc2 %in% cond3)) "C" else "", by=id]

     id icpc icpc2  reg.date condit
 1: 123  D95   F15 19JUN2015      A
 2: 123  F85       15AUG2016      A
 3: 332  A01       16MAR2010      A
 4: 332  A04       20JAN2018      A
 5: 332  K20       20FEB2017      A
 6: 100  B10       01JUN2017      B
 7: 100  A04       11JAN2008      B
 8: 113  T08       18MAR2018      A
 9: 113  P28       19JAN2017      A
10: 113  D95   A01 16JAN2013      A
11: 113  A04       01MAY2009      A
12: 551  B12   B10 03APR2011      C
13: 551  D96       09MAY2015      C

Данные : (слегка изменено по сравнению с оригиналом, так как не найдено условие "C".

df <- structure(list(id = c(123L, 123L, 332L, 332L, 332L, 100L, 100L, 
113L, 113L, 113L, 113L, 551L, 551L), icpc = c("D95", "F85", "A01", 
"A04", "K20", "B10", "A04", "T08", "P28", "D95", "A04", "B12", 
"D96"), icpc2 = c("F15", "", "", "", "", "", "", "", "", "A01", 
"", "B10", ""), reg.date = c("19JUN2015", "15AUG2016", "16MAR2010", 
"20JAN2018", "20FEB2017", "01JUN2017", "11JAN2008", "18MAR2018", 
"19JAN2017", "16JAN2013", "01MAY2009", "03APR2011", "09MAY2015"
)), class = "data.frame", row.names = c(NA, -13L))

Проверено на фрейме данных с 40M строками: system.time (...)

#    user  system elapsed 
#  111.11    1.17  111.97 

Использование dplyr:

# Error: cannot allocate vector of size 274.7 Mb
# Timing stopped at: 4.19 1.11 5.39
...