2011-12-12 49 views
13

数据帧我不得不应付称为ul一个丑陋的名单,看起来像这样:清单中的R

[[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。对不起,没有可重复的代码。非常感谢你。

回答

12

可能有更好的解决方案,但这应该让你开始。首先,我们加载一些库

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 
9

由于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。如果要将所有内容保留为字符,请在每次调用vapply时删除as.numeric并使用""交换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" 
1

我不知道你所说的“单独访问每个列表功能”的意思,但这种使用“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) 

我们仍然需要通过对FO finaldf元素改变从“人物”到什么是适合你的应用程序,例如

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

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

rownames(finaldf) <- NULL 

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

10

的回答类似的问题是由马克·施瓦茨在这个环节给出: 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 
+0

感谢您的链接,我不知道这件事时,我问的问题。 – Emer

+0

不客气! – rafaelvalle