【问题标题】:Reshape data frame with different column lengths into two columns replicating column ID将具有不同列长度的数据框重塑为复制列 ID 的两列
【发布时间】:2015-06-11 13:21:57
【问题描述】:

我有以下数据框,具有不同的行长:

myvar <- as.data.frame(rbind(c("Walter","NA","NA","NA","NA"),
                             c("Walter","NA","NA","NA","NA"),
                             c("Walter","Jesse","NA","NA","NA"),
                             c("Gus","Tuco","Mike","NA","NA"), 
                             c("Gus","Mike","Hank","Saul","Flynn")))
ID <- as.factor(c(1:5))   
data.frame(ID,myvar)

ID     V1    V2   V3   V4    V5
 1 Walter    NA   NA   NA    NA
 2 Walter    NA   NA   NA    NA
 3 Walter Jesse   NA   NA    NA
 4    Gus  Tuco Mike   NA    NA
 5    Gus  Mike Hank Saul Flynn

我的目标是将此数据框转换为两列数据框。第一列是 ID,另一列是角色名称。请注意,ID 必须与字符最初放置的行相对应。我期待以下结果:

ID      V
1  Walter    
2  Walter
3  Walter
3  Jesse
4  Gus
4  Tuco
4  Mike
5  Gus
5  Mike
5  Hank
5  Saul
5  Flynn

我试过 dcast {reshape2} 但它没有返回我需要的东西。值得注意的是,我的原始数据框很大。有小费吗?干杯。

【问题讨论】:

  • dcast 和你想要的相反,就是从长到宽
  • 不要使用 data.frame(cbind(,,,)) 或 data.frame(rbind)。坏事会发生。
  • 对不起 NA 作为字符。我的错。不过谢谢大家的回答!

标签: r multiple-columns reshape


【解决方案1】:

由于您正在考虑大量数据,

时间性能很重要,即使事后排序也可能需要很长时间

这是我的解决方案。你最好使用data.table,但在这里我将使用reshape2

  1. 第一个解决方案
myvar <- as.data.frame(rbind(c("Walter","NA","NA","NA","NA"),
                             c("Walter","NA","NA","NA","NA"),
                             c("Walter","Jesse","NA","NA","NA"),
                             c("Gus","Tuco","Mike","NA","NA"), 
                             c("Gus","Mike","Hank","Saul","Flynn")))
ID <- as.factor(c(1:5))   

dat = data.frame(ID,myvar)
dat[] <- lapply(dat, function(x) {x[x=="NA"]=NA; x})

str(dat$V5)

library(dplyr)
library(reshape2)

dat2 <- melt(dat, id.vars="ID", measure.vars = paste0("V", 1:5), na.rm=TRUE)
dat2
dat2[, c('ID', 'value')]
  1. 第二种解决方案需要一些预处理。对于海量数据,我会推荐data.table
datB <- t(dat)
datB
colnames(datB) <- datB["ID", ]
datB <- datB[-1,]

melt(datB, measure.vars = 1:5, na.rm=TRUE)[, c('Var2', 'value')]

之后不需要排序

【讨论】:

    【解决方案2】:

    修复您的"NA",使它们实际上是NA

    mydf[mydf == "NA"] <- NA
    

    使用一些子集来一口气完成所有操作:

    data.frame(ID=mydf$ID[row(mydf[-1])[!is.na(mydf[-1])]], V=mydf[-1][!is.na(mydf[-1])])
    
    #   ID      V
    #1   1 Walter
    #2   2 Walter
    #3   3 Walter
    #4   4    Gus
    #5   5    Gus
    #6   3  Jesse
    #7   4   Tuco
    #8   5   Mike
    #9   4   Mike
    #10  5   Hank
    #11  5   Saul
    #12  5  Flynn
    

    或者在基础 R 中更具可读性:

    sel <- which(!is.na(mydf[-1]), arr.ind=TRUE)
    data.frame(ID=mydf$ID[sel[,1]], V=mydf[-1][sel])
    

    【讨论】:

      【解决方案3】:
      myvar <- as.data.frame(rbind(c("Walter","NA","NA","NA","NA"),
                                   c("Walter","NA","NA","NA","NA"),
                                   c("Walter","Jesse","NA","NA","NA"),
                                   c("Gus","Tuco","Mike","NA","NA"), 
                                   c("Gus","Mike","Hank","Saul","Flynn")))
      ID <- as.factor(c(1:5))   
      df <- data.frame(ID, myvar)
      

      使用基础重塑。 (我正在将您的 "NA" 字符串转换为 NA 您可能不必这样做,这只是由于您创建此示例的方式)

      df[df == 'NA'] <- NA
      na.omit(reshape(df, direction = 'long', varying = list(2:6))[, c('ID','V1')])
      
      #     ID     V1
      # 1.1  1 Walter
      # 2.1  2 Walter
      # 3.1  3 Walter
      # 4.1  4    Gus
      # 5.1  5    Gus
      # 3.2  3  Jesse
      # 4.2  4   Tuco
      # 5.2  5   Mike
      # 4.3  4   Mike
      # 5.3  5   Hank
      # 5.4  5   Saul
      # 5.5  5  Flynn
      

      或使用reshape2

      library('reshape2')
      ## na.omit(melt(df, id.vars = 'ID')[, c('ID','value')])
      
      ## or better yet as ananda suggests:
      melt(df, id.vars = 'ID', na.rm = TRUE)[, c('ID','value')]
      
      #    ID  value
      # 1   1 Walter
      # 2   2 Walter
      # 3   3 Walter
      # 4   4    Gus
      # 5   5    Gus
      # 8   3  Jesse
      # 9   4   Tuco
      # 10  5   Mike
      # 14  4   Mike
      # 15  5   Hank
      # 20  5   Saul
      # 25  5  Flynn
      

      您会收到警告,表明列上的因子水平不一样,但这很好。

      【讨论】:

      • 我会更改参数以包含 stringsAsFactors=FALSE
      • melt 有一个na.rm 参数,因此您不需要使用na.omit。但是,由于数据的创建方式,您需要将它们变成真实的NA(您已经这样做了)。
      【解决方案4】:

      使用tidyr

      library("tidyr")
      
      myvar <- as.data.frame(rbind(c("Walter","NA","NA","NA","NA"),
                                   c("Walter","NA","NA","NA","NA"),
                                   c("Walter","Jesse","NA","NA","NA"),
                                   c("Gus","Tuco","Mike","NA","NA"), 
                                   c("Gus","Mike","Hank","Saul","Flynn")))
      ID <- as.factor(c(1:5))   
      
      myvar <- data.frame(ID,myvar)
      
      myvar %>% 
          gather(ID, Name, V1:V5 ) %>%
          select(ID, value) %>%
          filter(value != "NA")
      

      如果您的 NA 编码为 NA 而不是 "NA",那么我们实际上可以在 gather 中使用 na.rm = TRUE 选项。例如:

      myvar[myvar == "NA"] <- NA
      myvar %>% 
          gather(ID, Name, V1:V5, na.rm = TRUE ) %>%
          select(ID, value)
      

      给予

         ID  value
      1   1 Walter
      2   2 Walter
      3   3 Walter
      4   4    Gus
      5   5    Gus
      6   3  Jesse
      7   4   Tuco
      8   5   Mike
      9   4   Mike
      10  5   Hank
      11  5   Saul
      12  5  Flynn
      

      【讨论】:

      • 这个解决方案和@rawr的解决方案的关键是我们在连接名称列后删除NA,同时保留行号。
      • 我不断收到错误消息:eval 中的错误(expr、envir、enclos):找不到对象“值”
      • 您的tidyr 是什么版本?在tidyr 的某些版本中,新的名称分配存在一些问题,不要运行最后一个select 命令来查看剩下的列名。
      • 现已弃用 - 改用 pivot_longer
      【解决方案5】:

      你可以使用unlist

       res <- subset(data.frame(ID,value=unlist(myvar[-1], 
                                    use.names=FALSE)), value!='NA')
       res
       #   ID  value
       #1   1 Walter
       #2   2 Walter
       #3   3 Walter
       #4   4    Gus
       #5   5    Gus
       #6   3  Jesse
       #7   4   Tuco
       #8   5   Mike
       #9   4   Mike
       #10  5   Hank
       #11  5   Saul
       #12  5  Flynn
      

      注意: NAs 是数据集中的“字符”元素,最好不要使用引号创建它,这样它将是真正的 NA,我们可以通过 na.omit 将其删除, is.nacomplete.cases

      数据

      myvar <- data.frame(ID,myvar)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多