我是新的xml包用于r和新用于xpath。我有一个非常大的xml文件,我正在解析。我使用循环编写了一些代码但是花费的时间太长,所以我使用xpath编写更高效的代码。 xml看起来像这样:
...
<person personId="1">
<personNames>
<personName nameId="1000">
<first>Joe<last>
<last>Jones<last>
</personName>
<personName nameId="1001">
<first>Joseph><first>
<last>Jones<last>
</personName>
<personName nameId="1002"
<first>The One and only Joe<first>
</personName>
</personNames>
</person>
...
有些人有一个名字,有些人有更多。有些人有名字和姓氏,有些只是名字或姓氏。所以,我需要小心。
我能够使用xpath:
高效地创建名字和姓氏的数据框library(XML)
doc<-xmlTreeParse("People.xml",useInternalNodes = TRUE)
top<-xmlRoot(doc)
First<-as.character(xpathApply(top,"//person/personNames/personName/first", xmlValue))
name_id<-as.integer(xpathApply(top,"//person/personNames/personName[first]/@nameId"))
FirstNames<-data.frame(TMS_name_id=name_id,first=First)
Last<-as.character(xpathApply(top,"//person/personNames/personName/last", xmlValue))
name_id<-as.integer(xpathApply(top,"//person/personNames/personName[last]/@nameId"))
LastNames<-data.frame(name_id=name_id,last=Last)
Names<-merge(x=FirstNames,y=LastNames,by="name_id",all=TRUE)
我的姓名数据框看起来不错。它具有nameId,名字和每个人的姓氏。如果缺少名字或姓氏,则为空。它在几分钟内生成(610K行!)。真棒。
问题是将这些名称与父personId相关联。我假设我需要遍历数据框中的名称,并获取具有正确nameId属性的personId,但我无法执行此操作。例如,以下代码给出了一个null结果:
xpathSApply(top,"//person/personNames/personName[@nameId="1000"]/@personId")
我期待的结果是1.在personId的数据框中添加列的最有效方法是什么?
鉴于上面的示例,我想要一个如下所示的数据框:
nameId first last personId
1000 Joe Jones 1
1001 Joseph Jones 1
1002 The one and only Joe <NA> 1
答案 0 :(得分:2)
由于名字和姓氏都没有平衡,看起来你需要更加谨慎地匹配它们,然后一次性提取它们。
这里有一些有效的测试数据
library(XML)
dd<-xmlInternalTreeParse('<people><person personId="1">
<personNames>
<personName nameId="1000"><first>Joe</first><last>Jones</last></personName>
<personName nameId="1001"><first>Joseph</first><last>Jones</last></personName>
<personName nameId="1002"><first>The One and only Joe</first></personName>
</personNames>
</person></people>')
然后我将包含plyr
以使事情更容易崩溃,并创建一个辅助函数来用NA
library(plyr)
getXmlValue<-function(node, select) {
x<-node[select]
if(length(x)==1) {
xmlValue(x[[1]])
} else {
NA
}
}
然后我可以做
rbind.fill(xpathApply(dd, "//person", function(x) {
pn <- xpathApply(x, "./personNames/personName", function(x) {
data.frame(
nameId=xmlGetAttr(x, "nameId"),
first=getXmlValue(x, "first"),
last=getXmlValue(x,"last"))
})
cbind(personID=xmlGetAttr(x, "personId"), rbind.fill(pn))
}))
获取
personID nameId first last
1 1 1000 Joe Jones
2 1 1001 Joseph Jones
3 1 1002 The One and only Joe <NA>
答案 1 :(得分:0)
以下是有点复杂的;它的灵感来自于创建许多单行data.frames然后将它们组合在一起的成本。我不知道这是否更有效(获得反馈会很有意思......)。
在第一遍中,我记录事件发生时的“几何”
geom <- xpathSApply(dd, "//person|//personName|//first|//last", xmlName)
并在第二遍中提取我感兴趣的名字
## hack: implement XMLAttributeValue method for xmlValue
xmlValue.XMLAttributeValue <- as.character
nms <- xpathSApply(dd,
"//person/@personId|//personName/@nameId|//first|//last", xmlValue)
然后我弄清楚如何将我发现的名称放入矩形网格中的正确单元格
cols <- c(nameId="personName", first="first", last="last")
pidx = geom == "person"
ridx = cumsum(geom == "personName")
cidx <- match(geom, cols, 0)
## fill matrix with leaf nodes
m <- matrix(character(), max(ridx), max(cidx),
dimnames=list(NULL, names(cols)))
m[cbind(ridx, cidx)] <- nms[!pidx]
## 'expand' parent elements and bind to matrix
times <- diff(c(ridx[pidx], max(ridx)))
m <- cbind(personId=rep(nms[pidx], times), m)
带有最终结果
> m
personId nameId first last
[1,] "1" "1000" "Joe" "Jones"
[2,] "1" "1001" "Joseph" "Jones"
[3,] "1" "1002" "The One and only Joe" NA