Как векторизовать с данными XML? - PullRequest
4 голосов
/ 02 декабря 2009

скажем, у меня есть этот XML-файл:

<?xml version="1.0" encoding="UTF-8" ?>
<TimeSeries>
  <timeZone>1.0</timeZone>
  <series>
    <header/>
    <event date="2009-09-30" time="10:00:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="10:15:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="10:30:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="10:45:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="11:00:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="11:15:00" value="0.0" flag="2"></event>
  </series>
  <series>
    <header/>
    <event date="2009-09-30" time="08:00:00" value="1.0" flag="2"></event>
    <event date="2009-09-30" time="08:15:00" value="2.6" flag="2"></event>
    <event date="2009-09-30" time="09:00:00" value="6.3" flag="2"></event>
    <event date="2009-09-30" time="09:15:00" value="4.4" flag="2"></event>
    <event date="2009-09-30" time="09:30:00" value="3.9" flag="2"></event>
    <event date="2009-09-30" time="09:45:00" value="2.0" flag="2"></event>
    <event date="2009-09-30" time="10:00:00" value="1.7" flag="2"></event>
    <event date="2009-09-30" time="10:15:00" value="2.3" flag="2"></event>
    <event date="2009-09-30" time="10:30:00" value="2.0" flag="2"></event>
  </series>
  <series>
    <header/>
    <event date="2009-09-30" time="10:00:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="10:15:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="10:30:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="10:45:00" value="0.0" flag="2"></event>
    <event date="2009-09-30" time="11:00:00" value="0.0" flag="2"></event>
  </series>
</TimeSeries>

и, скажем, я хочу что-то сделать с элементами серии, и что я хотел бы применить на практике совет «векторизация векторизуемого» ... Я импортирую библиотеку XML и делаю следующее:

R> library("XML")
R> doc <- xmlTreeParse('/home/mario/Desktop/sample.xml')
R> TimeSeriesNode <- xmlRoot(doc)
R> seriesNodes <- xmlElementsByTagName(TimeSeriesNode, "series")
R> length(seriesNodes)
[1] 3
R> (function(x){length(xmlElementsByTagName(x[['series']], 'event'))}
+ )(seriesNodes)
[1] 6
R> 

и я не понимаю, почему я должен получить только результат применения функции к первому элементу: я ожидал три значения, так же как длина серийных узлов, что-то вроде этого:

R> mapply(length, seriesNodes)
series series series 
     7     10      6 

упс! Я уже пришел с ответом: «использовать mapply»:

R> mapply(function(x){length(xmlElementsByTagName(x, 'event'))}, seriesNodes)
series series series 
     6      9      5 

но тогда я вижу следующую проблему: R-ад говорит мне, что я "прячу петлю", а не "векторизую"! можно вообще избежать зацикливания? ...

Ответы [ 2 ]

3 голосов
/ 03 декабря 2009

Вы также можете использовать xpathApply или xpathSApply - эти функции извлекают наборы узлов, используя спецификацию XPath, а затем выполняют функцию для каждого набора. Обе эти функции предоставляются пакетом XML. Чтобы использовать эти функции, документ XML должен быть проанализирован с использованием xmlInternalTreeParse или с параметром useInternalNodes, равным xmlTreeParse, установленным в значение true:

require( XML )

countEvents <- function( series ){

  events <- xmlElementsByTagName( series, 'event' )
  return( length( events ) ) 

}

doc <- xmlTreeParse( "sample.xml", useInternalNodes = T )

xpathSApply( doc, '/TimeSeries/series', countEvents )
[1] 6 9 5

Я не знаю, работает ли он "быстрее", но код определенно чище и очень понятен всем, кто знает синтаксис XPath и как работает функция apply.

3 голосов
/ 02 декабря 2009

Поскольку seriesNodes является списком узлов, не существует простого способа избежать неявного зацикливания. Простые операции, такие как получение длины, не требуют больших вычислительных ресурсов, поэтому я не потерял бы сон из-за невозможности векторизации.

Обратите внимание, что вы можете использовать sapply(seriesNodes, length) вместо mapply, поскольку у функции length есть только один аргумент.

«Правильный R-способ» - использовать (s|m)apply вызовы для извлечения векторов полезных битов данных, а затем анализировать их обычным способом.

Наконец, если вы действительно отчаянно хотите векторизовать подсчет событий, используйте names(unlist(seriesNodes)), а затем посчитайте вхождения "series.children.event.name" между каждым вхождением "series.name". Это, несомненно, уродливее и, возможно, медленнее, чем вызов sapply.

...