【问题标题】:Better/faster way to concatenate two columns after removing duplicate values in one?将重复值合二为一后连接两列的更好/更快的方法?
【发布时间】:2016-01-06 21:37:34
【问题描述】:

我有一个包含两列的数据框,最后一列有重复:

 #reproducible data
    my.df <- data.frame(nr = paste(1:6,1,sep="_"),
                        text = c("aa","bb","aa",NA,"bb","xxxx"))
   nr text
1 1_1   aa
2 2_1   bb
3 3_1   aa
4 4_1 <NA>
5 5_1   bb
6 6_1 xxxx

我想对第一列中的值进行分组,然后添加第二列的值。我找到了一种方法来做到这一点:

apply(aggregate(nr~text, my.df, FUN=function(x) paste0(x, collapse = "/"))[,c(2,1)],1,FUN=function(x) paste(x[1],x[2], sep = ": "))

这给出了:

"1_1/3_1: aa" "2_1/5_1: bb" "6_1: xxxx"

这是我想要的结果,但代码似乎很长。我有一种感觉,一定有更好的,也许也更快的方法来做到这一点?

哦,是的,应该从结果中删除 NA。

编辑:

感谢您的所有回答。我认为会有比我自己的解决方案更简单的解决方案,但显然没有。可读性(恕我直言)非常主观,所以我做了一个基准测试:

microbenchmark(RHA(my.df),Heroka_DT(my.df),Heroka_Base(my.df),Jubbles(my.df),times=100L)

Unit: milliseconds
               expr       min        lq      mean    median        uq
         RHA(my.df)  9.116587  9.315988  9.662611  9.572361 10.036792
   Heroka_DT(my.df) 12.148374 12.448035 13.009290 12.766685 13.475480
 Heroka_Base(my.df)  2.947448  6.910890  7.475239  7.172847  7.614657
     Jubbles(my.df) 16.615067 40.609642 42.265267 41.799625 43.056632
      max neval
 10.78943   100
 21.12477   100
 15.97665   100
 61.68414   100

在这种情况下,基础解决方案显然比其他解决方案更快。由于 Heroka 有最短和最快的解决方案,我会接受他的回答。

【问题讨论】:

    标签: r string performance


    【解决方案1】:

    您可以使用 data.table,它更短一些并且(恕我直言)更具可读性:

    library(data.table)
    
    res <- setDT(my.df)[!is.na(text),.(output=sprintf("%s: %s",paste(nr,collapse="/"),text)),text][,output]
    res
    
    > res
    [1] "1_1/3_1: aa" "2_1/5_1: bb" "6_1: xxxx"  
    

    我们可以在 base-R 中做一些非常相似的事情:

    sapply(split(my.df, my.df$text),function(x){with(x, sprintf("%s: %s",paste(nr, collapse="/"),text[1]))})
    
               aa            bb          xxxx 
    "1_1/3_1: aa" "2_1/5_1: bb"   "6_1: xxxx" 
    

    【讨论】:

    • by = sapply + split,所以你可以这样做by(my.df, my.df$text,function(x)...
    【解决方案2】:

    我喜欢 Jubbles dplyr 方法,但是,我认为使用非常有用的 dplyr::do() 函数可以使其更具可读性。

    my.df %>%
      group_by(text) %>%
      do(new_nr = paste(.$nr, collapse = "/")) %>%
      do(done = paste(.$new_nr, .$text, sep = ": "))
    

    关于dplyr::do() 函数的注释。当它不是管道链的一部分时,它需要两个参数,数据和一个函数,即do(.data, .fun)。但是,当您使用管道时,数据会像往常一样自动放置在第一个参数中。但是,与其他 dplyr 函数不同,您必须引用已传递给 do() 的数据。您可以像往常一样使用$ 来执行此操作,但是在数据名称中,您会将传递给do 的数据称为.,因此.$。换句话说,do() 没有采用其他dplyr 函数采用的非标准评估方法。这当然是有目的的,因为它增加了do() 函数的功能。例如,它允许您拨打lm

    【讨论】:

    • 您的代码出错:Error in do_.tbl_dt(.data, .dots = lazyeval::lazy_dots(...)) : argument ".f" is missing, with no default
    • @RHA,嗯...对我来说很好用。您是否加载了dplyr 库?我相信错误消息表明do 函数缺少用于评估数据的函数。但是,查看代码显然并非如此。对do 的每次调用都包含对paste 的调用。为了调试,有时放弃管道链很方便。例如d &lt;- group_by(my.df, text) ; d &lt;- do(d, new_nr = paste(d$nr, collapse = "/")) 等......让我知道你发现了什么。
    【解决方案3】:

    data.table 更易读(IMO),但不短:)

    # dplyr_0.4.3
    library(dplyr)
    
    (my.df %>%
    filter(!(is.na(text))) %>%
    group_by(text) %>%
    summarize(my.nrs = paste(nr, collapse = "/")) %>%
    ungroup() %>%
    mutate(res = paste(my.nrs, text, sep = ": ")))$res
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-27
      • 2020-03-22
      • 1970-01-01
      • 1970-01-01
      • 2020-08-03
      • 1970-01-01
      • 2020-03-03
      • 1970-01-01
      相关资源
      最近更新 更多