【问题标题】:R count sum of partial string matches over multiple columnsR计数多列部分字符串匹配的总和
【发布时间】:2021-02-10 23:06:47
【问题描述】:

我正在处理一个不整洁的夏令营登记表。表单输出如下:

          leaders         teen_adventure
1 camp, overnight                   <NA>
2            <NA>                   <NA>
3 camp, overnight camp, float, overnight

我想生成新列来汇总每个可能答案的总数。

          leaders         teen_adventure camps overnights floats
1 camp, overnight                   <NA>     1          1      0
2            <NA>                   <NA>     0          0      0
3 camp, overnight camp, float, overnight     2          2      1

我觉得这有一个 dplyr 解决方案,例如:

reprex %>%
  mutate(camps = sum(case_when(
    str_detect(select(., everything()), "camp") ~ 1,
    TRUE ~ 0
  )))

或者也许使用cross()。

这里是样本数据集:

# data
reprex <- structure(list(leaders = c("camp, overnight", NA, "camp, overnight"), 
          teen_adventure = c(NA, NA, "camp, float, overnight")), 
          row.names = c(NA, -3L), class = "data.frame")

【问题讨论】:

  • 也许可以尝试使用pivot_longer() 进行堆叠,然后这样的事情可能会有所帮助:stackoverflow.com/questions/58674656/…
  • 除了camp、float、night,还有没有可能有更多的专栏?
  • @Dayne 实际上还有另一列用于善后护理,而不是在此代表中。如果您的问题是是否有更多包含实际数据的名义列,那么也可以。

标签: r dplyr stringr


【解决方案1】:

我们可以通过循环列(map)提取带有str_extract_all的单词,然后使用mtabulate获取频率计数,绑定list元素,summarise数字列以获取@ 987654326@

library(dplyr)
library(tidyr)
library(purrr)
library(stringr)
library(qdapTools)
library(data.table)
reprex %>% 
   map_dfr(~ str_extract_all(.x, "\\w+") %>%
             mtabulate, .id = 'grp') %>%
   group_by(grp = rowid(grp)) %>% 
   summarise(across(everything(), sum, na.rm = TRUE), 
       .groups = 'drop') %>%
   select(-grp) %>% 
   bind_cols(reprex, .)

-输出

#            leaders         teen_adventure camp overnight float
#1 camp, overnight                   <NA>    1         1     0
#2            <NA>                   <NA>    0         0     0
#3 camp, overnight camp, float, overnight    2         2     1

【讨论】:

    【解决方案2】:

    基本 R 选项

    v <- unique(unlist(strsplit(na.omit(unlist(reprex)), ",\\s+")))
    reprex <- cbind(
      reprex,
      do.call(
        rbind,
        lapply(
          1:nrow(reprex),
          function(k) table(factor(unlist(strsplit(na.omit(unlist(reprex[k, ])), ",\\s+")), levels = v))
        )
      )
    )
    

    给了

              leaders         teen_adventure camp overnight float
    1 camp, overnight                   <NA>    1         1     0
    2            <NA>                   <NA>    0         0     0
    3 camp, overnight camp, float, overnight    2         2     1
    

    【讨论】:

    • 不错的基础 R 选项
    【解决方案3】:

    此解决方案适用于任何数量的列和值:

    reprex %>%
     as_tibble %>%
     # split the values by `, `
     mutate_all(strsplit, ", ") %>%
     # map through each column then each cell in order make it a named vector
     # for example the first cell : c("camp", "overnight") => c("camp"=1, "overnight"=1)
     # then pivot it longer by the row_number (this is done for quickly suming the values)
     map_dfr( function(x) x %>% map_dfr( ~ set_names(rep(1, length(.x<-.x[!is.na(.x)])), .x)) %>%
         mutate(id = row_number()) %>% 
         pivot_longer(!id) ) %>%
     # group by id and name so group the same variables that are found in the same row
     group_by(id, name) %>%
     # get the sum
     summarise_all(sum, na.rm=T) %>%
     ungroup %>%
     # return the tibble to wide format
     pivot_wider %>%
     # remove the id column
     select(-id) %>%
     # add the original data.frame to it
     tibble(reprex, .)
    
    # A tibble: 3 x 5
      leaders         teen_adventure          camp float overnight
      <chr>           <chr>                  <dbl> <dbl>     <dbl>
    1 camp, overnight NA                         1     0         1
    2 NA              NA                         0     0         0
    3 camp, overnight camp, float, overnight     2     1         2
    

    【讨论】:

      【解决方案4】:

      一种方式:

      library(stringr)
      library(tidyr)
      reprex %>%
        replace_na(list(leaders='unknown',teen_adventure='unknown'))%>%
        mutate(camp=as.numeric(str_detect(leaders, 'camp')+str_detect(teen_adventure,'camp')),
               float=as.numeric(str_detect(leaders,'float')+str_detect(teen_adventure,'float')),
               overnight=as.numeric(str_detect(leaders,'overnight')+str_detect(teen_adventure,'overnight')))
      

      输出:

                leaders         teen_adventure camp float overnight
      1 camp, overnight                unknown    1     0         1
      2         unknown                unknown    0     0         0
      3 camp, overnight camp, float, overnight    2     1         2
      

      【讨论】:

      • 这个解决方案最接近我的理解。此方法将未包含在表示中的标称数据保留在单行中,而 pivot() 选项则不会。我很好奇,但如果不是输入正在计算的各个列,例如str_detect(leaders ....) + str_detect(teen....),而是一种对选择的列求和的方法。
      • 所以我认为可以有两种方法:首先,我们单独编写一个函数(或使用 lapply),以便 str_detect 为数据帧的每个向量工作。这样我们就只提到那个功能(肯定会尝试)。第二种方法是使用separate 拆分列领导者和teen_adventures,然后处理df(将在某处使用pivot)但它要求这些列足够整洁,因为没有随机文本或逗号(这就是我没有使用的原因这个方法)。
      • 如果您的前两列足够整洁,那么我认为其他大多数答案也应该可以正常工作。我没有使用这种方法,因为您在问题中说您的数据不整洁。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-10
      • 2016-09-14
      • 1970-01-01
      • 1970-01-01
      • 2021-09-14
      • 2016-10-18
      相关资源
      最近更新 更多