【问题标题】:How to split bigrams into column and row pairs for n-columns如何将二元组拆分为 n 列的列和行对
【发布时间】:2022-02-04 13:24:42
【问题描述】:

假设一个这样的数据框:

# example dataset
df <- data.frame(
         rowid = 1:3,
         a = c("ax","cz","by"),
         b = c("cy","ax","bz"),
         c = c("bz","ay","cx")
      )

实现以下转换的有效方法是什么?

#> # A tibble: 3 x 4
#>  rowid      a       b       c
#>  <int>  <chr>   <chr>   <chr>
#>      1      x       z       y
#>      2      x       y       z
#>      3      y       z       x

我们的目标是获取每个二元组的第二个字符并将其分类到由第一个字符挑选出来的列中,用于每一行。

如果可能,比较基本 R 和 Tidyverse 解决方案会很有用。

【问题讨论】:

    标签: r tidyverse


    【解决方案1】:

    由于您正在寻找基本和 Tidyverse 的比较,我将附上基本解决方案:

    tdf <- t(df[-1])
    tdf[] <- substr(tdf, 2, 2)[order(col(tdf), tdf)]
    df[-1] <- t(tdf)
    
    #  rowid a b c
    #1     1 x z y
    #2     2 x y z
    #3     3 y z x
    

    解释这三个步骤:

    .1) 复制一份 t()ransposed 版本的数据
    .2a) 在每行中获取order (col() 现在因为它已被转置) 每个字符串(隐式的第一个字母)
    .2b) 使用此顺序从每个字符串的第二个字母中选择并覆盖&lt;- 转置后的数据。
    .3) t()ranspose 回到原来的结构并覆盖&lt;-df中的数据


    30K 行的基准测试

    基地:

    bigdf <- df[rep(1:3,10000),]
    bigdf$rowid <- 1:30000
    
    system.time({
        tdf <- t(bigdf[-1])
        tdf[] <- substr(tdf,2,2)[order(col(tdf), tdf)]
        bigdf[-1] <- t(tdf)
    })
    ##   user  system elapsed 
    ##  0.023   0.000   0.023 
    

    整洁:

    bigdf <- df[rep(1:3,10000),]
    bigdf$rowid <- 1:30000
    
    library(dplyr)
    library(tibble)
    library(sjmisc)
    library(stringr)
    
    system.time({
        bigdf %>%
            rotate_df(cn=TRUE) %>%
            mutate(across(everything(),sort)) %>%
            rotate_df() %>%
            mutate(across(everything(),~str_sub(.,2,-1))) %>%
            rownames_to_column(var="rowid")
    })
    ##   user  system elapsed 
    ## 21.177   0.047  21.244 
    

    【讨论】:

    • 优雅。是否有任何不使用转置技巧的策略?
    • @louisdesu - 可能还有其他几种方法可以实现这一点 - 重塑为长文件,重新排序和重塑也可以做到。但这是我能想到的最快、最简单的方法。
    【解决方案2】:

    Tidyverse 解决方案的部分灵感来自最近使用来自sjmisc 包的rotate_df() 的帖子:https://stackoverflow.com/a/70682560/8068516

    df <- data.frame(
         rowid = 1:3,
         a = c("ax","cz","by"),
         b = c("cy","ax","bz"),
         c = c("bz","ay","cx")
      )
    
    library(sjmisc)
    df %>%
      # transpose the dataframe keeping column names
      rotate_df(cn=TRUE) %>%
      # sort columns by first character
      mutate(across(everything(),sort)) %>%
      # transpose back
      rotate_df() %>%
      # remove first character from each string
      mutate(across(everything(),~str_sub(.,2,-1))) %>%
      # make `rowid` column
      rownames_to_column(var="rowid")
    

    可以选择将数据框转换为带有as_tibble() 的tibble 以完全匹配目标输出,给出:

    #> # A tibble: 3 x 4
    #>  rowid       a        b        c
    #>  <int>   <chr>    <chr>    <chr>
    #>      1       x        z        y
    #>      2       x        y        z
    #>      3       y        z        x
    

    此解决方案将推广到 n 列并且与 %&gt;% 兼容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-18
      • 2021-01-30
      相关资源
      最近更新 更多