Преобразовать вложенный список в фрейм данных - PullRequest
3 голосов
/ 10 марта 2020

У меня есть список предметов, у каждого из них есть два предмета, один - список, а другой - символьное выражение. Мы генерируем списки

My_list <- list()
My_list$'product1' <- list()
My_list$'product1'$'sales' <- c(1,2,3)
My_list$'product1'$'model' <- "arima"
My_list$'product2'$'sales' <- c(4,5,6)
My_list$'product2'$'model' <- "prophet"

Это желаемая форма вывода

df1 <- data.frame(product=c("product1"),sales1 = 1, sales2 = 2, sales3 = 3)
df2 <- data.frame(product=c("product2"),sales1 = 4, sales2 = 5, sales3 = 6)
solution <- rbind (df1,df2)

Я пробовал что-то подобное

solution <- lapply(My_list, function(x) do.call(rbind, lapply(x, as.data.frame)))
solution <- do.call(rbind, Map(cbind, product = names(My_list), My_list))
```7

Ответы [ 6 ]

3 голосов
/ 10 марта 2020

Вот простая версия через базу R,

as.data.frame(matrix(unlist(My_list), nrow = length(My_list), byrow = TRUE))
#  V1 V2 V3      V4
#1  1  2  3   arima
#2  4  5  6 prophet

Вы можете легко внести изменения, чтобы соответствовать ожидаемому результату (измените имена и преобразуйте V4 в product1 и product2 ), то есть

#save the data frame
d1 <- as.data.frame(matrix(unlist(My_list), nrow = length(My_list), byrow = TRUE))
#Set the column names
d1 <- setNames(d1, c(paste0('sales', seq(ncol(d1) - 1)), 'Product'))
#Change the variable under `Product`
d1$Product <- paste0('Product', seq(nrow(d1)))

d1
#  sales1 sales2 sales3  Product
#1      1      2      3 Product1
#2      4      5      6 Product2
2 голосов
/ 10 марта 2020

На мой взгляд, довольно интуитивно понятный и простой в обслуживании метод:

data.frame(product=names(My_list), 
           do.call(rbind, lapply(My_list, FUN=function(x) unlist(x["sales"]))), row.names = NULL)

   product sales1 sales2 sales3
1 product1      1      2      3
2 product2      4      5      6

Он просматривает список списков, используя lapply, и выводит из списка все записи sales (которые автоматически присваивают им имена). Тогда это rbind s векторы вместе, используя do.call.


Быстрый способ добавить название модели в таблицу - использовать rapply, который по умолчанию выводит список результатов (см. ?rapply и аргумент how)

data.frame(model=rapply(My_list, f=paste, classes="character"),
           product=names(My_list), 
           do.call(rbind, lapply(My_list, FUN=function(x) unlist(x["sales"]))), row.names = NULL)

    model  product sales1 sales2 sales3
1   arima product1      1      2      3
2 prophet product2      4      5      6
2 голосов
/ 10 марта 2020

Вот решение data.table. Я добавил объяснение и промежуточные результаты в коде ниже как комментарий ...

library(data.table)
#bind list, using name as id
DT <- rbindlist( My_list, idcol = "product" )
#     product sales   model
# 1: product1     1   arima
# 2: product1     2   arima
# 3: product1     3   arima
# 4: product2     4 prophet
# 5: product2     5 prophet
# 6: product2     6 prophet

#create rowid's by product-group, used for casting in the next line
DT[, row_id := rowid(product) ]
#     product sales   model row_id
# 1: product1     1   arima      1
# 2: product1     2   arima      2
# 3: product1     3   arima      3
# 4: product2     4 prophet      1
# 5: product2     5 prophet      2
# 6: product2     6 prophet      3

#cast to wide format
dcast( DT, product ~ paste0( "sales", row_id ), value.var = "sales" )
#     product sales1 sales2 sales3
# 1: product1      1      2      3
# 2: product2      4      5      6
1 голос
/ 10 марта 2020

Вы можете использовать [[ в lapply, чтобы получить первый товар sales из My_list, который вы можете rbind с do.call. Из результата установлены colnames.

tt <- do.call(rbind, lapply(My_list, "[[", 1))
#tt <- do.call(rbind, lapply(My_list, "[[", "sales")) #Alternative
colnames(tt) <- paste0("sales",seq_len(ncol(tt)))
tt
#         sales1 sales2 sales3
#product1      1      2      3
#product2      4      5      6
1 голос
/ 10 марта 2020

Здесь базовое решение R:

# transpose and fetch the sales arguments putting them in a df
sales <-t(do.call(cbind,
        lapply(My_list, function(x) data.frame(x[names(x)=="sales"]))))

# rename the rows with products
rownames(sales) <- names(My_list)

# rename columns 
colnames(sales) <- paste0("sales",c(1:ncol(sales)))
sales

         sales1 sales2 sales3
product1      1      2      3
product2      4      5      6

На случай, если вам нужен фрейм данных с колонкой продукта:

sales <- data.frame(sales)
sales$product <- rownames(sales)
rownames(sales) <- 1:nrow(sales)
sales
  sales1 sales2 sales3  product
1      1      2      3 product1
2      4      5      6 product2
0 голосов
/ 10 марта 2020

Опция базового R

solution <- cbind(Product = names(My_list),
                  `names<-`(r <- as.data.frame(do.call(rbind,sapply(My_list, `[`,-2)),row.names = FALSE),
                            paste0("Sale",seq(ncol(r)))))

, которая дает

> solution
   Product Sale1 Sale2 Sale3
1 product1     1     2     3
2 product2     4     5     6
...