【问题标题】:Wide to long data in RR中的宽到长数据
【发布时间】:2022-08-14 15:48:37
【问题描述】:

我在 R 中有以下数据:

structure(list(Name = 1:4, Paper1 = c(\"C1\", \"C1\", \"C1\", \"C1\"), 
Marks1 = 1:4, Paper2 = c(\"D1\", \"D1\", \"D1\", \"D1\"), Marks2 = 1:4, 
Paper3 = c(\"E1\", \"E1\", \"E1\", \"E1\"), Marks3 = 12:15), class = \"data.frame\", row.names = c(NA, -4L))

我想这样安排我的数据:

structure(list(Name = c(1L, 1L, 1L, 2L, 2L, 2L), Paper = c(\"C1\", 
\"D1\", \"E1\", \"C1\", \"D1\", \"E1\"), Marks = c(1L, 1L, 12L, 2L, 2L, 
13L)), class = \"data.frame\", row.names = c(NA, -6L))

我试过shapemelt,但两者都没有提供所需的输出。请提出解决方案。

  • 输出数据中的名称 3 和 4 发生了什么变化?

标签: r data.table pivot reshape melt


【解决方案1】:

melt() 的一种选择:

library(data.table)
setDT(df)

melt(
  df, 
  id.vars      = 'Name', 
  measure.vars = patterns('^Paper', '^Marks'),
  value.name   = c('Paper', 'Marks')
)[order(Name), !'variable']

#     Name Paper Marks
#  1:    1    C1     1
#  2:    1    D1     1
#  3:    1    E1    12
#  4:    2    C1     2
#  5:    2    D1     2
#  6:    2    E1    13
#  7:    3    C1     3
#  8:    3    D1     3
#  9:    3    E1    14
# 10:    4    C1     4
# 11:    4    D1     4
# 12:    4    E1    15

【讨论】:

    【解决方案2】:

    对于较短的dplyr 选项,我们可以使用pivot_longer 使用names_sep-argument 和regex-look-around 来分隔数字之前。

    library(dplyr)
    
    df |> 
      pivot_longer(-Name, 
                   names_to = c(".value", NA), 
                   names_sep = "(?=\\d)")
    

    .. 或者正如@Allan Cameron 在第五位分裂所指出的那样:names_sep = 5

    如果您想保留相应论文和分数的编号,您可以将names_to 中的NA 替换为您想要的列名。

    输出:

    # A tibble: 12 × 3
        Name Paper Marks
       <int> <chr> <int>
     1     1 C1        1
     2     1 D1        1
     3     1 E1       12
     4     2 C1        2
     5     2 D1        2
     6     2 E1       13
     7     3 C1        3
     8     3 D1        3
     9     3 E1       14
    10     4 C1        4
    11     4 D1        4
    12     4 E1       15
    

    【讨论】:

      【解决方案3】:

      tidyverse 方式。

      df1 <-
        structure(list(
          Name = 1:4, 
          Paper1 = c("C1", "C1", "C1", "C1"), 
          Marks1 = 1:4, 
          Paper2 = c("D1", "D1", "D1", "D1"), 
          Marks2 = 1:4, 
          Paper3 = c("E1", "E1", "E1", "E1"), 
          Marks3 = 12:15), 
          class = "data.frame", row.names = c(NA, -4L))
      
      df2 <-
        structure(list(
          Name = c(1L, 1L, 1L, 2L, 2L, 2L), 
          Paper = c("C1", "D1", "E1", "C1", "D1", "E1"), 
          Marks = c(1L, 1L, 12L, 2L, 2L, 13L)), 
          class = "data.frame", row.names = c(NA, -6L))
      
      
      suppressPackageStartupMessages(library(tidyverse))
      
      df1 %>%
        mutate(across(starts_with("Marks"), as.character)) %>%
        pivot_longer(-Name, names_to = "PM") %>%
        mutate(PM = gsub("\\d+", "", PM)) %>% 
        group_by(PM) %>%
        mutate(id = row_number()) %>% 
        pivot_wider(
          id_cols = c(id, Name),
          names_from = PM,
          values_from = value
        ) %>%
        select(-id)
      #> # A tibble: 12 × 3
      #>     Name Paper Marks
      #>    <int> <chr> <chr>
      #>  1     1 C1    1    
      #>  2     1 D1    1    
      #>  3     1 E1    12   
      #>  4     2 C1    2    
      #>  5     2 D1    2    
      #>  6     2 E1    13   
      #>  7     3 C1    3    
      #>  8     3 D1    3    
      #>  9     3 E1    14   
      #> 10     4 C1    4    
      #> 11     4 D1    4    
      #> 12     4 E1    15
      

      reprex package (v2.0.1) 于 2022 年 8 月 1 日创建

      【讨论】:

        【解决方案4】:

        这是一个更简单的 tidyverse 解决方案

        library(tidyverse)
        
        df %>%
        group_by(Name) %>%
          summarise(Paper = unlist(across(starts_with('Paper'), c)),
                    Marks = unlist(across(starts_with('Marks'), c)))
        #> # A tibble: 12 x 3
        #> # Groups:   Name [4]
        #>     Name Paper Marks
        #>    <int> <chr> <int>
        #>  1     1 C1        1
        #>  2     1 D1        1
        #>  3     1 E1       12
        #>  4     2 C1        2
        #>  5     2 D1        2
        #>  6     2 E1       13
        #>  7     3 C1        3
        #>  8     3 D1        3
        #>  9     3 E1       14
        #> 10     4 C1        4
        #> 11     4 D1        4
        #> 12     4 E1       15
        

        【讨论】:

          【解决方案5】:

          使用reshape 的基本 R 选项

          reshape(
            df,
            direction = "long",
            idvar = "Name",
            varying = -1,
            v.names = c("Paper","Marks")
          )
          

              Name time Paper Marks
          1.1    1    1     1    C1
          2.1    2    1     2    C1
          3.1    3    1     3    C1
          4.1    4    1     4    C1
          1.2    1    2     1    D1
          2.2    2    2     2    D1
          3.2    3    2     3    D1
          4.2    4    2     4    D1
          1.3    1    3    12    E1
          2.3    2    3    13    E1
          3.3    3    3    14    E1
          4.3    4    3    15    E1
          

          【讨论】:

            【解决方案6】:

            另一种可能的解决方案,基于purrr::map2_dfr

            library(tidyverse)
            
            map2_dfr(select(df, starts_with("Paper")), select(df, starts_with("Marks")),
                     ~ tibble(Name = df$Name, Paper = .x, Marks = .y))
            
            #> # A tibble: 12 × 3
            #>     Name Paper Marks
            #>    <int> <chr> <int>
            #>  1     1 C1        1
            #>  2     2 C1        2
            #>  3     3 C1        3
            #>  4     4 C1        4
            #>  5     1 D1        1
            #>  6     2 D1        2
            #>  7     3 D1        3
            #>  8     4 D1        4
            #>  9     1 E1       12
            #> 10     2 E1       13
            #> 11     3 E1       14
            #> 12     4 E1       15
            

            【讨论】:

              【解决方案7】:

              另一个使用来自sjmiscto_long 的选项:

              df <- structure(list(Name = 1:4, Paper1 = c("C1", "C1", "C1", "C1"), 
                                   Marks1 = 1:4, Paper2 = c("D1", "D1", "D1", "D1"), Marks2 = 1:4, 
                                   Paper3 = c("E1", "E1", "E1", "E1"), Marks3 = 12:15), class = "data.frame", row.names = c(NA, -4L))
              
              library(sjmisc)
              output <- to_long(df, 
                      key = 'group', 
                      values = c('Paper', "Marks"), 
                      grep("Paper", names(df)),
                      grep("Marks", names(df)),
                      recode.key = TRUE)[,-2]
              output[order(output$Name),]
              #>    Name Paper Marks
              #> 1     1    C1     1
              #> 5     1    D1     1
              #> 9     1    E1    12
              #> 2     2    C1     2
              #> 6     2    D1     2
              #> 10    2    E1    13
              #> 3     3    C1     3
              #> 7     3    D1     3
              #> 11    3    E1    14
              #> 4     4    C1     4
              #> 8     4    D1     4
              #> 12    4    E1    15
              

              reprex package (v2.0.1) 于 2022 年 8 月 1 日创建

              【讨论】:

                【解决方案8】:

                另一种方法是从名称中删除 1,然后将它们与 rbind 堆叠在一起

                colnames(df) <- (colnames(df) |> stringr::str_extract("\\D+"))
                
                
                cbind(df[1], rbind(df[2:3],df[4:5],df[6:7]))
                
                   Name Paper Marks
                1     1    C1     1
                2     2    C1     2
                3     3    C1     3
                4     4    C1     4
                5     1    D1     1
                6     2    D1     2
                7     3    D1     3
                8     4    D1     4
                9     1    E1    12
                10    2    E1    13
                11    3    E1    14
                12    4    E1    15
                

                更少的硬编码

                cbind(df[1],
                      do.call(rbind,
                              lapply(seq(0, ncol(df) - 2, 2 ), \(x) {
                              colnames(df) <- strsplit(colnames(df), "\\d")
                  df[x + 2:3]
                })))
                
                

                【讨论】:

                  【解决方案9】:
                  library(tidyverse)
                  
                  pivot_longer(
                    data = df
                    cols = -Name, # choose all columns except the "Name" column
                    names_to = c(".value", "No."), # new column names according to names_pattern
                    names_pattern = "(.*)(\\d)" # separate into two parts, i.e. (string)(number)
                  )
                  

                  【讨论】:

                    猜你喜欢
                    • 2019-03-03
                    • 1970-01-01
                    • 2016-03-20
                    • 2016-06-27
                    • 1970-01-01
                    • 2018-04-19
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-06-07
                    相关资源
                    最近更新 更多