Как векторизовать с данными XML?
Скажем, у меня есть этот 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>
и я не понимаю, почему я должен получить только результат применения функции к первому элементу: я ожидал три значения, так же как длина seriesNodes, что-то вроде этого:
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 ответа
Вы также можете использовать xpathApply
или же xpathSApply
- эти функции извлекают наборы узлов, используя спецификацию XPath, а затем выполняют функцию каждый набор. Обе эти функции предоставляются XML
пакет. Чтобы использовать эти функции, документ XML должен быть проанализирован с использованием xmlInternalTreeParse
или с useInternalNodes
вариант xmlTreeParse
установить, чтобы быть правдой:
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
функция работает.
Поскольку seriesNodes
это список узлов, нет простого способа избежать неявного зацикливания. Простые операции, такие как получение длины, не требуют больших вычислительных ресурсов, поэтому я не потерял бы сон из-за невозможности векторизации.
Обратите внимание, что вы можете использовать sapply(seriesNodes, length)
, вместо mapply
поскольку существует только один аргумент length
функция.
"Правильный R-способ" - это использовать (s|m)apply
вызывает для извлечения векторов полезных бит данных, а затем анализировать их в обычном порядке.
Наконец, если вы действительно хотите векторизовать подсчет событий, используйте names(unlist(seriesNodes))
а затем посчитать происшествия "series.children.event.name"
между каждым появлением "series.name"
, Это, несомненно, уродливее и, возможно, медленнее, чем sapply
вызов.