Как спрятать R-фрейм данных, содержащий списки?
Я хочу найти лучший способ "R", чтобы сгладить структуру данных, которая выглядит так:
CAT COUNT TREAT
A 1,2,3 Treat-a, Treat-b
B 4,5 Treat-c,Treat-d,Treat-e
Итак, он будет структурирован следующим образом:
CAT COUNT1 COUNT2 COUNT3 TREAT1 TREAT2 TREAT3
A 1 2 3 Treat-a Treat-b NA
B 4 5 NA Treat-c Treat-d Treat-e
Пример кода для генерации исходного фрейма данных:
df<-data.frame(CAT=c("A","B"))
df$COUNT <-list(1:3,4:5)
df$TREAT <-list(paste("Treat-", letters[1:2],sep=""),paste("Treat-", letters[3:5],sep=""))
Мне кажется, мне нужна комбинация rbind и unlist? Любая помощь будет принята с благодарностью.
- Тим
Ответы
Ответ 1
Вот еще один способ в базе r
df<-data.frame(CAT=c("A","B"))
df$COUNT <-list(1:3,4:5)
df$TREAT <-list(paste("Treat-", letters[1:2],sep=""),paste("Treat-", letters[3:5],sep=""))
Создайте вспомогательную функцию для выполнения работы
f <- function(l) {
if (!is.list(l)) return(l)
do.call('rbind', lapply(l, function(x) `length<-`(x, max(lengths(l)))))
}
Всегда проверяйте свой код
f(df$TREAT)
# [,1] [,2] [,3]
# [1,] "Treat-a" "Treat-b" NA
# [2,] "Treat-c" "Treat-d" "Treat-e"
Применить его
df[] <- lapply(df, f)
df
# CAT COUNT.1 COUNT.2 COUNT.3 TREAT.1 TREAT.2 TREAT.3
# 1 A 1 2 3 Treat-a Treat-b <NA>
# 2 B 4 5 NA Treat-c Treat-d Treat-e
Ответ 2
Вот решение, использующее базу R, принимающую векторы любой длины внутри вашего списка, и не нужно указывать, какие столбцы кадра данных вы хотите свернуть. Часть решения была создана с использованием этого ответа.
df2 <- do.call(cbind,lapply(df,function(x){
#check if it is a list, otherwise just return as is
if(is.list(x)){
return(data.frame(t(sapply(x,'[',seq(max(sapply(x,length)))))))
} else{
return(x)
}
}))
Как и в случае R 3.2, существует lengths
для замены sapply(x, length)
,
df3 <- do.call(cbind.data.frame, lapply(df, function(x) {
# check if it is a list, otherwise just return as is
if (is.list(x)) {
data.frame(t(sapply(x,'[', seq(max(lengths(x))))))
} else {
x
}
}))
используемые данные:
df <- structure(list(CAT = structure(1:2, .Label = c("A", "B"), class = "factor"),
COUNT = list(1:3, 4:5), TREAT = list(c("Treat-a", "Treat-b"
), c("Treat-c", "Treat-d", "Treat-e"))), .Names = c("CAT",
"COUNT", "TREAT"), row.names = c(NA, -2L), class = "data.frame")
Ответ 3
Здесь есть удаленный ответ, который указывает, что для этого можно использовать "splitstackshape". Он может, но удаленный ответ использовал неправильную функцию. Вместо этого он должен использовать функцию listCol_w
. К сожалению, в его нынешнем виде эта функция не векторизована по столбцам, поэтому вам нужно будет вложить вызовы на listCol_w
для каждого столбца, который должен быть сплющен.
Здесь подход:
library(splitstackshape)
listCol_w(listCol_w(df, "COUNT", fill = NA), "TREAT", fill = NA)
## CAT COUNT_fl_1 COUNT_fl_2 COUNT_fl_3 TREAT_fl_1 TREAT_fl_2 TREAT_fl_3
## 1: A 1 2 3 Treat-a Treat-b NA
## 2: B 4 5 NA Treat-c Treat-d Treat-e
Обратите внимание, что fill = NA
указан, поскольку по умолчанию он равен fill = NA_character_
, что в противном случае принуждало бы все значения к символу.
Другой альтернативой может быть использование transpose
из "data.table". Здесь возможная реализация (выглядит страшно, но с помощью функции легко). Преимущества заключаются в том, что (1) вы можете указать столбцы для выравнивания, (2) вы можете решить, хотите ли вы удалить исходный столбец или нет, и (3) быстро.
flatten <- function(indt, cols, drop = FALSE) {
require(data.table)
if (!is.data.table(indt)) indt <- as.data.table(indt)
x <- unlist(indt[, lapply(.SD, function(x) max(lengths(x))), .SDcols = cols])
nams <- paste(rep(cols, x), sequence(x), sep = "_")
indt[, (nams) := unlist(lapply(.SD, transpose), recursive = FALSE), .SDcols = cols]
if (isTRUE(drop)) {
indt[, (nams) := unlist(lapply(.SD, transpose), recursive = FALSE),
.SDcols = cols][, (cols) := NULL]
}
indt[]
}
Использование будет...
Сохранение исходных столбцов:
flatten(df, c("COUNT", "TREAT"))
# CAT COUNT TREAT COUNT_1 COUNT_2 COUNT_3 TREAT_1 TREAT_2 TREAT_3
# 1: A 1,2,3 Treat-a,Treat-b 1 2 3 Treat-a Treat-b NA
# 2: B 4,5 Treat-c,Treat-d,Treat-e 4 5 NA Treat-c Treat-d Treat-e
Удаление исходных столбцов:
flatten(df, c("COUNT", "TREAT"), TRUE)
# CAT COUNT_1 COUNT_2 COUNT_3 TREAT_1 TREAT_2 TREAT_3
# 1: A 1 2 3 Treat-a Treat-b NA
# 2: B 4 5 NA Treat-c Treat-d Treat-e
См. этот смысл для сравнения с другими предлагаемыми решениями.