簡體   English   中英

如何將嵌套的 XML 導入 R 數據框

[英]How to import nested XML into R data frame

我希望將以下 XML 文檔導入數據框中: http : //opensource.adobe.com/Spry/data/donuts.xml

應該創建 3 個數據框:

  1. 項目 - (字段 = ID 類型名稱 PPU)
  2. Batters - (Fields = BatterID, BatterName, ItemID - Items 數據框的鍵)
  3. 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(.......) %>% .....

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM