Подтвердить что ты не робот

Список списков в dataframe в R

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

[[1]]
[[1]]$param
     name     value 
"Section"       "1" 

[[1]]$param
   name   value 
"field"     "1" 

[[1]]$param
          name          value 
"final answer"            "1" 

[[1]]$param
    name    value 
"points"   "-0.0" 


[[2]]
[[2]]$param
     name     value 
"Section"       "1" 

[[2]]$param
   name   value 
"field"     "2" 

[[2]]$param
          name          value 
"final answer"            "1" 

[[2]]$param
    name    value 
"points"    "1.0" 


[[3]]
[[3]]$param
     name     value 
"Section"       "1" 

[[3]]$param
   name   value 
"field"     "3" 

[[3]]$param
          name          value 
"final answer"        "0.611" 

[[3]]$param
    name    value 
"points"    "1.0" 

Я хотел бы преобразовать список в простой фрейм данных, т.е.

Section    field    final answer    points
      1        1               1      -0.0
      1        2               1       1.0
      1        3           0.611       1.0

Есть ли простой способ достичь этого? или мне нужно сделать функцию доступа к каждому списку отдельно и привязать его к кадру данных?

Данные импортируются из более уродливого XML файла, поэтому, если кто-то хочет поиграть с ним, есть ссылка на файл RData. Извините за отсутствие воспроизводимого кода. Большое вам спасибо.

4b9b3361

Ответ 1

Возможно, лучшее решение, но это должно вас начать. Во-первых, мы загружаем некоторые библиотеки

R> library(plyr)
R> library(reshape2)

Затем обрабатывайте списки в двух частях.

##lapply applies ldply to each list element in turn
ul1 = lapply(ul, ldply)

##We then do the same again
dd = ldply(ul1)[,2:3]

Затем мы выводим вывод в соответствии со списком

R> dd$num = rep(1:3, each=4)

Затем мы конвертируем из длинного в широкоформатный

R> dcast(dd, num ~ name)

  num field final answer points Section
1   1     1            1   -0.0       1
2   2     2            1    1.0       1
3   3     3        0.611    1.0       1

Ответ 2

Ответ на аналогичную проблему дал Марк Шварц по этой ссылке: https://stat.ethz.ch/pipermail/r-help/2006-August/111368.html

Я копирую его, если ссылка удалена.

 as.data.frame(sapply(a, rbind))

   V1 V2 V3
1  a  b  c
2  1  3  5
3  2  4  6

или

as.data.frame(t(sapply(a, rbind)))
   V1 V2 V3
1  a  1  2
2  b  3  4
3  c  5  6

Ответ 3

Поскольку структура ul является последовательной, вы можете просто получить каждый столбец отдельно (используя только базу R):

section <- vapply(ul, function(x) as.numeric(x[[1]][2]), 0)
field <- vapply(ul, function(x) as.numeric(x[[2]][2]), 0)
final_answer <- vapply(ul, function(x) as.numeric(x[[3]][2]), 0)
points <- vapply(ul, function(x) as.numeric(x[[4]][2]), 0)

(Заметьте, я использую vapply вместо sapply, поскольку он быстрее и надежно возвращает вектор, который здесь необходим).
Затем вы можете просто собрать все это вместе:

> data.frame(section, field, final_answer, points)
  section field final_answer points
1       1     1        1.000      0
2       1     2        1.000      1
3       1     3        0.611      1

Обратите внимание, что я превратил все в numeric. Если вы хотите сохранить все как символы, удалите as.numeric и обменивайтесь 0 с помощью "" в каждом вызове vapply.


Позднее обновление:

На самом деле есть приятный oneliner, который извлекает полные данные:

do.call("rbind", lapply(ul, function(x) as.numeric(vapply(x, "[", i = 2, ""))))

который дает:

     [,1] [,2]  [,3] [,4]
[1,]    1    1 1.000    0
[2,]    1    2 1.000    1
[3,]    1    3 0.611    1

чтобы использовать colnames:

> vapply(ul[[1]], "[", i = 1, "")
         param          param          param          param 
     "Section"        "field" "final answer"       "points" 

Ответ 4

Я не уверен, что вы подразумеваете под "функцией доступа к каждому списку по отдельности", но это довольно просто, используя "lapply" и "do.call(" rbind ",...)":

Я не мог загрузить ваш .RData файл, поэтому этот код работает для списка:

ul <- list(param = list(
             c(name = "Section", value = "1"),
             c(name = "field", value = "1"),
             c(name = "final answer", value = "1"),
             c(name = "points", value = "-0.0")),
           param = list(
             c(name = "Section", value = "1"),
             c(name = "field", value = "2"),
             c(name = "final answer", value = "1"),
             c(name = "points", value = "1.0")))

Возможно, вам придется настроить данные, если ваш список отличается; общий принцип останется прежним. Просто, чтобы сохранить код в чистоте, определите функцию "extractitem", которая вытащит все имена или значения для ul [[1]], ul [[2]] и т.д. Эта функция немного более общая, чем вам нужно.

extractitem <- function(listelement, item)
  unname(lapply(listelement, function(itemblock) itemblock[item]))

Теперь мы просто используем lapply для перехода через элемент ul по элементу; для каждого элемента мы извлекаем значения в кадр данных, а затем называем столбцы в соответствии с "именами".

rowlist <- lapply(ul, function(listelement) {
  d <- data.frame(extractitem(listelement, "value"), stringsAsFactors = FALSE)
  names(d) <- unlist(extractitem(listelement, "name"))
  d
})

список строк теперь представляет собой список фреймов данных; мы можем объединить их в единый кадр данных с помощью "rbind". Хорошая вещь об использовании кадров данных на предыдущем шаге (в отличие от векторов или чего-то с более низкими служебными данными) заключается в том, что rbind будет изменять порядок столбцов, если это необходимо, поэтому, если порядок полей изменяется от элемента к элементу, мы все еще все вправо.

finaldf <- do.call("rbind", rowlist)

Нам по-прежнему необходимо изменить элементы fo finaldf от "character" до любого, что подходит для вашего приложения, например,

finaldf$points <- as.numeric(finaldf$points)

и т.д. Последний шаг очищает кадр данных, удаляя автоматически созданные имена строк:

rownames(finaldf) <- NULL

В случае, если вам нужно изменить настройки, общая идея состоит в том, чтобы написать функцию, которая будет форматировать каждую ul [[i]] в качестве фрейма данных с правильными именами столбцов; затем вызовите эту функцию на каждом элементе ul с лапкой; и, наконец, свернуть результирующий список с помощью do.call( "rbind",...).