【问题标题】:Cumulatively paste (concatenate) values grouped by another variable累积粘贴(连接)由另一个变量分组的值
【发布时间】:2021-06-17 09:01:00
【问题描述】:

我在处理 R 中的数据框时遇到问题。我想根据另一列中单元格的值将不同行中的单元格内容粘贴在一起。我的问题是我希望逐步(累积)打印输出。输出向量必须与输入向量具有相同的长度。 这是一个类似于我正在处理的样本表:

id <- c("a", "a", "a", "b", "b", "b")
content <- c("A", "B", "A", "B", "C", "B")
(testdf <- data.frame(id, content, stringsAsFactors=FALSE))
#  id content
#1  a       A
#2  a       B
#3  a       A
#4  b       B
#5  b       C
#6  b       B

这是我希望结果看起来像:

result <- c("A", "A B", "A B A", "B", "B C", "B C B") 
result

#[1] "A"     "A B"   "A B A" "B"     "B C"   "B C B"

我不需要这样的东西:

ddply(testdf, .(id), summarize, content_concatenated = paste(content, collapse = " "))

#  id content_concatenated
#1  a                A B A
#2  b                B C B

【问题讨论】:

  • 您想要“累积粘贴”之类的东西。你可以使用Reduceave(as.character(testdf$content), testdf$id, FUN = function(x) Reduce(paste, x, acc = T))
  • @alexis_laz,这是评论框,不是答案框:-)
  • @alexis_laz:太好了!有效!多谢!万事如意
  • @AnandaMahto :我倾向于将其视为“它必须在某处,但我太懒惰搜索”框:P
  • @alexis_laz,但人们不会阅读 cmets 来寻找答案。因此,除非您要做找到要标记的重复项的工作,否则如果您花 10 秒时间发布答案,让 OP 接受它,并清楚地表明这是一个已解决的问题,这对社区会更有帮助....至少这是我的观点。

标签: r dataframe


【解决方案1】:

对于累积函数,我推荐runner 包和runner 函数,它可以在累积窗口上应用任何算法。它在速度方面无法与@alexis_laz 解决方案竞争,但如果需要特定大小的窗口、滞后或取决于日期的窗口 - 我建议使用 runner。


id <- c("a", "a", "a", "b", "b", "b")
content <- c("A", "B", "A", "B", "C", "B")
testdf <- data.frame(id, content, stringsAsFactors=FALSE)

library(runner)
library(dplyr)
testdf %>%
  group_by(id) %>%
  mutate(
    result = runner(x = content, 
                    f = function(x) paste(x, collapse = " "),
                    type = "character")) # specify output type - by default numeric

更多信息请访问documentationvignettes

【讨论】:

    【解决方案2】:

    使用dplyrpurrr 的一个选项可能是:

    testdf %>%
     group_by(id) %>%
     transmute(content_concatenated = accumulate(content, ~ paste(.x, .y)))
    
      id    content_concatenated
      <chr> <chr>               
    1 a     A                   
    2 a     A B                 
    3 a     A B A               
    4 b     B                   
    5 b     B C                 
    6 b     B C B  
    

    【讨论】:

      【解决方案3】:

      您可以使用Reduce 定义“累积粘贴”函数:

      cumpaste = function(x, .sep = " ") 
                Reduce(function(x1, x2) paste(x1, x2, sep = .sep), x, accumulate = TRUE)
      
      cumpaste(letters[1:3], "; ")
      #[1] "a"       "a; b"    "a; b; c"
      

      Reduce 的循环避免了从头开始重新连接元素,因为它通过下一个元素拉长了先前的连接。

      按组应用:

      ave(as.character(testdf$content), testdf$id, FUN = cumpaste)
      #[1] "A"     "A B"   "A B A" "B"     "B C"   "B C B"
      

      另一个想法,可以在开始时连接整个向量,然后逐步连接substring

      cumpaste2 = function(x, .sep = " ")
      {
          concat = paste(x, collapse = .sep)
          substring(concat, 1L, cumsum(c(nchar(x[[1L]]), nchar(x[-1L]) + nchar(.sep))))
      }
      cumpaste2(letters[1:3], " ;@-")
      #[1] "a"           "a ;@-b"      "a ;@-b ;@-c"
      

      这似乎也快了一些:

      set.seed(077)
      X = replicate(1e3, paste(sample(letters, sample(0:5, 1), TRUE), collapse = ""))
      identical(cumpaste(X, " --- "), cumpaste2(X, " --- "))
      #[1] TRUE
      microbenchmark::microbenchmark(cumpaste(X, " --- "), cumpaste2(X, " --- "), times = 30)
      #Unit: milliseconds
      #                  expr      min       lq     mean   median       uq      max neval cld
      #  cumpaste(X, " --- ") 21.19967 21.82295 26.47899 24.83196 30.34068 39.86275    30   b
      # cumpaste2(X, " --- ") 14.41291 14.92378 16.87865 16.03339 18.56703 23.22958    30  a
      

      ...这使它成为cumpaste_faster

      【讨论】:

      • Terrible 函数名称,但答案 +1(感谢您对我的 cmets 的支持):-)
      • @AnandaMahto :哈,我知道我必须使用“foo”,但有时你必须按照你看到的那样称呼它:)。 (或者,在这种情况下,当你使用它时..)
      【解决方案4】:

      你也可以试试dplyr

       library(dplyr)
       res <- testdf%>%
              mutate(n=row_number()) %>%
              group_by(id) %>%
              mutate(n1=n[1L]) %>%
              rowwise() %>% 
              do(data.frame(cont_concat= paste(content[.$n1:.$n],collapse=" "),stringsAsFactors=F))
      
       res$cont_concat
       #[1] "A"     "A B"   "A B A" "B"     "B C"   "B C B"
      

      【讨论】:

      • 这似乎只是因为content 被手动定义为上面的向量。有没有办法在 dplyr 中完成所有这些操作,而不必引用数据框之外的对象?
      • @ErikShilts 请作为新问题发布
      【解决方案5】:

      data.table解决方案

      library(data.table)
      setDT(testdf)[, content2 := sapply(seq_len(.N), function(x) paste(content[seq_len(x)], collapse = " ")), by = id]
      testdf
      
      ##    id content content2
      ## 1:  a       A        A
      ## 2:  a       B      A B
      ## 3:  a       A    A B A
      ## 4:  b       B        B
      ## 5:  b       C      B C
      ## 6:  b       B    B C B
      

      【讨论】:

      • 最佳和更简单的解决方案!
      • 这太棒了。如果您只想粘贴唯一值,只需在内容前添加 unique
      【解决方案6】:

      这是一个 ddply 方法,使用 sapply 和子集以递增地粘贴在一起:

      library(plyr)
      ddply(testdf, .(id), mutate, content_concatenated = sapply(seq_along(content), function(x) paste(content[seq(x)], collapse = " ")))
        id content content_concatenated
      1  a       A                    A
      2  a       B                  A B
      3  a       A                A B A
      4  b       B                    B
      5  b       C                  B C
      6  b       B                B C B
      

      【讨论】:

      • 您可能想提一下ddply 的来源
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-07
      • 1970-01-01
      • 2013-09-14
      • 2023-03-17
      • 2022-06-10
      • 1970-01-01
      相关资源
      最近更新 更多