XML в R: несколько дочерних элементов с одинаковым именем без циклов - PullRequest
1 голос
/ 21 октября 2019

У меня есть XML-документ, подобный показанному ниже:

<root>
    <Item>
        <A>text1</A>
        <B>text2</B>
        <C>text3</C>
        <C>text4</C>
        <C>text5</C>
        <D>text6</D>
        ...
    </Item>
    <Item>
        ...
    </Item>
    ...
</root>

Это относительно просто с одним усложняющим фактором: каждый item может иметь любое количество C с.

В конечном итоге, я бы хотел, чтобы это было в таблице вроде:

  A     B     C          D    
1 text1 text2 <list [3]> text6

Я уже создал свою таблицу для других переменных (что, вероятно, грязно, но работает):

vnames<-c("A","B","D")
dat<-list()
for(i in 1:length(vnames)){
    dat[[i]]<-xml_text(xml_find_first(nodeset,paste0(".//d1:",vnames[[i]]),xml_ns(xmlfile)))
}
dat<-as.data.frame(dat,col.names=vnames)

Но этот метод работает только тогда, когда xml_find_first фактически дает вам все, что вы хотите. Я мог бы использовать xml_find_all, но это делает длины списка неравными для слияния. Я получаю длинный список C с, и я не знаю, какой из них идет с каким элементом.

Я, конечно, могу пройтись по каждому элементу и xml_find_all C с, но это кажется неэффективным,Есть ли более простой способ сделать это?

Извинения, если об этом уже спрашивали;Я не мог найти это. Кроме того, я новичок в XML, поэтому может потребоваться совершенно новый подход. Спасибо!

1 Ответ

1 голос
/ 22 октября 2019

Вот возможное решение, я не уверен, что вы ищете конечный результат.

Это хорошо работает, если все данные находятся только на один уровень ниже. Если данные хранятся на нескольких уровнях вниз в XML, то это решение необходимо расширить. Основной подход состоит в том, чтобы разобрать все узлы Item. Соберите информацию со всех дочерних узлов в каждом узле элемента, затем создайте индекс элемента путем подсчета количества дочерних элементов в каждом элементе. Затем сохраните все данные в фрейме данных из 3 столбцов: ItemIndex, Child Name и value. Отсюда вопрос конвертации в желаемый конечный формат.

library(xml2)

page<-read_xml("<root>
    <Item>
        <A>text1</A>
        <B>text2</B>
        <C>text3</C>
        <C>text4</C>
        <C>text5</C>
        <D>text6</D>
    </Item>
    <Item>
        <A>text12</A>
        <B>text22</B>
        <C>text32</C>
    </Item>
</root>")

#find all items and store as a list
items<-xml_find_all(page, ".//Item")

#extract all children's names and values 
nodenames<-xml_name(xml_children(items))
contents<-trimws(xml_text(xml_children(items)))

#Need to create an index to associate the nodes/contents with each item
itemindex<-rep(1:length(items), times=sapply(items, function(x) {length(xml_children(x))}))

#store all information in data frame.
df<-data.frame(itemindex, nodenames, contents)

#Convert from long to wide format
library(tidyr)
pivot_wider(df, id_cols= itemindex, names_from = nodenames,
            values_from = contents)  # %>% unnest(cols = c(A, B, C, D))

# A tibble: 2 x 5
itemindex       A           B           C           D
<int> <list<fct>> <list<fct>> <list<fct>> <list<fct>>
    1         [1]         [1]         [3]         [1]
    2         [1]         [1]         [1]         [0]
...