R 中的 XML:多个 Children 同名且无循环
XML in R: Multiple Children with Same Name without Loops
我有一个 XML 文档,如下所示:
<root>
<Item>
<A>text1</A>
<B>text2</B>
<C>text3</C>
<C>text4</C>
<C>text5</C>
<D>text6</D>
...
</Item>
<Item>
...
</Item>
...
</root>
它相对简单,但有一个复杂因素:每个 item
可以有任意数量的 C
。
最终,我希望在 table 中拥有它,例如:
A B C D
1 text1 text2 <list [3]> text6
我已经为其他变量创建了 table(可能是一种混乱的方式,但它有效):
vnames<-c("A","B","D")
dat<-list()
for(i in 1:length(vnames)){
dat[[i]]<-xml_text(xml_find_first(nodeset,paste0(".//d1:",vnames[[i]]),xml_ns(xmlfile)))
}
dat<-as.data.frame(dat,col.names=vnames)
但这种方法只有在xml_find_first
真正给你想要的一切时才有效。我可以使用 xml_find_all
,但这会使列表长度不相等以进行合并。我得到一长串 C
的列表,但我不知道哪个与哪个项目对应。
我当然可以遍历每个项目和 xml_find_all
C
,但这似乎效率不高。
有没有更简单的方法来做到这一点?
这是一个可能的解决方案,我不确定最后的结果是否是你想要的。
如果所有数据仅向下一级,则此方法效果很好。如果数据在 xml 中向下存储多个级别,则需要扩展此解决方案。基本方法是将所有 Item 节点解析出来。从每个项目节点中收集所有子节点的信息,然后通过计算每个项目中的子节点数来创建项目索引。然后将所有数据存储在一个 3 列数据框中:ItemIndex、Child Name 和 value。从这里开始,就是转换为所需的最终格式。
library(xml2)
page<-read_xml("<root>
<Item>
<A>text1</A>
<B>text2</B>
<C>text3</C>
<C>text4</C>
<C>text5</C>
<D>text6</D>
</Item>
<Item>
<A>text12</A>
<B>text22</B>
<C>text32</C>
</Item>
</root>")
#find all items and store as a list
items<-xml_find_all(page, ".//Item")
#extract all children's names and values
nodenames<-xml_name(xml_children(items))
contents<-trimws(xml_text(xml_children(items)))
#Need to create an index to associate the nodes/contents with each item
itemindex<-rep(1:length(items), times=sapply(items, function(x) {length(xml_children(x))}))
#store all information in data frame.
df<-data.frame(itemindex, nodenames, contents)
#Convert from long to wide format
library(tidyr)
pivot_wider(df, id_cols= itemindex, names_from = nodenames,
values_from = contents) # %>% unnest(cols = c(A, B, C, D))
# A tibble: 2 x 5
itemindex A B C D
<int> <list<fct>> <list<fct>> <list<fct>> <list<fct>>
1 [1] [1] [3] [1]
2 [1] [1] [1] [0]
我有一个 XML 文档,如下所示:
<root>
<Item>
<A>text1</A>
<B>text2</B>
<C>text3</C>
<C>text4</C>
<C>text5</C>
<D>text6</D>
...
</Item>
<Item>
...
</Item>
...
</root>
它相对简单,但有一个复杂因素:每个 item
可以有任意数量的 C
。
最终,我希望在 table 中拥有它,例如:
A B C D
1 text1 text2 <list [3]> text6
我已经为其他变量创建了 table(可能是一种混乱的方式,但它有效):
vnames<-c("A","B","D")
dat<-list()
for(i in 1:length(vnames)){
dat[[i]]<-xml_text(xml_find_first(nodeset,paste0(".//d1:",vnames[[i]]),xml_ns(xmlfile)))
}
dat<-as.data.frame(dat,col.names=vnames)
但这种方法只有在xml_find_first
真正给你想要的一切时才有效。我可以使用 xml_find_all
,但这会使列表长度不相等以进行合并。我得到一长串 C
的列表,但我不知道哪个与哪个项目对应。
我当然可以遍历每个项目和 xml_find_all
C
,但这似乎效率不高。
有没有更简单的方法来做到这一点?
这是一个可能的解决方案,我不确定最后的结果是否是你想要的。
如果所有数据仅向下一级,则此方法效果很好。如果数据在 xml 中向下存储多个级别,则需要扩展此解决方案。基本方法是将所有 Item 节点解析出来。从每个项目节点中收集所有子节点的信息,然后通过计算每个项目中的子节点数来创建项目索引。然后将所有数据存储在一个 3 列数据框中:ItemIndex、Child Name 和 value。从这里开始,就是转换为所需的最终格式。
library(xml2)
page<-read_xml("<root>
<Item>
<A>text1</A>
<B>text2</B>
<C>text3</C>
<C>text4</C>
<C>text5</C>
<D>text6</D>
</Item>
<Item>
<A>text12</A>
<B>text22</B>
<C>text32</C>
</Item>
</root>")
#find all items and store as a list
items<-xml_find_all(page, ".//Item")
#extract all children's names and values
nodenames<-xml_name(xml_children(items))
contents<-trimws(xml_text(xml_children(items)))
#Need to create an index to associate the nodes/contents with each item
itemindex<-rep(1:length(items), times=sapply(items, function(x) {length(xml_children(x))}))
#store all information in data frame.
df<-data.frame(itemindex, nodenames, contents)
#Convert from long to wide format
library(tidyr)
pivot_wider(df, id_cols= itemindex, names_from = nodenames,
values_from = contents) # %>% unnest(cols = c(A, B, C, D))
# A tibble: 2 x 5
itemindex A B C D
<int> <list<fct>> <list<fct>> <list<fct>> <list<fct>>
1 [1] [1] [3] [1]
2 [1] [1] [1] [0]