【问题标题】:Convert dataframe from wide format to long format with key stored in row R将数据帧从宽格式转换为长格式,密钥存储在 R 行中
【发布时间】:2019-05-08 20:11:41
【问题描述】:

我正在使用tidyverse,但也欢迎使用base 解决方案。

有没有办法在不转置 gather 数据帧的情况下,但 key 不是列名,而是 key 存储在一行中。例如,假设我有一个名为 df 的 tibble。

df <- tibble(a = c(5,3,5,6,2,"G1"),
             b = c(5,3,5,6,2,"G1"),
             c = c(8,2,6,4,1,"G2"),
             d = c(8,2,6,4,1,"G2"),
             e = c(9,3,7,8,4,"G3"),
             f = c(9,3,7,8,4,"G3"),
             g = c(6,5,2,1,8,"G4"),
             h = c(6,5,2,1,8,"G4"))
df
# A tibble: 6 x 8
  a     b     c     d     e     f     g     h    
  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 5     5     8     8     9     9     6     6    
2 3     3     2     2     3     3     5     5    
3 5     5     6     6     7     7     2     2    
4 6     6     4     4     8     8     1     1    
5 2     2     1     1     4     4     8     8    
6 G1    G1    G2    G2    G3    G3    G4    G4 

要分组或聚集的组位于底行。有没有办法让df 只有三列,使 c、e 和 g 列聚集到 a 列,d、f 和 h 列聚集到 b 列,第 6 行变成 c 列?结果如下:

tibble(a = c(5,3,5,6,2,8,2,6,4,1,9,3,7,8,4,6,5,2,1,8),
       b = c(5,3,5,6,2,8,2,6,4,1,9,3,7,8,4,6,5,2,1,8),
       c = c("G1","G1","G1","G1","G1","G2","G2","G2","G2","G2",
             "G3","G3","G3","G3","G3","G4","G4","G4","G4","G4"))
# A tibble: 20 x 3
       a     b c    
   <dbl> <dbl> <chr>
 1     5     5 G1   
 2     3     3 G1   
 3     5     5 G1   
 4     6     6 G1   
 5     2     2 G1   
 6     8     8 G2   
 7     2     2 G2   
 8     6     6 G2   
 9     4     4 G2   
10     1     1 G2   
11     9     9 G3   
12     3     3 G3   
13     7     7 G3   
14     8     8 G3   
15     4     4 G3   
16     6     6 G4   
17     5     5 G4   
18     2     2 G4   
19     1     1 G4   
20     8     8 G4 

我想避免转置,因为我需要保留行和列的顺序,直到所有内容都正确标记为止。

【问题讨论】:

  • 试试data.frame(a = c(df$a, unlist(df[c("c", "e","g")])), b = c(df$b, unlist(df[c("d", "f", "h")]))) %&gt;% group_by(grp = lag(cumsum(grepl("^G", a)), default = 0)) %&gt;% mutate(c = b[n()])
  • @akrun 它可以工作,但并不完美。但问题在于您必须在unlist 中指定列。我的列比示例 df 多得多,因此最好无需指定列名即可解决此问题。
  • 我只是想问一下。您是否有任何列模式,例如特定顺序以及留下多少列(这里是 a 和 b)
  • 好的,你的问题得到了答案。希望对你有用

标签: r tidyr


【解决方案1】:

这是一个想法。

library(tidyverse)

df2 <- df %>%
  t() %>%
  as.data.frame(stringsAsFactors = FALSE) %>%
  split(f = .$V6) %>%
  map_dfr(~.x %>% 
            select(-V6) %>%
            t() %>%
            as.data.frame(stringsAsFactors = FALSE) %>%
            setNames(c("a", "b")),
          .id = "c") %>%
  select(a, b, c) %>%
  mutate_at(vars(-c), list(~as.numeric(.)))

df2
#    a b  c
# 1  5 5 G1
# 2  3 3 G1
# 3  5 5 G1
# 4  6 6 G1
# 5  2 2 G1
# 6  8 8 G2
# 7  2 2 G2
# 8  6 6 G2
# 9  4 4 G2
# 10 1 1 G2
# 11 9 9 G3
# 12 3 3 G3
# 13 7 7 G3
# 14 8 8 G3
# 15 4 4 G3
# 16 6 6 G4
# 17 5 5 G4
# 18 2 2 G4
# 19 1 1 G4
# 20 8 8 G4

【讨论】:

    【解决方案2】:

    这是一种实现。我们可以根据最后一行将split tibble 变成list 的tibble,循环通过list 和imap,rename 列到相同的列名('a','b'), mutate 使用 list 名称创建列 'c' 并绑定行

    library(tidyverse)
    df %>% 
       slice(-n()) %>%
       split.default(df %>% 
                        slice(n())  %>% 
                        flatten_chr) %>%
         imap_dfr(~ .x %>% 
                   rename_all(~ c('a', 'b')) %>%
         mutate(c = .y))
    # A tibble: 20 x 3
    #   a     b     c    
    #   <chr> <chr> <chr>
    # 1 5     5     G1   
    # 2 3     3     G1   
    # 3 5     5     G1   
    # 4 6     6     G1   
    # 5 2     2     G1   
    # 6 8     8     G2   
    # 7 2     2     G2   
    # 8 6     6     G2   
    # 9 4     4     G2   
    #10 1     1     G2   
    #11 9     9     G3   
    #12 3     3     G3   
    #13 7     7     G3   
    #14 8     8     G3   
    #15 4     4     G3   
    #16 6     6     G4   
    #17 5     5     G4   
    #18 2     2     G4   
    #19 1     1     G4   
    #20 8     8     G4  
    

    【讨论】:

      【解决方案3】:

      如果你一步一步地进行移调,可能不会有什么坏处。在这个基础 R 解决方案中,行和列信息一直保留到最后一行。

      d <- data.frame(t(as.matrix(df)))
      l <- lapply(split(d[-6], d$X6), t)
      res <- do.call(rbind, Map(cbind, l, c=names(l)))
      res <- setNames(data.frame(res, row.names=NULL), letters[1:3])
      res
      #    a b  c
      # 1  5 5 G1
      # 2  3 3 G1
      # 3  5 5 G1
      # 4  6 6 G1
      # 5  2 2 G1
      # 6  8 8 G2
      # 7  2 2 G2
      # 8  6 6 G2
      # 9  4 4 G2
      # 10 1 1 G2
      # 11 9 9 G3
      # 12 3 3 G3
      # 13 7 7 G3
      # 14 8 8 G3
      # 15 4 4 G3
      # 16 6 6 G4
      # 17 5 5 G4
      # 18 2 2 G4
      # 19 1 1 G4
      # 20 8 8 G4
      

      【讨论】:

        【解决方案4】:

        data.table 的一个选项

        首先,由于我们没有使用原始名称,请替换它们。同时删除最后一行并将所有内容转换为整数。

        library(data.table)
        setDT(df)
        
        df <- df[-.N]
        df[, names(df) := lapply(.SD, as.integer)]
        setnames(df, rep_len(c('a', 'b'), ncol(df)))
        
        #    a b a b a b a b
        # 1: 5 5 8 8 9 9 6 6
        # 2: 3 3 2 2 3 3 5 5
        # 3: 5 5 6 6 7 7 2 2
        # 4: 6 6 4 4 8 8 1 1
        # 5: 2 2 1 1 4 4 8 8
        

        现在melt 在行号上,添加 G[1-4] 列,并将熔化的 df dcast 为宽格式。

        df[, rid := 1:.N]
        df2 <- melt(df, 'rid')
        df2[, c := paste0('G', rowid(rid, variable))]
        dcast(df2, rid + c ~ variable)[order(c), -'rid']
        
        #      c a b
        #  1: G1 5 5
        #  2: G1 3 3
        #  3: G1 5 5
        #  4: G1 6 6
        #  5: G1 2 2
        #  6: G2 8 8
        #  7: G2 2 2
        #  8: G2 6 6
        #  9: G2 4 4
        # 10: G2 1 1
        # 11: G3 9 9
        # 12: G3 3 3
        # 13: G3 7 7
        # 14: G3 8 8
        # 15: G3 4 4
        # 16: G4 6 6
        # 17: G4 5 5
        # 18: G4 2 2
        # 19: G4 1 1
        # 20: G4 8 8 
        

        【讨论】:

          猜你喜欢
          • 2013-10-22
          • 1970-01-01
          • 2023-02-25
          • 1970-01-01
          • 1970-01-01
          • 2022-07-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多