Расплав + strsplit, или напротив совокупности

У меня есть маленький вопрос, который кажется таким легким в концепции, но я не могу найти способ сделать это...

Скажем, у меня есть data.frame df2 с колонкой, в которой перечислены марки автомобилей, и еще одна колонка, в которой все модели для каждой марки разделены знаком ",". Я получил df2, объединяющий другой data.frame с именем df1 с первичным ключом, являющимся моделью.

Как мне поступить, чтобы выполнить противоположную задачу (то есть: от df2 до df1)? Я думаю, что-то вроде melt(df2, id=unlist(strsplit('models',',')))... Большое спасибо!

Вот MWE:

df1 <- data.frame(model=c('a1','a2','a3','b1','b2','c1','d1','d2','d3','d4'), 
                      brand=c('a','a','a','b','b','c','d','d','d','d'))
df1
collap <- function(x){
  out <- paste(sort(unique(x)), collapse=",")
  return (out)
}
df2 <- aggregate(df1$model, by=list(df1$brand), collap)
names(df2) <- c('brand','models')
df2 #how can I do the opposite task (ie: from df2 to df1)?

4 ответа

Решение

В эти дни я бы использовал tidytext::unnest_tokens для этой задачи:

library(tidytext)
df2 %>% 
  unnest_tokens(model, models, token = "regex", pattern = ",")

# A tibble: 10 x 2
    brand model
   <fctr> <chr>
 1      a    a1
 2      a    a2
 3      a    a3
 4      b    b1
 5      b    b2
 6      c    c1
 7      d    d1
 8      d    d2
 9      d    d3
10      d    d4

Вот две альтернативы:

использование data.table а также unlist следующее:

library(data.table)
DT <- data.table(df2)
DT[, list(model = unlist(strsplit(as.character(models), ","))), 
   by = brand]
#     brand model
#  1:     a    a1
#  2:     a    a2
#  3:     a    a3
#  4:     b    b1
#  5:     b    b2
#  6:     c    c1
#  7:     d    d1
#  8:     d    d2
#  9:     d    d3
# 10:     d    d4

использование concat.split.multiple из моего пакета "splitstackshape". Одна из приятных особенностей этого подхода - возможность разбивать несколько столбцов одной простой командой.

library(splitstackshape)
out <- concat.split.multiple(df2, "models", ",", "long")
out[complete.cases(out), ]
#    brand time models
# 1      a    1     a1
# 2      b    1     b1
# 3      c    1     c1
# 4      d    1     d1
# 5      a    2     a2
# 6      b    2     b2
# 8      d    2     d2
# 9      a    3     a3
# 12     d    3     d3
# 16     d    4     d4

Вот как я мог бы сделать это с помощью plyr пакет

library("plyr")
ddply(df2, .(brand), function(DF) {
  data.frame(model = strsplit(DF$models, ",")[[1]])
})

Для сравнения: как использовать один и тот же пакет df1 в df2:

ddply(df1, .(brand), 
      summarize, models=paste(sort(unique(model)), collapse=","))

Играя вокруг, я нашел способ сделать трюк, хотя он может быть довольно грязным:

df1 <- data.frame(model=as.character(melt(strsplit(df2$models,','))$value), brand=as.character(df2[match(melt(strsplit(df2$models,','))$L1, rownames(df2)),]$brand))

Это не лучшее решение, так как на самом деле data.frames имеют гораздо больше столбцов, и я не хотел бы переходить один за другим... Если кто-то знает более красивый способ решения этой проблемы, я был бы признателен!

Другие вопросы по тегам