[英]Parsing uneven XML into R data.frame
我正在嘗試將大型XML文件解析為R數據幀。 XML的結構參差不齊,並不總是包含所有元素,有時每個節點包含1個以上的重復元素。
XML是:
<root>
<members>
<member>
<id>1</id>
<educations>
<education>
<institution>Sydney University</institution>
<program>Masters of Science</program>
<start-date>2010</start-date>
<end-date>2015</end-date>
<description></description>
</education>
<education>
<institution>UTS</institution>
<program>Bachelor of Science</program>
<start-date>2004</start-date>
<end-date>2008</end-date>
</education>
</educations>
</member>
<member>
<id>2</id>
</member>
<member>
<id>3</id>
<educations>
<education>
<is-current>true</is-current>
<institution>Monash Univeristy</institution>
<start-date>2010</start-date>
</education>
</educations>
</member>
</members>
</root>
期望的輸出表將為每個成員及其教育塊提供重復的ID。 因此,每個教育階段ID 1將具有2行,ID 3將僅具有1行。
使用xmlToList()會創建過多的列,但我找不到為每個子節點復制ID的方法。
這是一個公認的笨拙的解決方案,可能還有更優雅的tidiverse式解決方案。 但是,這似乎可以解決問題。
library(XML)
library(plyr)
names_use <- c("institution", "program", "start-date", "end-date","description")
list_xml <- xmlToList(test)
df_use <- ldply(list_xml$member, function(x){
if(is.null(x$educations)){
df_edu <- data.frame(x$id,t(rep(NA,5)))
names(df_edu) <- c("id",names_use)
return(df_edu)
}
df_res <- ldply(x$educations, function(edu_tmp){
df_edu <- as.data.frame(t(unlist(edu_tmp)),
stringsAsFactors = F)
for(i_names in names_use){
if(!i_names %in% names(df_edu)){
df_edu[,i_names] <- NA
}
}
return(df_edu)
})
df_res$id <- x$id
return(df_res[,c("id",names_use)])
})
df_use <- df_use[,c("id",names_use)]
df_use
id institution program start-date end-date description
1 1 Sydney University Masters of Science 2010 2015 NA
2 1 UTS Bachelor of Science 2004 2008 NA
3 2 <NA> <NA> <NA> <NA> NA
4 3 Monash Univeristy <NA> 2010 <NA> NA
另一種方法:
library(xml2)
library(tidyverse)
我喜歡整潔的列名,因此我們將添加一個輔助函數:
mgca <- function(tbl) {
x <- colnames(tbl)
x <- tolower(x)
x <- gsub("[[:punct:][:space:]]+", "_", x)
x <- gsub("_+", "_", x)
x <- gsub("(^_|_$)", "", x)
x <- make.unique(x, sep = "_")
colnames(tbl) <- x
tbl
}
doc <- read_xml("so.xml")
這里的想法是首先遍歷每個<member>
,然后為其提取<id>
。
進入<member>
看看我們是否有孩子。 如果不是,則只需在數據框中返回<id>
。 如果這樣做,則進一步遍歷每個<education>
節點,識別出存在的子節點,然后僅將其拉出並為每個子節點創建一個數據幀,包括<id>
,最后將所有子節點拖到最后一個數據幀中清理列名並獲得更好的列類型:
xml_find_all(doc, ".//member") %>%
map_df(~{
id <- (xml_find_first(.x, ".//id") %>% xml_text()) %||% NA_character_
edus <- xml_find_all(.x, ".//educations/education")
if (length(edus) > 0) {
map_df(edus, ~{
kid <- .x
nodes <- xml_children(kid) %>% xml_name()
map(nodes, ~xml_find_first(kid, sprintf(".//%s", .x)) %>%
xml_text()) %>%
set_names(nodes) %>%
append(list(id = id)) %>%
flatten_df()
})
} else {
data_frame(id = id)
}
}) %>%
mgca() %>%
type_convert()
## # A tibble: 4 x 7
## institution program start_date end_date description id is_current
## <chr> <chr> <int> <int> <chr> <int> <chr>
## 1 Sydney University Masters of Science 2010 2015 <NA> 1 <NA>
## 2 UTS Bachelor of Science 2004 2008 <NA> 1 <NA>
## 3 <NA> <NA> NA NA <NA> 2 <NA>
## 4 Monash Univeristy <NA> 2010 NA <NA> 3 true
由於type_convert()
無法讀懂思想,因此您可能必須自己將is_current
轉換為邏輯向量。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.