根据多个列中的多个条件创建新列

Create new column based on multiple conditions in multiple columns

已更新以在新变量中包含多个选项:

我正在处理一个混乱的大数据患者文件(> 4000 万行)。每个患者 (id) 有几行。每行(大致)代表一次咨询 symptom/disease 代码 (icpc)。我添加了一个新列,其中包含针对具有特定条件的患者的类别(基于 icpcicpc2 列)。

我的原始 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

假设我想在名为 'condit' 的新列(基于两列 icpcicpc2)。以下作品:

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

但是现在我想把icpcicpc2的几个代码归类到新的列condit中。例如,D95 and/or A01 from icpc or icpc2 as A, A04 and/or T08 as BB10condit 中的 C 相同。注意:A 应该覆盖 B(参见第 4、8 和 11 行),B 应该覆盖 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

任何帮助将不胜感激。谢谢!

我觉得你应该写:

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

当可能有多个选择时,您不应使用 ==。顺便说一句,我在你的逻辑比较中添加了括号

对于大型(> 4000 万行)数据集,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))

在具有 4000 万行的数据帧上进行测试: 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

你需要使用 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]) 

检查 c(icpc, icpc2) 的任何元素是否在所需代码中。请注意,结果是一个简单的字符串 yesno 因此我们可以只使用 if 而不是 if_else.

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

第二种方法是将数据转换为长格式,在这种情况下只有一个 icpc 列(称为 value),设置 condit,然后转换回宽格式(或您可能只想以长格式保留它)。最后的 select 将列重新排序为与输入相同的顺序,即首先是 id 列,然后是 icpc 列,依此类推。

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)