2011-12-12 44 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