将XML更高的节点属性,节点值和文件名获取到R data.frame

时间:2014-08-14 13:44:56

标签: xml r dataframe

我一直在与这个斗争已经很长时间了,无法让它发挥作用,所以我在这里发帖。我不是一个高级的R用户,但我正在学习并慢慢地前进。我还没有找到Stackoverflow的一个例子,我可以适应这个,示例似乎有不同的结构,不需要遍历每个节点的每个更高级别的属性。或者这就是我现在理解差异的方式。问题类似于this,但文件结构不同。现在我基本上使用了this example

假设我有大量的小型XML文件,其结构如下所示。它们的名称类似于file1.xml,file2.xml等。所以file1.xml将是:

<NODE>
<SUBNODE TYPE="WORDS" SPEAKER="person1">
<WORD>word1</WORD>
<WORD>word2</WORD>
<WORD>word3</WORD>
</SUBNODE>
<SUBNODE TYPE="WORDS" SPEAKER="person2">
<WORD>word4</WORD>
<WORD>word5</WORD>
<WORD>word6</WORD>
</SUBNODE>
</NODE>

然后file2.xml将是:

<NODE>
<SUBNODE TYPE="WORDS" SPEAKER="person3">
<WORD>word7</WORD>
<WORD>word8</WORD>
<WORD>word9</WORD>
</SUBNODE>
<SUBNODE TYPE="WORDS" SPEAKER="person4">
<WORD>word10</WORD>
<WORD>word11</WORD>
<WORD>word12</WORD>
</SUBNODE>
</NODE>

我想把它们变成这样的数据框:

Filename   Speaker   Word
file1      person1   word1
file1      person1   word2
file1      person1   word3
file1      person2   word4
file1      person2   word5
file1      person2   word6
file2      person3   word7
file2      person3   word8
file2      person3   word9
file2      person4   word10
file2      person4   word11
file2      person4   word12

我可以将所有单词列表合并到一个数据框中:

library(XML)
library(plyr)
xmlfiles <- list.files(pattern = "*.xml")
dat <- ldply(seq(xmlfiles), function(i){
    doc <- xmlTreeParse(xmlfiles[i], useInternal = TRUE)
    Word <- xpathSApply(doc, "//SUBNODE[@TYPE='WORDS']/WORD", xmlValue)
    return(data.frame(Word))
})

“dat”的内容现在应该是单词列表。但无论我尝试什么,我都无法将其他数据添加到其中。我试图在那里添加像:

xmlfiles <- list.files(pattern = "*.xml")
dat <- ldply(seq(xmlfiles), function(i){
    doc <- xmlTreeParse(xmlfiles[i], useInternal = TRUE)
    Word <- xpathSApply(doc, "//SUBNODE[@TYPE='WORDS']/WORD", xmlValue)
    Speaker <- xpathSApply(doc, "//SUBNODE[@TYPE='WORDS']", xmlGetAttr, "SPEAKER")        
    return(data.frame(Word, Speaker))
})

但是数据帧不正确,因为它没有将正确的发言者与正确的单词联系起来。

Word    Speaker
word1   person1
word2   person2
word3   person1
word4   person2
word5   person1
word6   person2
word7   person3
word8   person4
word9   person3
word10  person4
word11  person3
word12  person4

然后我也经常遇到如下错误:

"Error in UseMethod("xmlValue") : 
no applicable method for 'xmlValue' applied to an object of class "c('XMLInternalDocument', 'XMLAbstractDocument')"

或者我得到一个错误,这些错误的长度不同,当然,因为扬声器的数量少于单词。我尝试了很多东西,但我在这里只发布了“最成功”的方法。我知道我需要一个函数,将每个单词与上述节点中的speaker属性相匹配,只是将它们提取到自己的列表中没有帮助,我想现在这只是运气,在这个例子中是扬声器的数量和单词是匹配的,所以它们就像上面的数据框一样放在一起。

然后我仍然需要将文件名放到一列中,因为它们包含一些我在XML文件本身内部没有的信息。无论如何,这是我问题中最不重要的方面。我使用的实际文件要复杂得多,这就是为什么我在文件中有一些不必要的结构,如SUBNODE TYPE等。

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

我可能会尝试循环遍历文件并解析getNodeSet。我不经常使用ldply,但你可以用那个替换循环吗?

xmlfiles <- list.files(pattern = "*.xml")
n <- length(xmlfiles)
dat <- vector("list", n)
for(i in 1:n){
   doc <- xmlParse(xmlfiles[i])
   nodes <- getNodeSet(doc, "//SUBNODE")
   x<- lapply(nodes, function(x){ data.frame(
     Filename = xmlfiles[i],
     Speaker= xpathSApply(x, "." , xmlGetAttr, "SPEAKER"),
     Word= xpathSApply(x, ".//WORD" , xmlValue) )})
     dat[[i]] <- do.call("rbind", x)
}
do.call("rbind", dat)

答案 1 :(得分:2)

一种可能性是获取所有相关值(xml是我认为您的doc

x = xml['//SUBNODE/@SPEAKER | //SUBNODE/WORD/text()']

找到扬声器并将所有内容转换为简单的字符向量

isSpeaker = sapply(x, is, "XMLAttributeValue")
x[!isSpeaker] = sapply(x[!isSpeaker], xmlValue)
x = unlist(x, use.names=FALSE)

然后捣乱结果

r = rle(isSpeaker)
data.frame(Speaker=rep(x[isSpeaker], r$length[!r$value]), Word=x[!isSpeaker])

(我不认为这对没有言语的发言者来说是健壮的,但那会是什么样的发言者?)