【问题标题】:Repeat rows of a data.frame [duplicate]重复data.frame的行[重复]
【发布时间】:2012-06-22 16:44:31
【问题描述】:

我想重复 data.frame 的行,每个 N 次。结果应该是一个新的data.frame(带有nrow(new.df) == nrow(old.df) * N),保留了列的数据类型。

N = 2 的示例:

                        A B   C
  A B   C             1 j i 100
1 j i 100     -->     2 j i 100
2 K P 101             3 K P 101
                      4 K P 101

因此,每行重复 2 次,字符仍然是字符,因子仍然是因子,数字仍然是数字,...

我第一次尝试使用 apply:apply(old.df, 2, function(co) rep(co, each = N)),但这一次将我的值转换为字符,我得到:

     A   B   C    
[1,] "j" "i" "100"
[2,] "j" "i" "100"
[3,] "K" "P" "101"
[4,] "K" "P" "101"

【问题讨论】:

    标签: r dataframe rows repeat


    【解决方案1】:
    df <- data.frame(a = 1:2, b = letters[1:2]) 
    df[rep(seq_len(nrow(df)), each = 2), ]
    

    【讨论】:

    • 如果你想改变每行重复的次数,你可以使用n.times &lt;- c(2,4) ; df[rep(seq_len(nrow(df)), n.times),]
    【解决方案2】:

    一个干净的dplyr 解决方案,取自here

    library(dplyr)
    df <- tibble(x = 1:2, y = c("a", "b"))
    df %>% slice(rep(1:n(), each = 2))
    

    【讨论】:

    • 这是 imo 的首选解决方案,因为它可以在管道中干净地工作。
    【解决方案3】:

    有一个可爱的矢量化解决方案,每行仅重复某些行 n 次,例如可以通过在数据框中添加 ntimes 列:

      A B   C ntimes
    1 j i 100      2
    2 K P 101      4
    3 Z Z 102      1
    

    方法:

    df <- data.frame(A=c("j","K","Z"), B=c("i","P","Z"), C=c(100,101,102), ntimes=c(2,4,1))
    df <- as.data.frame(lapply(df, rep, df$ntimes))
    

    结果:

      A B   C ntimes
    1 Z Z 102      1
    2 j i 100      2
    3 j i 100      2
    4 K P 101      4
    5 K P 101      4
    6 K P 101      4
    7 K P 101      4
    

    这与 Josh O'Brien 和 Mark Miller 的方法非常相似:

    df[rep(seq_len(nrow(df)), df$ntimes),]
    

    但是,这种方法看起来有点慢:

    df <- data.frame(A=c("j","K","Z"), B=c("i","P","Z"), C=c(100,101,102), ntimes=c(2000,3000,4000))
    
    microbenchmark::microbenchmark(
      df[rep(seq_len(nrow(df)), df$ntimes),],
      as.data.frame(lapply(df, rep, df$ntimes)),
      times = 10
    )
    

    结果:

    Unit: microseconds
                                          expr      min       lq      mean   median       uq      max neval
       df[rep(seq_len(nrow(df)), df$ntimes), ] 3563.113 3586.873 3683.7790 3613.702 3657.063 4326.757    10
     as.data.frame(lapply(df, rep, df$ntimes))  625.552  654.638  676.4067  668.094  681.929  799.893    10
    

    【讨论】:

    • 我认为这是最通用的解决方案,因为它允许您为每行分配不同数量的复制!我很好奇,有没有办法在 tidyverse 中做到这一点?
    【解决方案4】:

    如果您可以重复整个事情,或者先将其子集然后重复,那么this similar question 可能会有所帮助。再次:

    library(mefa)
    rep(mtcars,10) 
    

    或者干脆

    mefa:::rep.data.frame(mtcars)
    

    【讨论】:

    • 啊哈!另一个出色的 R 函数隐藏在一个名称完全不相关的不起眼的专家包中。我喜欢这种语言!
    【解决方案5】:

    除了@dardisco 提到的mefa::rep.data.frame() 之外,它非常灵活。

    您可以将每一行重复 N 次

    rep(df, each=N)
    

    将整个数据帧重复 N 次(想一想:就像回收矢量化参数时一样)

    rep(df, times=N)
    

    mefa 点赞!直到现在我才听说过它,我不得不编写手动代码来做到这一点。

    【讨论】:

      【解决方案6】:

      作为参考和添加引用 mefa 的答案,如果您不想包含整个包,可能值得看看 mefa::rep.data.frame() 的实现:

      > data <- data.frame(a=letters[1:3], b=letters[4:6])
      > data
        a b
      1 a d
      2 b e
      3 c f
      > as.data.frame(lapply(data, rep, 2))
        a b
      1 a d
      2 b e
      3 c f
      4 a d
      5 b e
      6 c f
      

      【讨论】:

        【解决方案7】:

        rep.row 函数似乎有时会为列创建列表,这会导致内存崩溃。我写了以下似乎效果很好:

        library(plyr)
        rep.row <- function(r, n){
          colwise(function(x) rep(x, n))(r)
        }
        

        【讨论】:

          【解决方案8】:

          我的解决方案类似于mefa:::rep.data.frame,但速度更快,并且关心行名:

          rep.data.frame <- function(x, times) {
              rnames <- attr(x, "row.names")
              x <- lapply(x, rep.int, times = times)
              class(x) <- "data.frame"
              if (!is.numeric(rnames))
                  attr(x, "row.names") <- make.unique(rep.int(rnames, times))
              else
                  attr(x, "row.names") <- .set_row_names(length(rnames) * times)
              x
          }
          

          比较解决方案:

          library(Lahman)
          library(microbenchmark)
          microbenchmark(
              mefa:::rep.data.frame(Batting, 10),
              rep.data.frame(Batting, 10),
              Batting[rep.int(seq_len(nrow(Batting)), 10), ],
              times = 10
          )
          #> Unit: milliseconds
          #>                                            expr       min       lq     mean   median        uq       max neval cld
          #>              mefa:::rep.data.frame(Batting, 10) 127.77786 135.3480 198.0240 148.1749  278.1066  356.3210    10  a 
          #>                     rep.data.frame(Batting, 10)  79.70335  82.8165 134.0974  87.2587  191.1713  307.4567    10  a 
          #>  Batting[rep.int(seq_len(nrow(Batting)), 10), ] 895.73750 922.7059 981.8891 956.3463 1018.2411 1127.3927    10   b
          

          【讨论】:

            【解决方案9】:

            尝试使用例如

            N=2
            rep(1:4, each = N) 
            

            作为索引

            【讨论】:

              【解决方案10】:

              另一种方法是首先获取行索引,附加 df 的额外副本,然后按索引排序:

              df$index = 1:nrow(df)
              df = rbind(df,df)
              df = df[order(df$index),][,-ncol(df)]
              

              虽然其他解决方案可能更短,但这种方法在某些情况下可能更有利。

              【讨论】:

                猜你喜欢
                • 2016-11-09
                • 2016-05-18
                • 1970-01-01
                • 1970-01-01
                • 2016-05-03
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多