[英]How to import nested XML into R data frame
我希望將以下 XML 文檔導入數據框中: http : //opensource.adobe.com/Spry/data/donuts.xml
應該創建 3 個數據框:
(數據不需要是 3NF - 即每個擊球手可以針對它列出的每個項目重復)
使用 XML2 包,到目前為止,我已經使用以下代碼導入 XML 並將其轉換為嵌套列表:
library(xml2)
xmlobj <- read_xml("http://opensource.adobe.com/Spry/data/donuts.xml")
ls1 <- as_list(xmlobj) #Converts XML to a nested list
我現在希望將列表解析/展平為 3 個數據框,如上所述。
如何最好地實現這一目標? 是通過一系列循環(lapply/map),將對象傳遞給向量然后加載數據框嗎? 或者我應該完全避免使用 XML2 / Lists 並使用 XML 包並使用 XPath 類型語法實現這一點?
我嘗試了以下操作,可以為單個項目提取 Item 屬性和元素,但是當我嘗試 lapply 函數時,它崩潰了:
#Function for pulling out item attributes from list
ItemDF <- function(myItem){
#Gather Item data into DF including attributes
itemFrame <- data_frame(
id = attr(myItem$item,'id'),
type = attr(myItem$item,'type'),
name = unlist(myItem$item$name),
ppu = unlist(myItem$item$ppu)
)
return(itemFrame)
}
#Single instance
df1 <- ItemDF(ls1$items[1])
df1
#Lapply across all items throws an error
lapply(ls1$items,ItemDF)
(注意這個數據集是一個概念證明,所以我正在尋找一種方法,然后我可以適應我希望使用的其他 XML 文件)。
library(xml2)
library( tidyverse )
xmlobj <- read_xml("http://opensource.adobe.com/Spry/data/donuts.xml")
df_items <- data.frame(
id = xml_find_all( xmlobj, ".//item" ) %>% xml_attr( "id" ),
type = xml_find_all( xmlobj, ".//item" ) %>% xml_attr( "type" ),
name = xml_find_all( xmlobj, ".//item/name" ) %>% xml_text(),
ppu = xml_find_all( xmlobj, ".//item/ppu" ) %>% xml_text(),
stringsAsFactors = FALSE )
# id type name ppu
# 1 0001 donut Cake 0.55
# 2 0002 donut Raised 0.55
# 3 0003 donut Buttermilk 0.55
# 4 0004 bar Bar 0.75
# 5 0005 twist Twist 0.65
# 6 0006 filled Filled 0.75
df_batters <- xml_find_all( xmlobj, ".//item" ) %>%
map_df(~{
set_names(
xml_find_all(.x, ".//batters/batter") %>% xml_attr( "id" ),
xml_find_all(.x, ".//batters/batter") %>% xml_text()
) %>%
as.list() %>%
flatten_df() %>%
mutate(itemID = xml_attr(.x, "id" ) )
}) %>%
type_convert() %>%
gather( batter, batterID, -itemID, na.rm = TRUE) %>%
select( batterID, batter, itemID )
# # A tibble: 10 x 3
# batterID batter itemID
# * <int> <chr> <chr>
# 1 1001 Regular 0001
# 2 1001 Regular 0002
# 3 1001 Regular 0003
# 4 1001 Regular 0004
# 5 1001 Regular 0005
# 6 1001 Regular 0006
# 7 1002 Chocolate 0001
# 8 1002 Chocolate 0003
# 9 1003 Blueberry 0001
# 10 1003 Devil's Food 0001
df_toppings <- xml_find_all( xmlobj, ".//item" ) %>%
map_df(~{
set_names(
xml_find_all(.x, ".//topping") %>% xml_attr( "id" ),
xml_find_all(.x, ".//topping") %>% xml_text()
) %>%
as.list() %>%
flatten_df() %>%
mutate(itemID = xml_attr(.x, "id" ) )
}) %>%
type_convert() %>%
gather( topping, toppingID, -itemID, na.rm = TRUE) %>%
select( toppingID, topping, itemID )
# # A tibble: 20 x 3
# toppingID topping itemID
# * <int> <chr> <chr>
# 1 5001 None 0001
# 2 5001 None 0002
# 3 5002 Glazed 0001
# 4 5002 Glazed 0002
# 5 5002 Glazed 0005
# 6 5002 Glazed 0006
# 7 5005 Sugar 0001
# 8 5005 Sugar 0002
# 9 5005 Sugar 0005
# 10 5007 Powdered Sugar 0001
# 11 5007 Powdered Sugar 0006
# 12 5006 Chocolate with Sprinkles 0001
# 13 5003 Chocolate 0001
# 14 5003 Chocolate 0002
# 15 5003 Chocolate 0004
# 16 5003 Chocolate 0006
# 17 5004 Maple 0001
# 18 5004 Maple 0002
# 19 5004 Maple 0004
# 20 5004 Maple 0006
我的 2 美分用於物品數據部分的關鍵(僅限擊球手):
df_batters <- xml_find_all(xmlobj, ".//item") %>%
map_df(~{
bind_cols(
itemID = xml_attr(.x, "id"),
batterID = xml_find_all(.x, ".//batters/batter") %>% xml_attr("id"),
batter = xml_find_all(.x, ".//batters/batter") %>% xml_text()
)}) %>% type_convert()
# itemID batterID batter
# <chr> <dbl> <chr>
# 1 0001 1001 Regular
# 2 0001 1002 Chocolate
# 3 0001 1003 Blueberry
# 4 0001 1003 Devil's Food
# 5 0002 1001 Regular
# 6 0003 1001 Regular
# 7 0003 1002 Chocolate
# 8 0004 1001 Regular
# 9 0005 1001 Regular
# 10 0006 1001 Regular
以防其他人遇到我的問題。 上面的例子工作得很好。 但是,在使用上面的代碼時,我必須處理的 xml 文件沒有產生任何結果。 我必須首先在管道的開頭引入根節點才能使其工作,即:
xmlobj %>%
xml_root() %>%
xml_find_all(".//item") %>%
map(.......) %>% .....
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.