【问题标题】:reshaping columns in a data frame重塑数据框中的列
【发布时间】:2025-12-09 08:05:02
【问题描述】:

假设我有以下数据框:

ID<-c(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3, 4,4,4,4,4,5,5,5,5,5)
Score<- sample(1:20, 25, replace=TRUE)
days<-rep(c("Mon", "Tue", "Wed", "Thu", "Fri"), times=5)
t<-cbind(ID, Score, days)

我想重新调整它,以便新列是 ID 和实际的工作日名称(意味着 6 列),并且分数值根据它们的 ID 和日期名称分布。像这样的:

我发现 reshape 包可能会做。尝试过(熔化和铸造),但它没有产生我想要的结果,但类似于这篇文章:Melt data for one column

【问题讨论】:

  • 您不需要任何外部包来执行此操作。 R 有一个内置的reshape 命令可以做到这一点。此外,在发布使用随机抽样的示例时,您应该包含 set.seed(&lt;somenumber&gt;) 以便该项目可重现。

标签: r reshape


【解决方案1】:

使用内置 reshape 命令的基本 R 解决方案。

set.seed(12345)
t <- data.frame(id = c(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5),
                score = sample(x = 1:20,size = 25,replace = TRUE),
                days = rep(x = c("Mon","Tue","Wed","Thu","Fri"),times = 5))

t.wide <- reshape(data = t,
                  v.names = "score",
                  timevar = "days",
                  idvar = "id",
                  direction = "wide")
names(t.wide) <- gsub(pattern = "score.",replacement = "",x = names(t.wide),fixed = TRUE)
t.wide
   id Mon Tue Wed Thu Fri
1   1  15  18  16  18  10
6   2   4   7  11  15  20
11  3   1   4  15   1   8
16  4  10   8   9   4  20
21  5  10   7  20  15  13

【讨论】:

    【解决方案2】:

    您可以使用 reshape2 来执行此操作,但您需要 data.frame 来执行此操作。使用 cbind 生成一个矩阵。 (在这种情况下,将所有数值变量转换为字符,因为矩阵只能保存一种数据类型)。

    我已更改您的代码以生成一个数据帧,该数据帧已经是长格式(每次观察一行)。

    set.seed(123)
    ID<-c(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3, 4,4,4,4,4,5,5,5,5,5)
    Score<- sample(1:20, 25, replace=TRUE)
    days<-rep(c("Mon", "Tue", "Wed", "Thu", "Fri"), times=5)
    dat<-data.frame(ID, Score, days)
    

    使用reshape2 将其更改为宽则非常简单:

    library(reshape2)
    
    res <- dcast(ID~days,value.var="Score",data=dat)
    
    
    > res
      ID Fri Mon Thu Tue Wed
    1  1  16   3   2  12   6
    2  2  19  13  12   7  19
    3  3  19  19  17   8  15
    4  4  15   3   8   1  20
    5  5   3  11  18   8  15
    

    【讨论】:

      【解决方案3】:

      如果您的数据完整(每个 ID 的天数相同),您也可以使用 unstack

      这是一个示例(使用 TARehman 回答中的数据):

      unstack(t, score ~ days)
      #   Fri Mon Thu Tue Wed
      # 1  10  15  18  18  16
      # 2  20   4  15   7  11
      # 3   8   1   1   4  15
      # 4  20  10   4   8   9
      # 5  13  10  15   7  20
      

      这是对列排序和在 ID 列中添加的清理:

      cbind(ID = unique(t$id), unstack(t, score ~ days)[c("Mon", "Tue", "Wed", "Thu", "Fri")])
      ##   ID Mon Tue Wed Thu Fri
      ## 1  1  15  18  16  18  10
      ## 2  2   4   7  11  15  20
      ## 3  3   1   4  15   1   8
      ## 4  4  10   8   9   4  20
      ## 5  5  10   7  20  15  13
      

      【讨论】:

        【解决方案4】:

        我宁愿改用新的 tidyr 包,也不会像这样使用 dplyr:

        library(dplyr)
        library(tidyr)
        
        tdf<-as.data.frame(t) %>%
          mutate(Score=as.numeric(Score)) %>%
          spread(days,Score, fill=NA)
        
        glimpse(tdf)
        

        HTH

        【讨论】:

        • @boshek 我无法将 tidyr 安装到我的工作计算机(访问受限)。稍后会在家里尝试一下,然后告诉你。谢谢
        • @Heroka 它本身没什么问题。但是自从我转向一个 tidyr 和 dplyr 做事以来,我注意到我的工作流程的效率和代码可读性有所提高。管道和聚集/传播的结合使用可以使代码更好读更易读。
        • 你是如何在这里使用 dplyr 的?
        • @DavidArenburg 因为没有管道它不是一个完整的答案!
        • 这会返回所有因子列,考虑到它只是数字,这并不是真正可取的。
        【解决方案5】:

        使用splitstackshape的另一种选择

        library(splitstackshape)
        data = data.frame(t)
        out = setnames(cSplit(setDT(data)[, .(x = toString(Score)), by = ID], 
                       'x', ','), c('ID', unique(days)))
        
        #> out
        #   ID Mon Tue Wed Thu Fri
        #1:  1   8  14  11   5  10
        #2:  2  16   1   4  14   8
        #3:  3   8  18  19  13   3
        #4:  4  16   9  19  16   6
        #5:  5   7   2   1   2  13
        

        【讨论】:

          【解决方案6】:

          在 dplyr 和 tidyr 包中,使用 spread 来实现以下目标:

          library(dplyr)
          library(tidyr)
          t <- tbl_df(as.data.frame(t))
          t %>% spread(days, Score, ID)
          

          你会得到以下输出:

                ID    Fri    Mon    Thu    Tue    Wed
            (fctr) (fctr) (fctr) (fctr) (fctr) (fctr)
          1      1     10     10     18     17     10
          2      2     18     11     14      3     16
          3      3     11     13      9     15     17
          4      4     13     13     16     17     11
          5      5      7     14      9     15     20
          

          【讨论】:

          • 为什么不简单地library(tidyr); spread(data.frame(t), 'days', 'Score')
          • 这并不能解决数据创建中的问题:所有列都显示为数字,但都是因子。
          • @VeerendraGadekar,这也有效,我只是喜欢我所有的数据框作为表格数据框。所以是的 - 你不需要 dplyr 库。此外,可以通过将因子更改为数字来轻松更改变量类型 - 例如:as.numeric.factor &lt;- function(x) {as.numeric(levels(x))[x]} [*.com/questions/3418128/…