Кумулятивно вставьте (объедините) значения, сгруппированные по другой переменной

r dataframe

2289 просмотра

5 ответа

46 Репутация автора

У меня проблема с фреймом данных в 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
Автор: user3860074 Источник Размещён: 21.07.2014 09:44

Ответы (5)


2 плюса

54468 Репутация автора

Вот 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
Автор: James Размещён: 21.07.2014 10:50

29 плюса

11263 Репутация автора

Вы можете определить функцию «накопительной вставки», используя 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.

Автор: alexis_laz Размещён: 21.07.2014 11:29

2 плюса

81260 Репутация автора

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
Автор: David Arenburg Размещён: 21.07.2014 11:33

2 плюса

482778 Репутация автора

Вы также можете попробовать 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"
Автор: akrun Размещён: 21.07.2014 05:23

0 плюса

14171 Репутация автора

Один из вариантов использования dplyrи purrrможет быть:

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  
Автор: tmfmnk Размещён: 02.11.2019 10:22
Вопросы из категории :
32x32