【问题标题】:constructing an identifier string for each row in data为数据中的每一行构造一个标识符字符串
【发布时间】:2013-07-02 19:46:08
【问题描述】:

我有以下数据:

library(data.table)
d = data.table(a = c(1:3), b = c(2:4))

并希望得到这个结果(以一种适用于任意数量列的方式):

d[, c := paste0('a_', a, '_b_', b)]
d
#   a b       c
#1: 1 2 a_1_b_2
#2: 2 3 a_2_b_3
#3: 3 4 a_3_b_4

以下内容有效,但我希望找到更短更清晰的内容。

d = data.table(a = c(1:3), b = c(2:4))
d[, c := apply(mapply(paste, names(.SD), .SD, MoreArgs = list(sep = "_")),
               1, paste, collapse = "_")]

【问题讨论】:

    标签: string r data.table


    【解决方案1】:

    一种方式,只是稍微干净一点:

    d[, c :=  apply(d, 1, function(x) paste(names(d), x, sep="_", collapse="_")) ]
    
         a b       c
    1: 1 2 a_1_b_2
    2: 2 3 a_2_b_3
    3: 3 4 a_3_b_4
    

    【讨论】:

    • 谢谢,用更少的应用肯定更清晰;当前的 R 高尔夫球冠军 :)
    • 我认为apply 会将d 转换为矩阵。所以,会有副本,效率会降低。
    • @Roland,你是对的,这绝不是有效的。另一种方法是使用by=names(d),但您必须将值拉回paste,或者您必须将paste拉到j的外部
    【解决方案2】:

    这是一种使用do.call('paste') 的方法,但只需要一次调用paste

    我将在列是整数的情况下进行基准测试(因为这似乎是一个更明智的测试用例

    N <- 1e4
    
    d <- setnames(as.data.table(replicate(5, sample(N), simplify = FALSE)), letters[seq_len(5)])
    
    f5 <- function(d){
      l <- length(d)
      o <- c(1L, l + 1L) + rep_len(seq_len(l) -1L, 2L * l)
      do.call('paste',c((c(as.list(names(d)),d))[o],sep='_'))}
    
    
    microbenchmark(f1(d), f2(d),f5(d))
    Unit: milliseconds
      expr       min        lq    median        uq       max neval
     f1(d)  41.51040  43.88348  44.60718  45.29426  52.83682   100
     f2(d) 193.94656 207.20362 210.88062 216.31977 252.11668   100
     f5(d)  30.73359  31.80593  32.09787  32.64103  45.68245   100
    

    【讨论】:

      【解决方案3】:

      为了避免循环遍历行,你可以使用这个:

      do.call(paste, c(lapply(names(d), function(n)paste0(n,"_",d[[n]])), sep="_"))

      基准测试:

      N <- 1e4
      
      d <- data.table(a=runif(N),b=runif(N),c=runif(N),d=runif(N),e=runif(N))
      
      f1 <- function(d)
      {
          do.call(paste, c(lapply(names(d), function(n)paste0(n,"_",d[[n]])), sep="_"))
      }
      
      f2 <- function(d)
      {
          apply(d, 1, function(x) paste(names(d), x, sep="_", collapse="_"))
      }
      
      require(microbenchmark)
      
      microbenchmark(f1(d), f2(d))
      

      注意:f2 灵感来自@Ricardo 的回答。

      结果:

      Unit: milliseconds
        expr      min       lq   median       uq      max neval
       f1(d) 195.8832 213.5017 216.3817 225.4292 254.3549   100
       f2(d) 418.3302 442.0676 451.0714 467.5824 567.7051   100
      

      编辑说明:之前使用N &lt;- 1e3 进行的基准测试在时间上没有太大差异。再次感谢@eddi。

      【讨论】:

      • 嗯,我 +1 是因为它看起来是正确的,但在测试时,我认为您的 f1 并没有按原样工作
      • 哦好吧,你只需要把d[,n]替换成d[[n]]
      • 在我的测试中,它实际上比 Ricardo 或 agstudy 的答案快 2 倍(运行与上面相同的代码,我也尝试了更大的 N)
      • 我认为这个解决方案非常适合寻求速度的人,在这种情况下,我正在寻找更短的表达方式,这种速度差异并不重要;再次感谢
      • 问题中没有指定这个约束(正如我刚刚意识到的那样),但是我实际上最终使用了这个版本,因为我需要在粘贴时保留列类型(我的实际计算是比 OP 更复杂)和 apply 破坏了该信息
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-14
      • 2016-12-02
      • 2014-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多