【问题标题】:Reshape data based on column in dataframe根据数据框中的列重塑数据
【发布时间】:2011-03-29 14:41:55
【问题描述】:

我需要采用以下格式的data.frame:

  id1 id2 mean start end
1   A   D    4    12  15
2   B   E    5    14  15
3   C   F    6     8  10

并根据start - end 的差异生成重复行。例如,第一行需要 3 行,第二行需要 1 行,第三行需要 2 行。开始和结束字段应在最终 data.frame 中按顺序排列。这个 data.frame 的最终结果应该是:

   id1 id2 mean start end
1    A   D    4    12  13
2    A   D    4    13  14
3    A   D    4    14  15
21   B   E    5    14  15
31   C   F    6     8   9
32   C   F    6     9  10

我已经编写了这个函数,它可以工作,但不是用非常 R'esque 的代码编写的:

dupData <- function(df){
    diff <- abs(df$start - df$end)
    ret <- {}

    #Expand our dataframe into the appropriate number of rows.
    for (i in 1:nrow(df)){
        for (j in 1:diff[i]){
            ret <- rbind(ret, df[i,])
        } 
    }

    #If matching ID1 and ID2, generate a sequential ordering of start & end dates
    for (k in 2:nrow(ret) - 1) {
        if ( ret[k,1] == ret[k + 1, 1] & ret[k, 2] == ret[k, 2]  ){ 
            ret[k, 5] <- ret[k, 4] + 1
            ret[k + 1, 4] <- ret[k, 5]  
        }
    }
    return(ret)
}

有人对如何优化此代码有建议吗? plyr中是否有可能适用的功能?

#sample daters
df <- data.frame(id1 = c("A", "B", "C")
        , id2 = c("D", "E", "F")
        , mean = c(4,5,6)  
        , start = c(12,14,8)
        , end = c(15, 15, 10)
)

【问题讨论】:

    标签: r reshape plyr


    【解决方案1】:

    可能有更通用的方法可以做到这一点,但下面使用rbind.fill

    cbind(df[rep(1:nrow(df), times = apply(df[,4:5], 1, diff)), 1:3],
          rbind.fill(apply(df[,4:5], 1, function(x)
                           data.frame(start = x[1]:(x[2]-1), end = (x[1]+1):x[2]))))
    
    
    ##     id1 id2 mean start end
    ## 1     A   D    4    12  13
    ## 1.1   A   D    4    13  14
    ## 1.2   A   D    4    14  15
    ## 2     B   E    5    14  15
    ## 3     C   F    6     8   9
    ## 3.1   C   F    6     9  10
    

    【讨论】:

    • 那是一些非常漂亮的工作,我很感激。使用 100k 行数据帧以适当的格式输出数据大约需要 1.5 分钟。谢谢!
    【解决方案2】:

    survival 包的survSplit 函数在这些方面做了一些事情,尽管它有更多的选项(例如指定切割时间)。您也许可以使用它,或者查看它的代码,看看您是否可以更好地实现您的简化版本。

    【讨论】:

      【解决方案3】:

      毫无疑问,这不是迟到总比没有好的时代之一,但我遇到了类似的问题并想出了这个......

      library(plyr)
      ddply(df, c("id1", "id2", "mean", "start", "end"), summarise,
                          sq=seq(1:(end-start)))
      

      【讨论】:

        【解决方案4】:

        两个替代方案,许多年后,使用当今流行的 data.tabletidyverse 包提供替代方案:

        选项 1:

        library(data.table)
        setDT(mydf)[, list(mean, start = start:(end-1)), .(id1, id2)][, end := start + 1][]
           id1 id2 mean start end
        1:   A   D    4    12  13
        2:   A   D    4    13  14
        3:   A   D    4    14  15
        4:   B   E    5    14  15
        5:   C   F    6     8   9
        6:   C   F    6     9  10
        

        选项 2:

        library(tidyverse)
        mydf %>% 
          group_by(id1, id2, mean) %>% 
          summarise(start = list(start:(end-1))) %>% 
          unnest(start) %>% 
          mutate(end = start+1)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-12-27
          • 1970-01-01
          • 1970-01-01
          • 2013-02-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多