【问题标题】:R: create single new column based upon matching string in multiple other columnsR:根据多个其他列中的匹配字符串创建单个新列
【发布时间】:2019-06-13 21:08:24
【问题描述】:

我有一个数据集,其中包含大量表示程序代码的字符串列变量。还有另一列变量表示编码格式(有些是 ICD9,有些是其他更神秘的格式)。每个观察都是一个病人。我需要:

  1. 使用特定前缀搜索每个变量名称
  2. 确保使用的代码是 ICD9 代码(用“02”表示)。
  3. 找出哪些代码与特定字符串的前 3 个字符匹配
  4. 如果任何变量以这三个字符开头,则创建一个新列变量 =1,如果不匹配则 =0

变量太多了,通过 c("cd1", "cd2", ...) 手动创建每个变量的列表是微不足道的,而且我可能需要多次执行此操作,所以我'想让它成为一个尽可能通用的解决方案。

这是一个简化的示例,我需要搜索的字符串以“231”开头:

ID   cd1    type1  cd2    type2  cd3    type3  cd4    type4
1    "231"  "02"   "219"  "02"   "1321" "02"  "2314"  "02"
2    "222"  "02"  
3    "123"  "142"   
4    "145"  "02"   "521"  "02"   "2313" "02"   
5    "231"  "01"

我想要的输出是:

ID   cd1    type1  cd2    type2  cd3    type3  cd4    type4   flag_var
1    "231"  "02"   "219"  "02"   "1321" "02"  "2314"  "02"    1
2    "222"  "02"                                              0
3    "123"  "142"                                             0
4    "145"  "02"   "521"  "02"   "2313" "02"                  1
5    "231"  "01"                                              0

(ID #5 设置为 0,因为即使 cd1 代码是“231”,type1 变量也是“01”,因此编码格式不正确”)

我在使用 mutate 和 case_when 实现这一点方面取得了一定的成功:

df <- df %>%
  mutate(flag_var = case_when(
    startsWith(cd1, "231") ~ 1, 
    startsWith(cd2, "231") ~ 1, 
    startsWith(cd3, "231") ~ 1, 
    startsWith(cd4, "231") ~ 1, 
    TRUE ~ 0))

就像我说的那样,实际的数据集有太多的变量和潜在的搜索,因此以上述方式对其进行硬编码是有意义的。我认为应该有一种使用 mutate_at 或其他 dplyr 函数的好方法,但我无法弄清楚。

我已经能够使用以下代码获得一组等于 1 或 0 的新变量,但不是单个变量。然后我可以使用 rowSums 对所有列求和,并检查该值是否为非零。但它又丑又费力:

df <- df %>% mutate_at(vars(starts_with("cd")),
                       funs(testvar = ifelse(startsWith(., "231"), 1, 0)))
test_names = df %>% select(ends_with("_testvar")) %>% names()
df <- df %>% mutate(flag_var = (rowSums(.[test_names]) == 1))
df <- df %>% select(-ends_with("_testvar"))

有人有更简单的想法吗?非常感谢!

编辑:我意识到我还必须合并编码类型变量。最初的样本数据表已经过编辑以反映这一点。

【问题讨论】:

  • 这绝对超出了我的 CRAN 'icd' 包,而 'dplyr' 是完成这项工作的好工具;只是提醒一下,“icd”可能有助于您可能需要的预处理、代码验证和更复杂的合并症计算。

标签: r dplyr


【解决方案1】:

这可能会回答问题,还是您需要 0-1 作为行值?

library(tidyverse)

dat <- tribble(~ID,   ~cd1,      ~cd2,      ~cd3,      ~cd4,
        1,    "231",    "219",    "1321",   "2314",
        2,    "222",    ""      , ""    ,   "",
        3,    "123",    "142",    ""    ,   "",
        4,    "145",    "521",    "2313",   "122")

dat %>% 
  gather("cd_type", "code", 2:5) %>% 
  mutate(flag_var = case_when(
    startsWith(code, "231") ~ 1,
    TRUE ~ 0
  ))
#> # A tibble: 16 x 4
#>       ID cd_type code  flag_var
#>    <dbl> <chr>   <chr>    <dbl>
#>  1     1 cd1     231          1
#>  2     2 cd1     222          0
#>  3     3 cd1     123          0
#>  4     4 cd1     145          0
#>  5     1 cd2     219          0
#>  6     2 cd2     ""           0
#>  7     3 cd2     142          0
#>  8     4 cd2     521          0
#>  9     1 cd3     1321         0
#> 10     2 cd3     ""           0
#> 11     3 cd3     ""           0
#> 12     4 cd3     2313         1
#> 13     1 cd4     2314         1
#> 14     2 cd4     ""           0
#> 15     3 cd4     ""           0
#> 16     4 cd4     122          0

或者这样做以恢复原始宽格式

dat %>% 
  gather("cd_type", "code", 2:5) %>% 
  mutate(flag_var = case_when(
    startsWith(code, "231") ~ 1,
    TRUE ~ 0
  )) %>% 
  spread(cd_type, code) %>% 
  select(ID, cd1:cd4, flag_var)
#> # A tibble: 6 x 6
#>      ID cd1   cd2   cd3   cd4   flag_var
#>   <dbl> <chr> <chr> <chr> <chr>    <dbl>
#> 1     1 <NA>  219   1321  <NA>         0
#> 2     1 231   <NA>  <NA>  2314         1
#> 3     2 222   ""    ""    ""           0
#> 4     3 123   142   ""    ""           0
#> 5     4 145   521   <NA>  122          0
#> 6     4 <NA>  <NA>  2313  <NA>         1

reprex package (v0.2.1) 于 2019-01-19 创建

【讨论】:

  • 谢谢,这似乎是一个优雅的方法。您可以从您的示例中看到,尽管它实际上并没有返回未更改的数据。当数据返回宽格式时,ID #1 和 #4 现在各有两个观察值。最终的数据集形式需要与其开始的方式保持不变,除非添加了一个新列。用你的方法可以吗?
【解决方案2】:

我们可以遍历列并与greplReducevectors 中的list 进行部分匹配,以单个逻辑vector 并将值强制为二进制

df$flag_var <- +(Reduce(`|`, lapply(df[-1], grepl, pattern = '^231')))

数据

df <- structure(list(ID = c("1", "2", "3", "4"), cd1 = c("231", "222", 
"123", "145"), cd2 = c("219", "", "142", "521"), cd3 = c("1321", 
 "", "", "2313"), cd4 = c("2314", "", "", "122")), row.names = c(NA, 
 -4L), class = "data.frame")

【讨论】:

    【解决方案3】:

    这是另一个解决方案:

    df$flag_var <- 1*(rowSums(substring(as.matrix(df[, 2:ncol(df)]), 1, 3) == '231') > 0)
    
    #   ID cd1 cd2  cd3  cd4 flag_var
    # 1  1 231 219 1321 2314        1
    # 2  2 222                      0
    # 3  3 123 142                  0
    # 4  4 145 521 2313  122        1
    

    对于更新后的示例,由于 cd 列和 type 列是配对的,因此以下代码应该可以工作:

    cd.cols <- grepl('^cd', colnames(df))
    type.cols <- grepl('^type', colnames(df))
    
    flag <- substring(as.matrix(df[,cd.cols]), 1, 3) == '231' & df[,type.cols] == '02'
    df$flag_var <- 1 * (rowSums(flag) > 0)
    
    # > df
    #   ID cd1 type1 cd2 type2  cd3 type3  cd4 type4 flag_var
    # 1  1 231    02 219    02 1321    02 2314    02        1
    # 2  2 222    02                                        0
    # 3  3 123   142                                        0
    # 4  4 145    02 521    02 2313    02                   1
    # 5  5 231    01                                        0
    

    【讨论】:

    • 非常感谢 - 这很好用。我意识到我的数据集还有另一个问题,那就是有一个额外的变量使事情变得复杂(请参阅我的原始帖子,该帖子已被编辑)。
    猜你喜欢
    • 1970-01-01
    • 2013-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 2022-12-05
    相关资源
    最近更新 更多