首页 > 解决方案 > 如何将嵌套的 XML 导入 R 数据框

问题描述

我希望将以下 XML 文档导入数据框: http: //opensource.adobe.com/Spry/data/donuts.xml

应该创建 3 个数据框:

  1. 项目 - (字段 = ID 类型名称 PPU)
  2. 击球手 - (字段 = BatterID、BatterName、ItemID - Items 数据框的键)
  3. Toppings - (Fields = 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 文件)。

标签: rxmlxml-parsingtidyversexml2

解决方案


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  

推荐阅读