【问题标题】:Reshape table with multiple columns in groups of 3 into table with 3 columns [duplicate]将具有 3 组中的多列的表格重塑为具有 3 列的表格 [重复]
【发布时间】:2017-12-21 01:20:59
【问题描述】:

我有一个包含多列和多行的数据框, 看起来像:

                V1       V2        V3         V4      V5       V6
  1             1         2         3         13      14       15
  2             4         5         6         16      NA       NA 
  3             7         8         9         19      20       21 
  4             10        11        12        22      23       24

我想把它改造成:

                V1       V2        V3       
  1             1         2         3         
  2             4         5         6         
  3             7         8         9         
  4             10        11        12       
  5             13        14        15
  6             16        NA        NA 
  7             19        20        21 
  8             22        23        24

在原始data.frame中,将每3列保持为一个组,使得(V1V2V3)是group1,(V4,@987654327 @、V6) 是 group2 等。然后将 group2(值的顺序不变)移动到 group1 的末尾,然后将 group3 移动到 group2 的末尾。

我试过了:

  as.data.frame(matrix(unlist(mydata, use.names=FALSE), ncol=3, byrow=TRUE))

但存在值序问题。

我怎样才能得到我想要的数据结构?

【问题讨论】:

    标签: r dataframe multiple-columns reshape


    【解决方案1】:

    您已经注意到unlist 会按列为您提供值:

    unlist(df[1:3], use.names = FALSE)
    ##  [1]  1  4  7 10  2  5  8 11  3  6  9 12
    

    要逐行获取值,您可以使用 c(t(...)) 成语:

    c(t(df[1:3]))
    ##  [1]  1  2  3  4  5  6  7  8  9 10 11 12
    

    这将允许您使用以下方法解决基础 R 中的问题:

    as.data.frame(matrix(c(t(df[1:3]), t(df[4:6])), ncol = 3, byrow = TRUE))
    ##   V1 V2 V3
    ## 1  1  2  3
    ## 2  4  5  6
    ## 3  7  8  9
    ## 4 10 11 12
    ## 5 13 14 15
    ## 6 16 NA NA
    ## 7 19 20 21
    ## 8 22 23 24
    

    概括为一个函数,你可以试试这样的:

    splitter <- function(indf, ncols) {
      if (ncol(indf) %% ncols != 0) stop("Not the right number of columns to split")
      inds <- split(sequence(ncol(indf)), c(0, sequence(ncol(indf)-1) %/% ncols))
      temp <- unlist(lapply(inds, function(x) c(t(indf[x]))), use.names = FALSE)
      as.data.frame(matrix(temp, ncol = ncols, byrow = TRUE))
    }
    splitter(df, 3)
    

    更灵活的“data.table”方法如下所示:

    library(data.table)
    rbindlist(split.default(as.data.table(df), 
                            c(0, sequence(ncol(df)-1) %/% 3)), 
              use.names = FALSE)
    ##    V1 V2 V3
    ## 1:  1  2  3
    ## 2:  4  5  6
    ## 3:  7  8  9
    ## 4: 10 11 12
    ## 5: 13 14 15
    ## 6: 16 NA NA
    ## 7: 19 20 21
    ## 8: 22 23 24
    

    【讨论】:

    • 非常感谢!我已经用我的真实世界大数据框尝试了最后两个代码,效果惊人!
    • @Duerna,很高兴为您提供帮助! cmatrix 等应该非常快,并且不需要任何包。 data.table 为您提供了使用大型数据集的优势....
    【解决方案2】:

    我很惊讶没有人提到split.default,它也适用于具有更多列的数据:

    x <- split.default(df, ceiling(seq_along(df) / 3 ))
    do.call(rbind, lapply(x, setNames, names(x[[1]])))
    
    #     V1 V2 V3
    # 1.1  1  2  3
    # 1.2  4  5  6
    # 1.3  7  8  9
    # 1.4 10 11 12
    # 2.1 13 14 15
    # 2.2 16 NA NA
    # 2.3 19 20 21
    # 2.4 22 23 24
    

    添加make.row.names = FALSE 去掉奇怪的行名:

    do.call(rbind, c(lapply(x, setNames, names(x[[1]])), list(make.row.names = FALSE)))
    #   V1 V2 V3
    # 1  1  2  3
    # 2  4  5  6
    # 3  7  8  9
    # 4 10 11 12
    # 5 13 14 15
    # 6 16 NA NA
    # 7 19 20 21
    # 8 22 23 24
    

    【讨论】:

    • split.default 在这里确实有意义。
    • split.default 也让data.table 的使用变得非常简单......
    【解决方案3】:

    你可以使用data.table解决这个问题:-

    df <- data.frame(V1 = c(1, 4, 7, 10), V2 = c(2, 5, 8, 11), V3 = c(3, 6, 9, 12), V4 = c(13, 16, 19, 22), V5 = c(14, NA, 20, 23), V6 = c(15, NA, 21, 24))
    
    
    library(data.table)
    setDT(df)
    df1 <- df[, c("V4", "V5", "V6")]
    setnames(df1, "V4", "V1")
    setnames(df1, "V5", "V2")
    setnames(df1, "V6", "V3")
    df <- df[, c("V1", "V2", "V3")]
    df <- rbind(df, df1)
    

    输出将是:-

       V1 V2 V3
    1:  1  2  3
    2:  4  5  6
    3:  7  8  9
    4: 10 11 12
    5: 13 14 15
    6: 16 NA NA
    7: 19 20 21
    8: 22 23 24
    

    【讨论】:

    • 谢谢!看来我需要很长的代码,因为我有 225 列(75 组 X3)。
    • @Duerna,我用更灵活的data.table 方法编辑了my answer
    • @A5C1D2H2I1M1N2O1R2T1 非常感谢!
    【解决方案4】:

    使用 的解决方案。

    library(dplyr)
    library(tidyr)
    
    dt2 <- dt %>%
      gather(Column, Value) %>%
      extract(Column, into = c("Group", "Index"), regex = "([A-Z+])([\\d].*$)",
              convert = TRUE) %>%
      mutate(Index = Index %% 3) %>%
      mutate(Index = ifelse(Index == 0, 3, Index)) %>%
      unite(Column, c("Group", "Index"), sep = "") %>%
      group_by(Column) %>%
      mutate(ID = 1:n()) %>%
      spread(Column, Value) %>%
      select(-ID)
    dt2
    # # A tibble: 8 x 3
    #      V1    V2    V3
    # * <int> <int> <int>
    # 1     1     2     3
    # 2     4     5     6
    # 3     7     8     9
    # 4    10    11    12
    # 5    13    14    15
    # 6    16    NA    NA
    # 7    19    20    21
    # 8    22    23    24
    

    数据

    dt <- read.table(text = "              V1       V2        V3         V4      V5       V6
      1             1         2         3         13      14       15
                     2             4         5         6         16      NA       NA 
                     3             7         8         9         19      20       21 
                     4             10        11        12        22      23       24",
                     header = TRUE)
    

    更新

    这是一个示例,显示代码也适用于更大的数据框。

    library(dplyr)
    library(tidyr)
    
    # Create example data frame
    dt <- as_data_frame(matrix(1:60, ncol = 12, byrow = TRUE))
    
    dt2 <- dt %>%
      gather(Column, Value) %>%
      extract(Column, into = c("Group", "Index"), regex = "([A-Z+])([\\d].*$)",
              convert = TRUE) %>%
      mutate(Index = Index %% 3) %>%
      mutate(Index = ifelse(Index == 0, 3, Index)) %>%
      unite(Column, c("Group", "Index"), sep = "") %>%
      group_by(Column) %>%
      mutate(ID = 1:n()) %>%
      spread(Column, Value) %>%
      select(-ID)
    dt2
    # # A tibble: 20 x 3
    #      V1    V2    V3
    # * <int> <int> <int>
    #  1     1     2     3
    #  2    13    14    15
    #  3    25    26    27
    #  4    37    38    39
    #  5    49    50    51
    #  6     4     5     6
    #  7    16    17    18
    #  8    28    29    30
    #  9    40    41    42
    # 10    52    53    54
    # 11     7     8     9
    # 12    19    20    21
    # 13    31    32    33
    # 14    43    44    45
    # 15    55    56    57
    # 16    10    11    12
    # 17    22    23    24
    # 18    34    35    36
    # 19    46    47    48
    # 20    58    59    60
    

    【讨论】:

    • 非常感谢!成功了!
    • @Duerna 请查看我更新的帖子。我将正则表达式更改为([A-Z+])([\\d].*$)。原因是我相信原始代码只能处理小数据帧(列号小于10),这是因为即使原始列名超过一位(V11 是@ 987654328@, V12V1, ...)。新的正则表达式可以捕捉到这一点。我还添加了一个包含 12 列的数据框来显示代码的工作原理。
    • 谢谢您,我已经运行了您提供的原始数据的两个代码,并且所有两个代码都可以得到结果显示“A tibble:44,400 x 7”,似乎所有代码都可以处理大数据帧。至于为什么“dt2”显示为“x 7”而不是“x 3”,我认为原因是我的原始数据标题,所有列标题都包含相同的字母:“因子,x,y”,如“因子1, x1,y1,因子2,x2,y2,因子3,x3,y3,..." 。无论如何,非常感谢您的帮助。
    • @Duerna ([A-Z+]) 仅捕获大写字母,因此它不适用于您的真实数据框。我不知道您的标头比您的示例数据框更复杂。我认为对于您的真实数据,我们可以使用([A-Za-z].*) 来捕获任何字母。
    • 谢谢。我会注意描述我的数据框。我尝试使用 ([A-Za-z].*),得到“A tibble: 2,368 x 73”,代码从我的真实数据头中捕获了 75 个中的 73 个。您提供的第一个和第二个代码在我的真实数据上效果更好。
    【解决方案5】:

    这是任意数量列的通用解决方案,使用dplyr

    测试数据data

    # A tibble: 5 x 9
         V1    V2    V3    V4    V5    V6    V7    V8    V9
      <int> <int> <int> <int> <int> <int> <int> <int> <int>
    1     1     2     3     4     5     6     7     8     9
    2    10    11    12    13    14    15    16    17    18
    3    19    20    21    22    23    24    25    26    27
    4    28    29    30    31    32    33    34    35    36
    5    37    38    39    40    41    42    43    44    45
    

    代码:

    for (i in seq(1, ncol(data), by = 3)) {
      if (i == 1) {
        out <- select(data, 1:3)
      } else {
        out <- select(data, i:(i+2)) %>% setNames(names(out)) %>% bind_rows(out, .)
      }
    }
    

    输出out:

    # A tibble: 15 x 3
          V1    V2    V3
       <int> <int> <int>
     1     1     2     3
     2    10    11    12
     3    19    20    21
     4    28    29    30
     5    37    38    39
     6     4     5     6
     7    13    14    15
     8    22    23    24
     9    31    32    33
    10    40    41    42
    11     7     8     9
    12    16    17    18
    13    25    26    27
    14    34    35    36
    15    43    44    45
    

    【讨论】:

    • @Duerna 太棒了!
    猜你喜欢
    • 1970-01-01
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 2018-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多