2014-12-02 63 views
0

我是新来的xml包和rpath的新手。我有一个非常大的XML文件,我解析。我使用循环编写了一些代码,但需要很长时间,所以我正在使用xpath编写更高效的代码。 的XML看起来是这样的:xpath和r - 创建一个密钥表

... 
<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) 

我的姓名数据帧看起来不错。它有每个人的姓名,名字和姓氏。如果缺少名字或姓氏,则该名称为空。它在几分钟内产生(610K行!)。真棒。

问题在于将这些名称与父级personId关联。我假设我需要遍历数据框中的名称,并获取具有正确nameId属性的personId,但我无法执行此操作。例如,下面的代码给我一个空的结果:

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

您能否包含您对样本输入数据的期望结果?我不确定你想要的输出的确切形式。 – MrFlick 2014-12-02 19:07:40

+0

编辑。感谢您的时间。 – user2980491 2014-12-02 19:18:30

回答

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> 
0

以下有点复杂;它受到创建许多单行数据帧的成本的启发,然后将它们结合在一起。我不知道这是否更有效率(获得反馈会很有趣......)。

在第一遍,因为它们发生

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