Расплав + 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 имеют гораздо больше столбцов, и я не хотел бы переходить один за другим... Если кто-то знает более красивый способ решения этой проблемы, я был бы признателен!