【问题标题】:splitting all the columns in a dataframe based on their value and delimiter根据值和分隔符拆分数据框中的所有列
【发布时间】:2023-03-16 05:12:01
【问题描述】:

我有一个如下的数据框:

df <- data.frame(s1=c("a","a/b","b","a","a/b"),s2=c("ab/bb","bb","ab","ab","bb"),s3=c("Doa","Doa","Dob/Doa","Dob/Doa","Dob"))
   s1    s2      s3
1   a ab/bb     Doa
2 a/b    bb     Doa
3   b    ab Dob/Doa
4   a    ab Dob/Doa
5 a/b    bb     Dob

每列可以采用两个值之一,也可以采用“/”分隔。我想根据它们的值将它们分解为二进制列集。

所需的数据框是:

   a   b   ab   bb   Doa   Dob
1  1   0    1    1    1    0
2  1   1    0    1    1    0
3  0   1    1    0    1    1
4  1   0    1    0    1    1
5  1   1    0    1    0    1

我尝试使用 tidyr::separate 和 tapply 执行此操作,但它变得相当复杂,因为我必须为每一对指定列名。有很多列。

【问题讨论】:

    标签: r dplyr tidyr


    【解决方案1】:

    首先确保您的数据是字符而不是因素。然后将每一行和每一行拆分为一个 data.frame,获取'/' 上的 str_split,将名称设置为等于值,并将其设为列表。现在您可以将这些结果绑定在一起,最后将所有非 na 值设置为 1。

    library(tidyverse) # dplyr, + stringr for str_split, + purrr for map
    
    df %>% 
      mutate_all(as.character) %>% 
      split(seq(nrow(.))) %>% 
      map(~ str_split(., '/') %>% unlist %>% setNames(., .) %>% as.list) %>% 
      bind_rows %>% 
      mutate_all(~as.numeric(!is.na(.)))
    
    # # A tibble: 5 x 6
    #       a    ab    bb   Doa     b   Dob
    #   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    # 1     1     1     1     1     0     0
    # 2     1     0     1     1     1     0
    # 3     0     1     0     1     1     1
    # 4     1     1     0     1     0     1
    # 5     1     0     1     0     1     1
    

    另一个类似的选项(相同的输出)

    df %>% 
      mutate_all(as.character) %>% 
      split(seq(nrow(.))) %>% 
      map(~ str_split(., '/') %>% unlist %>% table %>% as.list) %>% 
      bind_rows %>% 
      mutate_all(replace_na, 0)
    

    或者您可以先转换为长,然后再转换为宽,类似于 akrun 的回答

    library(data.table)
    setDT(df)
    library(magrittr)
    
    melt(df[, r := 1:.N], 'r') %>% 
      .[, .(value = strsplit(value, '/')[[1]]), .(r, variable)] %>% 
      dcast(r ~ value, fun.aggregate = length)
    
    #    r Doa Dob a ab b bb
    # 1: 1   1   0 1  1 0  1
    # 2: 2   1   0 1  0 1  1
    # 3: 3   1   1 0  1 1  0
    # 4: 4   1   1 1  1 0  0
    # 5: 5   0   1 1  0 1  1
    

    【讨论】:

      【解决方案2】:

      另一种方法是使用pivot_longer 转换为“长”格式,然后使用separate_rows 拆分“值”列并重新整形为“宽”格式

      library(dplyr)
      library(tidyr)
      df %>%
          mutate(rn = row_number()) %>%
          pivot_longer(cols = -rn) %>%
          separate_rows(value) %>%
          mutate(i1 = 1) %>%
          select(-name) %>%
          pivot_wider(names_from = value, values_from = i1, values_fill = list(i1 = 0)) %>%
          select(-rn)
      # A tibble: 5 x 6
      #      a    ab    bb   Doa     b   Dob
      #  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
      #1     1     1     1     1     0     0
      #2     1     0     1     1     1     0
      #3     0     1     0     1     1     1
      #4     1     1     0     1     0     1
      #5     1     0     1     0     1     1
      

      或将base Rtablestrsplit 一起使用

      +(table(stack(setNames(strsplit(as.character(unlist(df)), "/",
             fixed = TRUE), c(row(df))))[2:1]) > 0)
      #  values
      #ind a ab b bb Doa Dob
      #  1 1  1 0  1   1   0
      #  2 1  0 1  1   1   0
      #  3 0  1 1  0   1   1
      #  4 1  1 0  0   1   1
      #  5 1  0 1  1   0   1
      

      【讨论】:

      • 你能解释一下+table吗?
      • @camille &gt; 0 返回一个逻辑矩阵。所以,我只是用+ 将其强制为二进制
      • 为什么不只是t(table(stack(setNames(strsplit(as.character(unlist(df)), "/", fixed = TRUE), c(row(df))))))
      • @Matt 它正在转置,我只是重新排序stack
      • separate_rows 的默认分隔符是什么? “[^[:alnum:].]”是否包含“/”
      猜你喜欢
      • 2018-10-25
      • 1970-01-01
      • 2013-02-27
      • 2023-02-11
      • 2018-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多