Некорректное извлечение повторяющихся значений из списка объектов XML в R - PullRequest
0 голосов
/ 08 мая 2018

У меня проблема с использованием lapply и xml_find_first из пакета xml2 для извлечения узлов из списка объектов XML. Я извлекаю несколько тысяч записей из Scopus API. Поскольку я могу получить только 25 записей одновременно, я запускаю его, поэтому получаю список из 100+ элементов по 25 записей в каждой. Я знаю, что в некоторых записях пропущены значения, поэтому моя цель - перемешать, пока я не получу список, в котором каждая запись является отдельным элементом, а затем используйте lapply и xml_find_first, чтобы получить нулевые значения, где подходящее. Проблема в том, что я получаю повторяющиеся значения, как будто все еще вложено в их начальные списки.

Вот воспроизводимый пример со списком из 2 элементов по 2 записи в каждом, из которых citedby-count отсутствует в последнем:

```{r}
library(xml2)

# Simulate how data come in from Scopus
# Build 2 list elements, 2 entries each
el1 <- read_xml(
"<feed>
  <blah>Bunch of stuff I don't need</blah>
  <blah>Bunch of other stuff I don't need</blah>
  <entry>
    <eid>2-s2.0-1542382496</eid>
    <citedby-count>9385</citedby-count>
  </entry>
  <entry>
    <eid>2-s2.0-0032721879</eid>
    <citedby-count>4040</citedby-count>
  </entry>
</feed>"
)
el2 <- read_xml( # This one's missing citedby-count for last entry
"<feed>
  <blah>Bunch of stuff I don't need</blah>
  <blah>Bunch of other stuff I don't need</blah>
  <entry>
    <eid>2-s2.0-0041751098</eid>
    <citedby-count>3793</citedby-count>
  </entry>
  <entry>
    <eid>2-s2.0-73449149291</eid>
  </entry>
</feed>"
)
# Combine into list
lst <- list(el1,el2)
# Check
lst
```

Это дает мне:

enter image description here

Моя цель - вытащить записи, чтобы они были элементами списка. Таким образом, xml_find_first должен вставить нулевое значение для записи, где отсутствует citedby-count.

```{r}
# Pull entry nodes
lst2 <- lapply(lst, xml_find_all, "//entry")
# Unlist
lst2 <- unlist(lst2, recursive=FALSE)
# Check - each entry is its own element
lst2
```

enter image description here

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

```{r}
cbc <- lapply(lst2, xml_find_first, "//citedby-count")
cbc <- lapply(cbc, xml_text)
cbc # Repeats the first values of original nesting
```

enter image description here

Итак, я проверил, что будет с xml_find_all:

```{r}
cbc2 <- lapply(lst2, xml_find_all, "//citedby-count")
cbc2 <- lapply(cbc2, xml_text)
cbc2 # Elements contain all values from initial nesting
```

enter image description here

Что не имеет смысла по сравнению с выводом lst2 выше. По какой-то причине при извлечении текста сохраняются значения из начального вложения, даже если он не отображается при просмотре окончательного списка объектов XML. Я в тупике.

1 Ответ

0 голосов
/ 08 мая 2018

Действительно, как отмечает @ Dave2e, не просто используйте поиск XPath «где угодно» (в частности, поиск по потомкам или самостоятельно) с // для дочерних элементов, поскольку поиск будет выполняться по всему документу.

Как это может быть, если я не вызываю явно исходный документ? Если вы запустите str() в любом из ваших списков xml_find , вы увидите, что объект переносит Rcpp внешних указателей на текущий узел и документ , доступный для вспомнить по мере необходимости. На самом деле, я считаю, что указатель node отображается при вызове списка.

str(ls2)    
# List of 4
#  $ :List of 2
#   ..$ node:<externalptr> 
#   ..$ doc :<externalptr> 
#   ..- attr(*, "class")= chr "xml_node"
#  $ :List of 2
#   ..$ node:<externalptr> 
#   ..$ doc :<externalptr> 
#   ..- attr(*, "class")= chr "xml_node"
#  $ :List of 2
#   ..$ node:<externalptr> 
#   ..$ doc :<externalptr> 
#   ..- attr(*, "class")= chr "xml_node"
#  $ :List of 2
#   ..$ node:<externalptr> 
#   ..$ doc :<externalptr> 
#   ..- attr(*, "class")= chr "xml_node"

lst2[[1]]$doc
# <pointer: 0x000000000ca7ff90>

typeof(lst2[[1]]$doc)
# [1] "externalptr"

Поэтому при поиске соблюдайте осторожность в контексте. Вы можете использовать префикс точки (как советует @ Dave2e), .// или вообще не использовать косую черту для поиска дочерних элементов, которые здесь будут эквивалентны.

cbc2 <- lapply(lst2, xml_find_all, "citedby-count")
cbc2 <- lapply(cbc2, xml_text)
cbc2

# [[1]]
# [1] "9385"

# [[2]]
# [1] "4040"

# [[3]]
# [1] "3793"

# [[4]]
# character(0)


cbc2 <- lapply(lst2, xml_find_all, ".//citedby-count")
cbc2 <- lapply(cbc2, xml_text)
cbc2
# [[1]]
# [1] "9385"

# [[2]]
# [1] "4040"

# [[3]]
# [1] "3793"

# [[4]]
# character(0)

Обратите внимание, что .// будет искать ВСЕХ потомков (то есть детей, внуков и т. Д.), Начиная с текущего узла. См. В чем разница между .// и // * в XPath?

...