我有一个我想从中提取数据的XML文件。到目前为止
样本XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:ArchiveView>
<Notification ID="1001">
<persons>
<Timestamp>07:39:25</Timestamp>
<person type="A" name="Barney">
<uniqueUserId>2222</uniqueUserId>
</person>
</persons>
<persons>
<Timestamp>08:40:25</Timestamp>
<person type="B" name="John">
<uniqueUserId>1111</uniqueUserId>
</person>
</persons>
</Notification>
<Notification ID="1002">
<persons>
<Timestamp>14:39:25</Timestamp>
<person type="A" name="Barney">
<uniqueUserId>2222</uniqueUserId>
</person>
</persons>
</Notification>
<Notification ID="1003">
</Notification>
</ns2:ArchiveView>
由于可以分配给通知的最大人数为3,所以我想获得一个数据。形式看起来像这样:
ID name1 time1 type1 name2 time2 type2 name3 time3 type3
1001 Barney 07:39:25 A John 08:40:25 B NA NA NA
1002 Barney 14:39:25 A NA NA NA NA NA NA
1003 NA NA NA NA NA NA NA NA NA
我设法到目前为止得到了什么:
doc <- read_xml( "./data/test.xml" )
提取所有ID
df.ID <- data.frame(
ID = xml_find_all( doc, ".//Notifications" ) %>% xml_attrs() %>% unlist() ,
stringsAsFactors = FALSE )
确定已附加人员的通知ID
ID.with.persons <- xml_find_all( doc, ".//Notifications[ persons ]" ) %>%
xml_attrs() %>%
unlist()
与附加人员的通知创建一个节点
nodes.persons <- xml_find_all( doc, ".//Notifications[ persons ]"
我还设法获得了所有人的名字(一个向量(
persons.name <- nodes.persons %>% xml_attr("name") %>% unlist()
我感觉自己非常接近解决方案,但是我不能围绕如何将所有这些数据合并到一个不错的数据中(如上所述(。
。所有建议都受到热烈的赞赏:(
这是一种非常有效的方法(我对R很新,所以它可能不是很r的。将其转换为末端的矩阵,然后将其插入数据框架中。这之所以起作用,是因为有固定数量的列可以用。
构建矩阵。library(xml2)
doc <- read_xml("test.xml")
row <- c()
notifications <- xml_find_all(doc, ".//Notification")
for (i in 1:length(notifications)) {
row <- c(row, xml_attr(notifications[i], "ID"))
for (j in 1:3) {
person <- xml_find_all(notifications[i], sprintf("persons[%d]", j))
if (length(person) > 0) {
row <- c(row, xml_find_chr(person, "string(./person/@name)"))
row <- c(row, xml_find_chr(person, "string(./Timestamp/text())"))
row <- c(row, xml_find_chr(person, "string(./person/@type)"))
} else {
row <- c(row, NA, NA, NA)
}
}
}
df <- data.frame(matrix(data=rows, ncol=10, byrow=TRUE))
colnames(df) <- c("ID", "name1", "time1", "type1", "name2", "time2", "type2", "name3", "time3", "type3")
df
输出:
ID name1 time1 type1 name2 time2 type2 name3 time3 type3
1 1001 Barney 07:39:25 A John 08:40:25 B <NA> <NA> <NA>
2 1002 Barney 14:39:25 A <NA> <NA> <NA> <NA> <NA> <NA>
3 1003 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
这是解决方案。它的手动编码比我想要的更多,但确实显示了解决方案技术:
library(xml2)
doc<-read_xml("*Your xml Document goes here*")
#find the Notification nodes
Notices<-xml_find_all( doc, ".//Notification" )
#find all of the timestamps in each Notification
timestamps<-sapply(Notices, function(x){xml_text(xml_find_all(x, ".//Timestamp"))})
#extract the three timestamps in each Notification (missing ones return NA)
#sapply returns a column, need to transpose to create the row in the data frame
time.df<-data.frame(t(sapply(timestamps, function(x){c(x[1], x[2], x[3])})))
#rename the column names
names(time.df)<-paste0("time", 1:3)
#repeat for the person's name and type
persons.name <-sapply(Notices, function(x){x %>% xml_find_all( ".//person" ) %>% xml_attr("name")})
name.df<-data.frame(t(sapply(persons.name, function(x){c(x[1], x[2], x[3])})))
names(name.df)<-paste0("name", 1:3)
persons.type <-sapply(Notices, function(x){x %>% xml_find_all( ".//person" ) %>% xml_attr("type")})
type.df<-data.frame(t(sapply(persons.type, function(x){c(x[1], x[2], x[3])})))
names(type.df)<-paste0("type", 1:3)
#assemble the final answer and rearrange the column order
answer<-cbind(name.df, time.df, type.df)
answer<-answer[,c(1, 4, 7, 2, 5, 8, 3, 6, 9)]
df.ID <- data.frame(ID = xml_find_all( doc, ".//Notification" ) %>%
xml_attr("ID"), stringsAsFactors = FALSE)
answer<-cbind(df.ID, answer)
代码的评论说明了解决方案所采取的步骤。我确定可能有一些优化,但这是一个很好的开始。