У меня большой, сложный XML -файл, и мне нужно извлечь значения и атрибуты определенных под (под ...) узлов. Но так как не все сноски имеют все нужные значения (некоторые отсутствуют), я не могу легко использовать очень быстрый xml_find_all
(пакетный XML2), потому что он, конечно, не будет включать сноски с пропущенными значениями.
Мое решение должен использовать for-l oop, циклически проходящий по всем моим xml -узлам (объектам), и проверять в каждом узле, существует ли мое желаемое значение - если да, извлеките его. Благодаря индексу l oop я знаю, к какому объекту он принадлежит, и записал его в соответствующий data.frame$Feature[i]
.
. Этот подход работает хорошо, но для моего большого XML -Node он занимает ОЧЕНЬ долго (20 минут) и занимает очень много памяти (~ 1,5 ГБ из-за if-l oop). Мои XML: 100 МБ, около 30 000 «записей / объектов», каждая из которых содержит около 50 функций (~ 2 млн. Строк)
Основная проблема, которую я выяснил: xpathSApply(...xml_path(Obj[i]...)
очень медленная, если индексация [ я] из моего l oop довольно высоко (> 5000)
Мои вопросы:
- У вас есть идея лучше или проще решить моя проблема с очень сложным и сильно неоднородным c, структурированным XML, где не все функции присутствуют во всех объектах (узлах)?
- Я прочитал , этот интересный подход , но не мог понять, как перевести его в мой очень сложный XML, где мои желаемые значения находятся на разных уровнях Nodeset ...
- Возможно, есть какое-то вложенное выражение xpathSApply, чтобы обойти for-l oop и избегать использования индекса?
- Есть ли у вас какие-либо "векторные" подходы (которые в R более быстры) для моей проблемы?
См. Мой MWE-код с еще несколько комментариев ниже.
XML
<?xml version="1.0" encoding="UTF-8"?>
<featureMember>
<Object>
<XML_Name>Object 1</XML_Name>
<XML_Feature1>
<XML_Feature1a href="URL1"></XML_Feature1a>
</XML_Feature1>
<XML_Feature2>
<XML_Feature2a>1</XML_Feature2a>
<XML_Feature2a>1x</XML_Feature2a>
<XML_Feature2a>1y</XML_Feature2a>
</XML_Feature2>
<XML_Feature3>
<XML_Feature3a>F3a_1</XML_Feature2a>
<XML_Feature3b>F3b_1</XML_Feature2a>
</XML_Feature3>
<XML_Feature3>
<XML_Feature3a>F3a_2</XML_Feature2a>
<XML_Feature3b>F3b_2</XML_Feature2a>
</XML_Feature3>
<XML_Feature4>F4_1</XML_Feature4>
<XML_Feature4>F4_2</XML_Feature4>
</Object>
<Object >
<XML_Name>Object 2</XML_Name>
<XML_Feature1>
<XML_Feature1a href="URL2"></XML_Feature1a>
</XML_Feature1>
</Object>
<Object >
<XML_Name>Object 3</XML_Name>
<XML_Feature1>
<XML_Feature1>
</XML_Feature1>
</XML_Feature1>
<XML_Feature2>
<XML_Feature2a>Value 3</XML_Feature2a>
</XML_Feature2>
</Object>
</featureMember>
R * 10 39 *
require(xml2)
require(XML)
test_xml2 <- read_xml("above_file.xml") # using Packet xml2 (for using xml_find_all)
test_XML <- xmlParse("above_file.xml") # Packet XML (for using xpathSApply)
# XML-Noteset of all Objects I want to process:
Obj <- xml_find_all(test_xml2, "//Object") # --> has 3 nodes, contains all Objects!
# initialize a destination dataframe and fill with NAs
df <- data.frame('Name'=integer(), 'f2a'=character() , 'f1a'=character(), stringsAsFactors = FALSE)
df[1:length(Obj),] <- NA
# My Initial approach to extract all features by xml_find_all (which is very fast) is not working because not all xml-nodes have all wanted xml-features:
Name <- xml_text(xml_find_all(test_xml2, "//XML_Name"))
# --> length(Name)=3, because all 3 Objects have a name!
f1a <- xml_attr(xml_find_all(test_xml2, "//XML_Feature1/XML_Feature1a"),"href")
# --> length(f1a)=2, because XML_Feature1a is missing in Object3!
f2a <- xml_text(xml_find_all(test_xml2, "//XML_Feature2/XML_Feature2a"))
# --> length(f2a)=2, because XML_Feature2a is missing in Object2!
# Joining these to a final df is not possible, because "Name", "f2a" and "f1a" have of course different lengths, plus correct data matching is not possible!
# Therefore I decided to make instead the following approach.
# 1.) crawl all features, which are present in all nodes, because its fast (here: "Name"):
df$Name <- xml_text(xml_find_all(test_xml2, "//XML_Name"))
# 2.) making a for-loop over all Objects/XML-Nodes of interest and check if eacht wanted feature exist.
# if yes: write to df$FeatureXY[i]
# if not: make nothing (thus df$FeatureXY[i]stays NA from initialization)
for (i in 1:length(Obj))
{ # 1. Feature:
tmp <- xpathSApply(test_XML, paste0(xml_path(Obj[i]),"/XML_Feature1/XML_Feature1a"), xmlGetAttr, "href")
if(length(tmp )>0) { df$f1a[i] <- tmp # otherwise it would produce an error-message}
# 2. Feature:
tmp <- xpathSApply(test_XML, paste0(xml_path(Obj[i]),"/XML_Feature2/XML_Feature2a"), xmlValue)
if(length(tmp )>0) { df$f2a[i] <- tmp}
}
# Result of df as it should be:
# Name f2a f1a f3a f3b f4
# Object 1 1 # 1x # 1y URL1 F3a_1 # F3a_2 F3b_1 # F3b_2 F4_1 # F4_2
# Object 2 NA URL2 NA NA NA
# Object 3 Value 3 NA NA NA NA
правка 1: расширенный пример XML (несколько элементов feature2a, feature3a / b feature4)