如何高效将大型嵌套XML转换为R DataFrame?
优化嵌套XML数据提取效率:关联信息提取节点
我有一个嵌套结构的XML文件,需要提取所有<np>节点的数据,同时将每个<np>所属的<folder>节点下的group和ID信息添加到结果的每一行。
现有的解决方案可以处理小样本,但面对包含数千个节点的大文件时,处理速度极慢,急需更高效的实现方式。
XML示例
<File> <Time> <date>20220301</date> <Name>1</Name> <folder> <group>800</group> <ID>ESK</ID> <Type>S</Type> <Customer>1</Customer> <currency>USD</currency> <Port> <ec>X</ec> <np> <A>FIRST</A> <B>ES</B> <C>GOR</C> <D>2021</D> <E>-1000</E> </np> <np> <A>TEST</A> <B>ES</B> <C>RUN</C> <D>202303</D> <E>202303</E> <F>C</F> <G>3200</G> <H>32</H> </np> </Port> </folder> <folder> <group>900</group> <ID>ABC</ID> <Type>D</Type> <Customer>1</Customer> <currency>USD</currency> <Port> <ec>X</ec> <np> <A>CAT</A> <B>ES</B> <C>GO</C> <D>202303</D> <E>-500</E> </np> </Port> </folder> </Time> </File>
当前低效代码
URL <- 'H:/testSO.xml' doc <- read_xml(URL) df <- xml_find_all(doc, ".//np") %>% map_df( function(x) { set_names( c( xml_find_all( x, "./ancestor::folder/group") %>% xml_text(), xml_find_all( x, "./ancestor::folder/ID") %>% xml_text(), xml_find_all( x, ".//A") %>% xml_text(), xml_find_all( x, ".//B") %>% xml_text(), xml_find_all( x, ".//C") %>% xml_text(), xml_find_all( x, ".//D") %>% xml_text(), xml_find_all( x, ".//E") %>% xml_text()), #set the column names c( "group","id", "A", "B", "C","D","E") ) %>% as.list() %>% #make list flatten_df() }) %>% type_convert() head(df)
优化思路
原方案的核心问题是每个<np>节点都重复查询其祖先<folder>的信息,当<np>数量达数千级时,XML查询的重复开销会被放大。优化方向改为先遍历<folder>节点,一次性获取当前folder的group和ID,再批量处理该folder下的所有<np>节点,避免重复的祖先查询。
优化后代码
library(xml2) library(tidyverse) URL <- 'H:/testSO.xml' doc <- read_xml(URL) # 遍历所有folder节点,批量处理每个folder下的np df_opt <- xml_find_all(doc, ".//folder") %>% map_df(function(folder_node) { # 一次性获取当前folder的group和ID group_val <- xml_find_first(folder_node, "./group") %>% xml_text() id_val <- xml_find_first(folder_node, "./ID") %>% xml_text() # 获取当前folder下的所有np节点 np_nodes <- xml_find_all(folder_node, "./Port/np") # 处理每个np节点,提取A-E字段,合并group和ID map_df(np_nodes, function(np_node) { tibble( group = group_val, id = id_val, A = xml_find_first(np_node, "./A") %>% xml_text(), B = xml_find_first(np_node, "./B") %>% xml_text(), C = xml_find_first(np_node, "./C") %>% xml_text(), D = xml_find_first(np_node, "./D") %>% xml_text(), E = xml_find_first(np_node, "./E") %>% xml_text() ) }) }) %>% type_convert() head(df_opt)
效率提升点
- 减少XML查询次数:原方案每个
<np>执行2次祖先查询,优化后每个<folder>仅执行2次查询,查询次数从O(N)(N为<np>数量)降至O(M)(M为<folder>数量,M远小于N)。 - 精准节点定位:使用
./Port/np替代.//np,避免全局扫描XML树;用xml_find_first替代xml_find_all,明确获取单个节点文本,减少不必要的结果处理。 - 批量数据构造:在
<folder>层级批量处理下属<np>,利用tibble直接构造行数据,减少list转换的额外开销。
内容的提问来源于stack exchange,提问作者1998postfinance
相关产品推荐
相关产品推荐

