Как импортировать вложенный XML в фрейм данных R - PullRequest
0 голосов
/ 04 октября 2018

Я хочу импортировать следующий XML-документ в фреймы данных: http://opensource.adobe.com/Spry/data/donuts.xml

Должно быть создано 3 фрейма данных:

  1. Элементы - (Поля = ID Имя типаPPU)
  2. Batters - (Fields = 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 для одного элемента, но когда япопытался выполнить функцию, в которой произошел сбой:

#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)

(NB. Этот набор данных является подтверждением концепции, поэтому я ищу метод, который затем можно адаптировать для других файлов XML, над которыми я ожидаю работать).

1 Ответ

0 голосов
/ 04 октября 2018
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  
...