如何将嵌套 XML 导入 R 数据框
How to import nested XML into R data frame
我希望将以下 XML 文档导入数据框:
http://opensource.adobe.com/Spry/data/donuts.xml
应该创建了 3 个数据框:
- 项目 -(字段 = ID 类型名称 PPU)
- Batters -(字段 = BatterID、BatterName、ItemID - Items 数据框的键)
- Toppings -(字段 = ToppingID、ToppingName、ItemID - Items 数据框的键)
(数据不需要是 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(.......) %>% .....
我希望将以下 XML 文档导入数据框: http://opensource.adobe.com/Spry/data/donuts.xml
应该创建了 3 个数据框:
- 项目 -(字段 = ID 类型名称 PPU)
- Batters -(字段 = BatterID、BatterName、ItemID - Items 数据框的键)
- Toppings -(字段 = ToppingID、ToppingName、ItemID - Items 数据框的键)
(数据不需要是 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(.......) %>% .....