【问题标题】:Repeat each row of data.frame the number of times specified in a column重复每一行 data.frame 列中指定的次数
【发布时间】:2011-02-23 02:22:40
【问题描述】:
df <- data.frame(var1 = c('a', 'b', 'c'), var2 = c('d', 'e', 'f'),
                 freq = 1:3)

在上面data.frame的前两列中展开每一行的最简单方法是什么,以便每一行重复'freq'列中指定的次数?

换句话说,从这里开始:

df
  var1 var2 freq
1    a    d    1
2    b    e    2
3    c    f    3

到这里:

df.expanded
  var1 var2
1    a    d
2    b    e
3    b    e
4    c    f
5    c    f
6    c    f

【问题讨论】:

    标签: r dataframe replicate


    【解决方案1】:

    这里有一个解决方案:

    df.expanded <- df[rep(row.names(df), df$freq), 1:2]
    

    结果:

        var1 var2
    1      a    d
    2      b    e
    2.1    b    e
    3      c    f
    3.1    c    f
    3.2    c    f
    

    【讨论】:

    • 太棒了!我总是忘记你可以那样使用方括号。我一直在考虑索引只是为了子集或重新排序。我有另一种解决方案,它远没有那么优雅,而且毫无疑问效率低下。无论如何,我可能会发布,以便其他人可以比较。
    • 对于大的data.frame,更有效的是将row.names(df)替换为seq.int(1,nrow(df))seq_len(nrow(df))
    • 这对于一个大数据框非常有效——150 万行,5 列,速度非常快。谢谢!
    • 1:2 硬编码此示例的解决方案,1:ncol(df) 将适用于任意数据帧。
    【解决方案2】:

    tidyverse 中的老问题,新动词:

    library(tidyr) # version >= 0.8.0
    df <- data.frame(var1=c('a', 'b', 'c'), var2=c('d', 'e', 'f'), freq=1:3)
    df %>% 
      uncount(freq)
    
        var1 var2
    1      a    d
    2      b    e
    2.1    b    e
    3      c    f
    3.1    c    f
    3.2    c    f
    

    【讨论】:

      【解决方案3】:

      使用splitstackshape 包中的expandRows()

      library(splitstackshape)
      expandRows(df, "freq")
      

      语法简单,速度非常快,适用于data.framedata.table

      结果:

          var1 var2
      1      a    d
      2      b    e
      2.1    b    e
      3      c    f
      3.1    c    f
      3.2    c    f
      

      【讨论】:

        【解决方案4】:

        @neilfws 的解决方案适用于data.frames,但不适用于data.tables,因为它们缺少row.names 属性。这种方法适用于以下两种情况:

        df.expanded <- df[rep(seq(nrow(df)), df$freq), 1:2]
        

        data.table 的代码更简洁:

        # convert to data.table by reference
        setDT(df)
        df.expanded <- df[rep(seq(.N), freq), !"freq"]
        

        【讨论】:

        • 另一种选择:df[rep(seq(.N), freq)][, freq := NULL]
        • 另一种选择df[rep(1:.N, freq)][, freq:=NULL]
        【解决方案5】:

        我知道情况并非如此,但如果您需要保留原始频率列,您可以使用另一个 tidyverse 方法和 rep

        library(purrr)
        
        df <- data.frame(var1 = c('a', 'b', 'c'), var2 = c('d', 'e', 'f'), freq = 1:3)
        
        df %>% 
          map_df(., rep, .$freq)
        #> # A tibble: 6 x 3
        #>   var1  var2   freq
        #>   <fct> <fct> <int>
        #> 1 a     d         1
        #> 2 b     e         2
        #> 3 b     e         2
        #> 4 c     f         3
        #> 5 c     f         3
        #> 6 c     f         3
        

        reprex package (v0.3.0) 于 2019 年 12 月 21 日创建

        【讨论】:

        • 或者在uncount()中使用.remove = FALSE
        【解决方案6】:

        另一个dplyr替代slice,我们重复每个行号freq

        library(dplyr)
        
        df %>%  
          slice(rep(seq_len(n()), freq)) %>% 
          select(-freq)
        
        #  var1 var2
        #1    a    d
        #2    b    e
        #3    b    e
        #4    c    f
        #5    c    f
        #6    c    f
        

        seq_len(n()) 部分可以替换为以下任何内容。

        df %>% slice(rep(1:nrow(df), freq)) %>% select(-freq)
        #Or
        df %>% slice(rep(row_number(), freq)) %>% select(-freq)
        #Or
        df %>% slice(rep(seq_len(nrow(.)), freq)) %>% select(-freq)
        

        【讨论】:

          【解决方案7】:

          如果您必须在非常大的 data.frames 上执行此操作,我建议将其转换为 data.table 并使用以下内容,它应该运行得更快:

          library(data.table)
          dt <- data.table(df)
          dt.expanded <- dt[ ,list(freq=rep(1,freq)),by=c("var1","var2")]
          dt.expanded[ ,freq := NULL]
          dt.expanded
          

          看看这个解决方案有多快:

          df <- data.frame(var1=1:2e3, var2=1:2e3, freq=1:2e3)
          system.time(df.exp <- df[rep(row.names(df), df$freq), 1:2])
          ##    user  system elapsed 
          ##    4.57    0.00    4.56
          dt <- data.table(df)
          system.time(dt.expanded <- dt[ ,list(freq=rep(1,freq)),by=c("var1","var2")])
          ##    user  system elapsed 
          ##    0.05    0.01    0.06
          

          【讨论】:

          • 我收到一个错误:Error in rep(1, freq) : invalid 'times' argument。鉴于这个问题已经有一个 data.table 答案,您可能想要描述您的方法有何不同,或者何时它比当前的 data.table 答案更好。或者,如果没有重大区别,您可以将其作为评论添加到现有答案中。
          • @SamFirke:感谢您的评论。奇怪,我刚刚又试了一次,我没有收到这样的错误。您是否使用来自 OP 问题的原始 df?我的答案更好,因为另一个答案是通过使用data.frame 语法来滥用data.table 包,请参阅data.table 的常见问题解答:“按数字而不是名称来引用列通常是不好的做法。”
          • 感谢您的解释。您的代码适用于 OP 发布的示例 df,但是当我尝试在更大的 data.frame 上对此进行基准测试时,我得到了那个错误。我使用的 data.frame 是:set.seed(1) dfbig &lt;- data.frame(var1=sample(letters, 1000, replace = TRUE), var2=sample(LETTERS, 1000, replace = TRUE), freq=sample(1:10, 1000, replace = TRUE)) 在微小的 data.frame 上,基本答案在我的基准测试中表现良好,只是不能很好地扩展到更大的 data.frame。其他三个答案使用这个更大的 data.frame 成功运行。
          • @SamFirke:这确实很奇怪,它也应该在那里工作,我不知道为什么它不工作。你想从中提出一个问题还是我应该?
          • 好主意。你可以吗?我不知道data.table 语法,所以我不应该是判断答案的人。
          【解决方案8】:

          另一种可能是使用tidyr::expand

          library(dplyr)
          library(tidyr)
          
          df %>% group_by_at(vars(-freq)) %>% expand(temp = 1:freq) %>% select(-temp)
          
          #> # A tibble: 6 x 2
          #> # Groups:   var1, var2 [3]
          #>   var1  var2 
          #>   <fct> <fct>
          #> 1 a     d    
          #> 2 b     e    
          #> 3 b     e    
          #> 4 c     f    
          #> 5 c     f    
          #> 6 c     f
          

          vonjd's answer的单行版本:

          library(data.table)
          
          setDT(df)[ ,list(freq=rep(1,freq)),by=c("var1","var2")][ ,freq := NULL][]
          
          #>    var1 var2
          #> 1:    a    d
          #> 2:    b    e
          #> 3:    b    e
          #> 4:    c    f
          #> 5:    c    f
          #> 6:    c    f
          

          reprex package (v0.2.1) 于 2019 年 5 月 21 日创建

          【讨论】:

            【解决方案9】:

            事实上。使用向量和索引的方法。我们也可以达到同样的效果,而且更容易理解:

            rawdata <- data.frame('time' = 1:3, 
                       'x1' = 4:6,
                       'x2' = 7:9,
                       'x3' = 10:12)
            
            rawdata[rep(1, time=2), ] %>% remove_rownames()
            #  time x1 x2 x3
            # 1    1  4  7 10
            # 2    1  4  7 10
            
            
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-03-07
              • 1970-01-01
              • 2020-12-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-01-25
              • 1970-01-01
              相关资源
              最近更新 更多