2016-11-25 73 views
0

我编写了一个将大型异构XML文件拆分为数据框的功能,其中拆分由xpath表达式完成。异构我的意思是说,感兴趣的项目属于一组不同的“列”结构。但是,对于大小为50K的项目和5种类型的XML文件,代码似乎比我预期的更为“低迷”。通过xpath表达式将XML文档快速拆分为data.frames

问题是:是否有现有的功能来做到这一点,我错过了,如果没有,是否有一种明显的方式来提高下面的代码速度?

这里是我正在考虑的一种XML结构的一个小例子:

xmldoc <- xml2::read_xml(
    '<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
    <resp> 

    <respMeta> 
     <status>200</status> 
     <!-- ... -->  
    </respMeta> 

    <content> 
     <list> 
     <item> 
      <Type>Type1</Type> 
      <ColA>Foo</ColA> 
      <ColB>Bar</ColB> 
     </item> 
     <item> 
      <Type>Type2</Type> 
      <ColC>Baz</ColC> 
     </item> 
     <item> 
      <Type>Type3</Type> 
      <ColA>Lorem</ColA> 
      <ColB>Ipsum</ColB> 
      <ColC>Dolor</ColC> 
     </item> 
     </list> 
     <!-- ... many many more entries here --> 
    </content> 

    </resp>') 

的目标是将其转换为ň数据帧,其中ñ是唯一值的数量//item/Type(在解析时未知)。

这里是我的实现:

#' Split XML Document into Dataframes by Xpath Expression 
#' 
#' @param xml An (xml2) xml document object. 
#' 
#' @param xpath the path to the values to split by. \code{xml_text} is used 
#' to get the value. 
#' 
#' @importFrom xml2 xml_text xml_find_all xml_find_first xml_children xml_name 
#' @importFrom stats setNames 
#' @importFrom dplyr bind_cols 
#' @importFrom magrittr %>% 
#' 
#' @return list of data frames (tbl_df) 
#' 
#' @export 
xml_to_dfs <- function(xml, xpath) 
{ 
    u <- xml_find_all(xml, xpath) %>% xml_text %>% unique %>% sort 

    select <- paste0(xpath, "[. ='", u, "']/..") %>% setNames(u) 

    paths <- 
    lapply(select, . %>% xml_find_first(x = xml) %>% xml_children %>% xml_name) 

    queries <- Map(paste, select, paths, MoreArgs = list(sep = "/")) 

    columns <- 
    lapply(queries, . %>% lapply(. %>% xml_find_all(x = xml) %>% xml_text)) 

    Map(setNames, columns, paths) %>% lapply(bind_cols) 
} 

最小例的结果,只有一个在每帧行,则是:

xml_to_dfs(xmldoc, "//item/Type") 
$Type1 
# A tibble: 1 × 3 
    Type ColA ColB 
    <chr> <chr> <chr> 
1 Type1 Foo Bar 

$Type2 
# A tibble: 1 × 2 
    Type ColC 
    <chr> <chr> 
1 Type2 Baz 

$Type3 
# A tibble: 1 × 4 
    Type ColA ColB ColC 
    <chr> <chr> <chr> <chr> 
1 Type3 Lorem Ipsum Dolor 

回答

1

什么是这样的:

require(xml2) 
require(purrr) 

oc <- read_xml("path-to-xml-file") 
xml_find_all(doc, ".//item") %>% 
    map(xml_children) %>% 
    map(~as_tibble(t(set_names(xml_text(.), xml_name(.))))) #or map_df 

我甚至会在map_df的最后一行给你:

# A tibble: 3 × 4 
    Type ColA ColB ColC 
    <chr> <chr> <chr> <chr> 
1 Type1 Foo Bar <NA> 
2 Type2 <NA> <NA> Baz 
3 Type3 Lorem Ipsum Dolor 

P.S:这也涉及到:https://github.com/hadley/purrr/issues/255

+0

对我来说,速度的解决方案,我的解决方案几乎是完全一样的,〜20secs。你的解决方案更短,因此+1;但也需要一些后期处理,因为结果应该是dfs列表,只有与每种类型相关的列(尽管这应该足够快)。 – Stefan