Прочитайте несколько файлов xml в R и объедините данные

У меня есть папка, содержащая более 1000 файлов с расширением (хотя они не являются настоящими файлами xml).

Я хочу автоматически извлекать определенное содержимое из этих файлов, чтобы конечным результатом была матрица или таблица (которые я могу использовать в дальнейшем в R для анализа или экспортировать в 1 файл csv и т. д.).

Я сделал/изменил код, который работает для одного файла, но не могу заставить его работать автоматически для остальных. По петле?

Итак, мой код для одного файла выглядит следующим образом:

library(xml2)

temp <- read_xml("test.xml")
# get all the <ns2:opendataField>s
recs <- xml_find_all(temp, "//ns2:opendataField")
# extract and clean all the columns
vals <- trimws(xml_text(recs))
#create columns
cols <- xml_attr(xml_find_all(temp, "//ns2:opendataField"), "key")
#create rows
rows <- xml_attr(xml_find_all(temp, "//ns2:opendataField"), "value")
datakvk <- data.frame(cols,rows)

Это приводит к:

 > head(datakvk)
                                              cols       rows
1                                  SbiBusinessCode      18129
2                             DocumentAdoptionDate 2017-08-22
3                                    FinancialYear       2016
4                                     BalanceSheet       <NA>
5 BalanceSheetBeforeAfterAppropriationResultsTitle       <NA>
6      BalanceSheetBeforeAfterAppropriationResults         Na
> 

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

                                              cols       file 1   file 2
1                                  SbiBusinessCode      18129     34234
2                             DocumentAdoptionDate 2017-08-22     452454
3                                    FinancialYear       2016     2016
4                                     BalanceSheet       <NA>     2016
5 BalanceSheetBeforeAfterAppropriationResultsTitle       <NA>     <NA>
6      BalanceSheetBeforeAfterAppropriationResults         Na
> 

Я пробовал следующий код, но он не работал:

list.files(pattern=".xml$") #

# create a list from these files
list.filenames<-list.files(pattern=".xml$")

# create an empty list that will serve as a container to receive the incoming files
list.data<-list()

# create a loop to read in your data
for (i in 1:length(list.filenames))
{
  list.data[[i]]<-read_xml(list.filenames[i])
  recs <- xml_find_all(list.data[[i]], "//ns2:opendataField")
  vals <- trimws(xml_text(recs))
  cols <- xml_attr(xml_find_all(list.data[[i]], "//ns2:opendataField"), "value")
  rows <- xml_attr(xml_find_all(list.data[[i]], "//ns2:opendataField"), "key")
}

# add the names of  data to the list
names(list.data)<-list.filenames

Что мне не хватает? где я ошибаюсь?

Заранее спасибо за помощь....

Чтобы быть полным: (Один единственный исходный файл (из 1000 выглядит так:)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<opendata xmlns:ns2="http://schemas.kvk.nl/xb/query/service/2016/1/0/0">
  <ns2:opendataField key="SbiBusinessCode" value="18129"/>
  <ns2:opendataField key="DocumentAdoptionDate" value="2017-08-22"/>
  <ns2:opendataField key="FinancialYear" value="2016"/>
  <ns2:opendataField key="BalanceSheet">
    <ns2:opendataField key="BalanceSheetBeforeAfterAppropriationResultsTitle">
      <ns2:opendataField key="BalanceSheetBeforeAfterAppropriationResults" value="Na"/>
    </ns2:opendataField>
    <ns2:opendataField key="BalanceSheetTitle">
      <ns2:opendataField key="Assets" value="61296">
        <ns2:opendataField key="AssetsNoncurrent" value="8978">
          <ns2:opendataField key="IntangibleAssets" value="8978"/>
        </ns2:opendataField>
        <ns2:opendataField key="AssetsCurrent" value="52318">
          <ns2:opendataField key="Inventories" value="2239"/>
          <ns2:opendataField key="Receivables" value="40560"/>
          <ns2:opendataField key="CashAndCashEquivalents" value="9519"/>
        </ns2:opendataField>
      </ns2:opendataField>
      <ns2:opendataField key="EquityAndLiabilities" value="61296">
        <ns2:opendataField key="Equity" value="201">
          <ns2:opendataField key="ShareCapital" value="1"/>
          <ns2:opendataField key="ReservesOther" value="200"/>
        </ns2:opendataField>
        <ns2:opendataField key="LiabilitiesCurrent" value="61095"/>
      </ns2:opendataField>
    </ns2:opendataField>
  </ns2:opendataField>
</opendata>

person RobertHaa    schedule 09.03.2018    source источник
comment
но это не "настоящие" xml-файлы ... из приведенного ниже примера, который представляет собой совершенно правильный XML! Пожалуйста, объясни.   -  person Parfait    schedule 09.03.2018
comment
@Parfait в моем предыдущем вопросе мне сказали, что это не XML: stackoverflow.com/questions/49189629/ (и я нахожусь на уровне новичка, поэтому я просто говорю то, что мне сказали другие)   -  person RobertHaa    schedule 09.03.2018
comment
Это проблема копирования/вставки. Вы просто перестали закрывать теги в конце. Комментатор не уточнял и не проверял. Часто XML-постеры делают это и даже опускают корневые теги с такими же пространствами имен, как у вас!   -  person Parfait    schedule 09.03.2018


Ответы (2)


Рассмотрите возможность преобразования вашего цикла for в цикл lapply, который вызывает data.frame() для списка кадров данных. И поскольку ваши XML-файлы потенциально могут иметь разные ключи/значения, простое cbind из списка фреймов данных не сработает, поэтому используйте слияние цепочки с Reduce(), сохраняя все строки (т. е. полное внешнее соединение).

...
# BUILD DATAFRAME LIST
df_list <- lapply(list.filenames, function(f) {
  doc <- read_xml(f)

  setNames(data.frame(
    xml_attr(xml_find_all(doc, "//ns2:opendataField"), "key"),
    xml_attr(xml_find_all(doc, "//ns2:opendataField"), "value")
  ), c("key", f))

})

# CHAIN MERGE INTO MASTER DATAFRAME
final_df <- Reduce(function(x,y) merge(x, y, by="key", all=TRUE), df_list)
person Parfait    schedule 09.03.2018

Вы можете использовать lapply, а затем cbind элементы списка с do.call:

library(xml2)
library(dplyr)

files <- list.files(pattern = ".xml$")
data  <- lapply(files, function(x) {
  temp <- read_xml(x) %>% xml_find_all("//ns2:opendataField")
  cols <- xml_attr(xml_find_all(temp, "//ns2:opendataField"), "key")
  rows <- xml_attr(xml_find_all(temp, "//ns2:opendataField"), "value")
  out  <- data.frame(rows, row.names = cols)
  names(out) <- x
  out
})
do.call(cbind, data)

Вывод для двух файлов с одинаковым содержимым:

                                                  file1.xml  file2.xml
SbiBusinessCode                                       18129      18129
DocumentAdoptionDate                             2017-08-22 2017-08-22
FinancialYear                                          2016       2016
BalanceSheet                                           <NA>       <NA>
BalanceSheetBeforeAfterAppropriationResultsTitle       <NA>       <NA>
BalanceSheetBeforeAfterAppropriationResults              Na         Na
BalanceSheetTitle                                      <NA>       <NA>
Assets                                                61296      61296
AssetsNoncurrent                                       8978       8978
IntangibleAssets                                       8978       8978
AssetsCurrent                                         52318      52318
Inventories                                            2239       2239
Receivables                                           40560      40560
CashAndCashEquivalents                                 9519       9519
EquityAndLiabilities                                  61296      61296
Equity                                                  201        201
ShareCapital                                              1          1
ReservesOther                                           200        200
LiabilitiesCurrent                                    61095      61095
person Martin Schmelzer    schedule 09.03.2018
comment
Спасибо, Мартин, но я получаю следующую ошибку: Error in data.frame(..., check.names = FALSE) : arguments imply differing number of rows: 19, 16, 23, 21, 12, 22, 24, 14, 15, 18, 17, 20, 31 Не могли бы вы посоветовать? - person RobertHaa; 09.03.2018
comment
Да, ваши файлы не имеют однородной структуры. Так что КБ - person Martin Schmelzer; 10.03.2018