【问题标题】:List of lists to dataframe in RR中数据框的列表列表
【发布时间】:2012-01-18 15:45:35
【问题描述】:

我必须处理一个名为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 file 的链接。抱歉没有可重现的代码。非常感谢。

【问题讨论】:

    标签: xml list r dataframe


    【解决方案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】:

      由于ul的结构是一致的,您可以简单地单独获取每一列(仅使用base 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,并在每次调用vapply 时将0"" 交换。


      后期更新:

      其实有一个不错的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" 
      

      【讨论】:

        【解决方案3】:

        我不确定“单独访问每个列表的函数”是什么意思,但使用“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
        })
        

        rowlist 现在是数据框列表;我们可以使用“rbind”将它们合并到一个数据框中。在上一步中使用数据帧的好处(与向量或开销较低的东西相反)是 rbind 将在必要时重新排序列,因此如果字段的顺序从元素到元素发生变化,我们仍然是对。

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

        我们仍然需要将 finaldf 的元素从“字符”更改为适合您的应用程序的任何内容,例如

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

        等等。最后一步通过剥离自动生成的行名来清理数据框:

        rownames(finaldf) <- NULL
        

        如果您需要调整一些东西,一般的想法是编写一个函数,将每个 ul[[i]] 格式化为具有正确列名的数据框;然后使用 lapply 在 ul 的每个元素上调用该函数;最后用 do.call("rbind",...) 折叠结果列表。

        【讨论】:

          【解决方案4】:

          Marc Schwartz 在此链接上给出了类似问题的答案: 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
          

          【讨论】:

          • 感谢您的链接,当我问这个问题时我不知道它。
          猜你喜欢
          • 1970-01-01
          • 2016-09-10
          • 1970-01-01
          • 2015-04-21
          • 2016-03-30
          • 1970-01-01
          • 1970-01-01
          • 2020-02-01
          • 1970-01-01
          相关资源
          最近更新 更多