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

У меня проблема с кадром данных в R. Я хотел бы вставить содержимое ячеек в разных строках вместе на основе значений ячеек в другом столбце. Моя проблема в том, что я хочу, чтобы результат был постепенно (кумулятивно) напечатан. Выходной вектор должен быть такой же длины, как и входной вектор. Вот таблица sampel, подобная той, с которой я имею дело:

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", "AB", "ABA", "B", "BC", "BCB") result #[1] "A" "AB" "ABA" "B" "BC" "BCB" 

Что мне НЕ нужно что-то вроде этого:

 ddply(testdf, .(id), summarize, content_concatenated = paste(content, collapse = " ")) # id content_concatenated #1 a ABA #2 b BCB 

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

Другая идея, могла бы конкатенировать весь вектор в начале и затем постепенно 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 .

Вот метод ddply использующий sapply и subsetting для постепенного вложения:

 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 AA 2 a BAB 3 a AABA 4 b BB 5 b CBC 6 b BBCB 

Вы также можете попробовать 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" "AB" "ABA" "B" "BC" "BCB" 

решение 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 AA ## 2: a BAB ## 3: a AABA ## 4: b BB ## 5: b CBC ## 6: b BBCB 
  • объекты data.table, назначенные с помощью: = из функции, не напечатанной
  • Динамически строить вызов для поиска нескольких столбцов
  • Извлечение столбца из таблицы данных. В виде вектора по позиции
  • Изменение шрифтов в ggplot2
  • Найти сходство косинусов между двумя массивами
  • Отображение значений данных на гистограмме с разбивкой по столбцам в ggplot2
  • Установка абсолютного размера граней в ggplot2
  • Использовать переменную в выражении plotmath
  • Бок о бок участки с ggplot2
  • Отключить сообщения при загрузке пакета
  • Какие способы редактирования функции в R?
  • Давайте будем гением компьютера.