xml2 - более эффективно получать информацию от родительских узлов

у меня есть xml, который выглядит так:

...
<node id=1>
 <child>a</child>
 <child>b</child>
 <child>c</child>
</node>
<node id=2>
 <child>d</child>
 <child>e</child>
</node>
...

Я ищу data.frame, относящийся к соответствующему родительскому элементу дочернего элемента:

node_id  child_text
      1           a
      1           b
      1           c
      2           d
      2           e

Есть только два решения, которые я мог бы придумать с помощью xml2:

(a) создайте набор дочерних узлов с помощью xml_find_all (...), а затем используйте цикл for для перемещения по структуре xml для получения необходимой информации. Очевидно, что крайне неэффективно.

(б) получить набор родительских узлов и набор дочерних узлов для каждого. Извлеките информацию из родительского набора и посчитайте, сколько детей имеет каждый родитель. Затем использовал rep (информация, no_of_children) для заполнения столбца node_id сверху. Лучше, но все равно глупо.

Я полагаю, должен быть более эффективный способ? Я рад любому предложению, поскольку я использую эти подходы уже более месяца, имея дело с гигабитами - очень неэффективных - структур данных в формате xml. Я также не ограничен использованием xml2 и могу переключиться на другой язык программирования, если это резко повысит производительность. Единственная важная часть - это то, что я хотел бы загрузить данные прямо оттуда в базу данных.


person thomas    schedule 26.10.2017    source источник


Ответы (1)


Учитывая очень общий пример, который вы привели, я бы использовал rvest вместе с purrr:

x <- "<node id=1>
   <child>a</child>
   <child>b</child>
   <child>c</child>
  </node>
  <node id=2>
   <child>d</child>
   <child>e</child>
  </node>"


library(rvest)
library(purrr)

read_html(x) %>% 
  html_nodes('node') %>% 
  map_df(~{
    .x %>% 
      html_nodes('child') %>% 
      html_text() -> child
    data.frame(child = child, stringsAsFactors = FALSE)
  }, .id = 'node')

#>   node child
#> 1    1     a
#> 2    1     b
#> 3    1     c
#> 4    2     d
#> 5    2     e

И какой-то тест для сравнения с вашими методами:

rbenchmark::benchmark(read_html(xml) %>% 
                        html_nodes('node') %>% 
                        map_df(~{
                          .x %>% 
                            html_nodes('child') %>% 
                            html_text() -> child
                          data.frame(child = child, stringsAsFactors = FALSE)
                        }, .id = 'node'), columns = c('replications', 'elapsed', 'user.self'))
#>   replications elapsed user.self
#> 1          100   0.964     0.964
person GGamba    schedule 26.10.2017
comment
На самом деле этот подход намного медленнее, чем подходы, которые я уже указывал: Контрольное время для вашего подхода: 1,45 Для решения (a): 0,46 Для решения (b): 0,12 - person thomas; 26.10.2017