Как разобрать информацию из родительских узлов XML в R - PullRequest
1 голос
/ 05 июля 2019

Я использую продукт поставщика, который производит некоторые интересные экспорты XML в формате файла .udf. Моя цель - сохранить иерархию элементов XML в фрейме данных.

Элементы - это вещи, которые мы встроили в структуру папок внутри продукта. Name в каждом элементе - это имя, которое мы дали папке ИЛИ имя того, что мы создаем (XML называет их всех «Элементом», независимо от того, создается ли это элемент или просто папка).

Это образец XML. Предположим, что существует неизвестное количество папок, и, в конечном счете, будет имя и определение, которое я хочу записать и которое задает количество строк, которые я хочу в моем фрейме данных:

<Root>
 <Items>
    <Item Id="2148" Type="Category" Name="Group 1">
        <Item Id="2148" Type="Category" Name="SubGroup A">
            <Item Id="2347" Type="Category" Name="Name1"> [Definition of Name 1] </Item>
            <Item Id="2348" Type="Category" Name="Name2"> [Definition of Name 2] </Item>
        </Item>
        <Item Id="2148" Type="Category" Name="SubGroup A">
            <Item Id="2347" Type="Category" Name="Name1"> [Definition of Name 1] </Item>
            <Item Id="2348" Type="Category" Name="Name2"> [Definition of Name 2] </Item>
        </Item>
    </Item>
</Items>

Я хочу, чтобы кадр данных выглядел так, где будет v1 - vN в зависимости от количества подпапок. Достаточно хорошим решением было бы просто выбрать несколько уровней подпапок и предположить, что их не больше (не более 5 уровней, но нужно работать на меньшем количестве).

v1 <- "Group 1"
v2 <- c("SubGroup A", "SubGroup B")
name <- c("Name1", "Name2", "Name 3", "Name4")
definition <- "Definition of name"

df <- tibble::as_tibble(cbind(v1, v2, name, definition))

Я могу получить строку для каждой вещи со столбцом с надписью «Root.Items.Item.Item», используя пакет xml2, но не могу извлечь информацию из этих родительских узлов.

1 Ответ

0 голосов
/ 09 июля 2019

Мне удалось найти достаточно хорошее решение частично с помощью ответа, который кто-то опубликовал, но впоследствии удалил (мне довелось увидеть его и скопировать его код, прежде чем они удалили).

Ключом для меня было изучение функции attributes(), которая извлекала нужную мне информацию из списков в более высоких частях иерархии. Мое решение ручное и не элегантное, но оно работает.

Ниже показано, что я сделал, чтобы получить данные до двух уровней. Я продолжаю вкладывать эти циклы while и корректировать их логику до глубины 6 уровней. Предположим, что XML хранится в doc.

xml_ls <- xml2::as_list(xml2::xml_find_all(doc, ".//Item"))

a_len <- length(attributes(xml_ls[[1]])$names)
a_name <- attributes(xml_ls[[1]])$Name
a_id <- attributes(xml_ls[[1]])$Id
a_item <- xml_ls[[1]]$Item

cat_level <- 1
cat_names <- a_name
cat_ids <- a_id
cat_items <- a_item

a_dir <- 1
#print(paste0("Top Level: ", a_name))
while (a_dir <= a_len){
  b_len <- length(attributes(xml_ls[[1]][[a_dir]])$names)
  b_name <- attributes(xml_ls[[1]][[a_dir]])$Name
  b_id <- attributes(xml_ls[[1]][[a_dir]])$Id
  b_item <- xml_ls[[1]][[a_dir]]$Item
  #print(paste0("Level B: ", b_name))

  cat_level <- c(cat_level, 2)
  cat_names <- c(cat_names, b_name)
  cat_ids <- c(cat_ids, b_id)
  cat_items <- c(cat_items, b_item)


  b_dir <- 1
    while (b_dir <= b_len){

      c_len <- length(attributes(xml_ls[[1]][[a_dir]][[b_dir]])$names)
      c_name <- attributes(xml_ls[[1]][[a_dir]][[b_dir]])$Name
      c_id <- attributes(xml_ls[[1]][[a_dir]][[b_dir]])$Id
      c_item <- xml_ls[[1]][[a_dir]][[b_dir]]$Item
      #print(paste0("Level C: ", c_name))

      cat_level <- c(cat_level, 3)
      cat_names <- c(cat_names, c_name)
      cat_ids <- c(cat_ids, c_id)
      cat_items <- c(cat_items, c_item)

Это создает списки, которые начинаются с "cat_". Затем я помещаю их в фрейм данных и манипулирую ими в нужной мне структуре:

df <- as_tibble(cbind(cat_level, cat_ids, cat_names)) %>%
  mutate(cat_level = as.integer(cat_level))

df2 <- df %>%
  mutate(a = if_else(cat_level == 1, cat_names, NA_character_),
         b = if_else(cat_level == 2, cat_names, NA_character_),
         c = if_else(cat_level == 3, cat_names, NA_character_),
         d = if_else(cat_level == 4, cat_names, NA_character_),
         e = if_else(cat_level == 5, cat_names, NA_character_),
         f = if_else(cat_level == 6, cat_names, NA_character_)
         ) %>%
  fill(c(a, b, c, d, e, f)) %>%
  mutate(lvl1 = if_else(cat_level >= 1, a, NA_character_),
         lvl2 = if_else(cat_level >= 2, b, NA_character_),
         lvl3 = if_else(cat_level >= 3, c, NA_character_),
         lvl4 = if_else(cat_level >= 4, d, NA_character_),
         lvl5 = if_else(cat_level >= 5, e, NA_character_),
         lvl6 = if_else(cat_level >= 6, f, NA_character_)) %>%
  select(-a, -b, -c, -d, -e, -f)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...