Эффективное объединение содержимого символов в одном столбце по группам в R

#r #dataframe #concatenation #aggregate #dplyr

#r #фрейм данных #объединение #агрегировать #dplyr

Вопрос:

Какой самый быстрый способ выполнить операцию, подобную объединению, над data.frame in R ? Предположим, у меня есть следующая таблица:

 df <- data.frame(content = c("c1", "c2", "c3", "c4", "c5"),
                 groups = c("g1", "g1", "g1", "g2", "g2"),
                 stringsAsFactors = F)

df$groups <- as.factor(df$groups)
  

Я хочу эффективно объединить содержимое ячеек в content столбце по группам, чтобы получить эквивалент:

 df2 <- data.frame(content = c("c1 c2 c3", "c4 c5"),
                  groups = c("g1", "g2"),
                  stringsAsFactors = F)

df2 $groups <- as.factor(df2 $groups)
  

Я бы предпочел некоторую dplyr операцию, но не имею хорошей идеи, как ее применить.

Ответ №1:

Близкий родственник tapply is aggregate , который позволяет вам делать это:

 aggregate(content ~ groups, df, paste, collapse = " ")
#   groups  content
# 1     g1 c1 c2 c3
# 2     g2    c4 c5
  

Факторы сохраняются:

 str(.Last.value)
# 'data.frame':  2 obs. of  2 variables:
#  $ groups : Factor w/ 2 levels "g1","g2": 1 2
#  $ content: chr  "c1 c2 c3" "c4 c5"
  

Поскольку вы упомянули, что ищете dplyr подход, вы можете попробовать что-то вроде этого:

 library(dplyr)
df %>% group_by(groups) %>% summarise(content = paste(content, collapse = " "))
# Source: local data frame [2 x 2]
# 
#   groups  content
# 1     g1 c1 c2 c3
# 2     g2    c4 c5
  

Ответ №2:

Используя data.table :

 library(data.table)
dt = as.data.table(df)

dt[, paste(content, collapse = " "), by = groups]
#   groups       V1
#1:     g1 c1 c2 c3
#2:     g2    c4 c5
  

Поскольку скорость была упомянута в OP, data.table и dplyr довольно близки (базовые методы очень медленные, нет смысла их тестировать):

 dt = data.table(content = sample(letters, 26e6, T), groups = LETTERS)
df = as.data.frame(dt)

system.time(dt[, paste(content, collapse = " "), by = groups])
#   user  system elapsed 
#   5.37    0.06    5.65 

system.time(df %>% group_by(groups) %>% summarise(paste(content, collapse = " ")))
#   user  system elapsed 
#   7.10    0.13    7.67 
  

Комментарии:

1. «Базовые методы очень медленные, нет смысла их тестировать». Теперь это просто подло (но я не отрицаю его истинности!) 🙂

Ответ №3:

Вот метод, использующий базовые tapply

 splat<-with(df, tapply(content, groups, paste, collapse=" "))
df2<-data.frame(groups=names(splat), content=splat, stringsAsFactors=F)
df2$groups <- as.factor(df2$groups)
  

что дает вам

 #    groups  content
# g1     g1 c1 c2 c3
# g2     g2    c4 c5
  

(дополнительные «g1 / g2» — это имена строк в data.frame)

Комментарии:

1. Спасибо! Я действительно ценю вашу помощь, это сэкономило мне много времени! =)