Разобрать XML-файл с помощью R Получить во фрейме данных

XML-данные

<HealthData locale="en_US">
 <ExportDate value="2016-06-02 14:05:23 -0400"/>
 <Me HKCharacteristicTypeIdentifierDateOfBirth="" HKCharacteristicTypeIdentifierBiologicalSex="HKBiologicalSexNotSet" HKCharacteristicTypeIdentifierBloodType="HKBloodTypeNotSet" HKCharacteristicTypeIdentifierFitzpatrickSkinType="HKFitzpatrickSkinTypeNotSet"/>
 <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Ryan Praskievicz iPhone" unit="count" creationDate="2014-10-02 08:30:17 -0400" startDate="2014-09-24 15:07:06 -0400" endDate="2014-09-24 15:07:11 -0400" value="7"/>
 <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Ryan Praskievicz iPhone" unit="count" creationDate="2014-10-02 08:30:17 -0400" startDate="2014-09-24 15:12:13 -0400" endDate="2014-09-24 15:12:18 -0400" value="15"/>
 <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Ryan Praskievicz iPhone" unit="count" creationDate="2014-10-02 08:30:17 -0400" startDate="2014-09-24 15:17:16 -0400" endDate="2014-09-24 15:17:21 -0400" value="20"/>
</HealthData>

R-код

> library(XML)
> doc="\\pathtoXMLfile"
> list <-xpathApply(doc, "//HealthData/Record", xmlAttrs)
> df <- do.call(rbind.data.frame, list)
> str(df)

Я пытаюсь взять образец XML-данных, показанный выше, и загрузить его во фрейм данных в R с именем каждой записи, т.е. типом, источником, единицей, endDate, значением в качестве заголовка столбца и каждым значением записи, т.е. количество, 2014-09 -24 15:07:11 -0400, 7 в качестве значений для каждой строки во фрейме данных.

Когда df <- do.call(rbind.data.frame, list) это близко, но также похоже, что оно также связывает все значения для заголовков столбцов. Если вы View(df) или str(df), вы поймете, что я имею в виду. Как использовать имена переменных записи в качестве имен заголовков столбцов?

Спасибо, Райан


person Ryan Praskievicz    schedule 28.07.2016    source источник


Ответы (2)


Рассмотрим xpathSApply() для извлечения атрибутов, а затем перенесите полученный список с помощью t() в фрейм данных:

library(XML)

xmlstr <- '<?xml version="1.0" encoding="UTF-8"?>
            <HealthData locale="en_US">
              <ExportDate value="2016-06-02 14:05:23 -0400"/>
              <Me HKCharacteristicTypeIdentifierDateOfBirth="" HKCharacteristicTypeIdentifierBiologicalSex="HKBiologicalSexNotSet" HKCharacteristicTypeIdentifierBloodType="HKBloodTypeNotSet" HKCharacteristicTypeIdentifierFitzpatrickSkinType="HKFitzpatrickSkinTypeNotSet"/>
              <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Ryan Praskievicz iPhone" unit="count" creationDate="2014-10-02 08:30:17 -0400" startDate="2014-09-24 15:07:06 -0400" endDate="2014-09-24 15:07:11 -0400" value="7"/>
              <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Ryan Praskievicz iPhone" unit="count" creationDate="2014-10-02 08:30:17 -0400" startDate="2014-09-24 15:12:13 -0400" endDate="2014-09-24 15:12:18 -0400" value="15"/>
              <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Ryan Praskievicz iPhone" unit="count" creationDate="2014-10-02 08:30:17 -0400" startDate="2014-09-24 15:17:16 -0400" endDate="2014-09-24 15:17:21 -0400" value="20"/>
            </HealthData>'

xml <- xmlParse(xmlstr)

recordAttribs <- xpathSApply(doc=xml, path="//HealthData/Record",  xmlAttrs)
df <- data.frame(t(recordAttribs))
df

#                                type              sourceName  unit
# 1 HKQuantityTypeIdentifierStepCount Ryan Praskievicz iPhone count
# 2 HKQuantityTypeIdentifierStepCount Ryan Praskievicz iPhone count
# 3 HKQuantityTypeIdentifierStepCount Ryan Praskievicz iPhone count
#                creationDate                 startDate                   endDate
# 1 2014-10-02 08:30:17 -0400 2014-09-24 15:07:06 -0400 2014-09-24 15:07:11 -0400
# 2 2014-10-02 08:30:17 -0400 2014-09-24 15:12:13 -0400 2014-09-24 15:12:18 -0400
# 3 2014-10-02 08:30:17 -0400 2014-09-24 15:17:16 -0400 2014-09-24 15:17:21 -0400
#   value
# 1     7
# 2    15
# 3    20

В случае атрибутов, которые появляются в одних и не появляются в других, рассмотрите возможность сопоставления с заранее определенным списком имен и итеративно заполните NAs. Ниже приведены две версии, использующие sapply() с циклом for и вторым аргументом списка:

recordnames <- c("type", "unit", "sourceName", "device", "sourceVersion", 
                 "creationDate", "startDate", "endDate", "value")

# FOR LOOP VERSION
recordAttribs <- sapply(recordAttribs, function(i) {
  for (r in recordnames){
    i[r] <- ifelse(is.null(i[r]), NA, i[r])
  }
  i <- i[recordnames]  # REORDER INNER VECTORS
  return(i)
})

# TWO LIST ARGUMENT SAPPLY
recordAttribs <- sapply(recordAttribs, function(i,r) {  
    if (is.null(i[r])) i[r] <- NA
        else i[r] <- i[r]         
    i <- i[recordnames]  # REORDER INNER VECTORS
    return(i)
}, recordnames)


df <- data.frame(t(recordAttribs))
person Parfait    schedule 28.07.2016
comment
спасибо, это сработало отлично для тестовых данных, которые я предоставил. Когда я вернулся и попытался применить его к полному набору данных, я понял, что есть некоторые записи с 9 столбцами, а не с 7, т.е. <Record type="HKQuantityTypeIdentifierFlightsClimbed" sourceName="Ryan Praskievicz iPhone" sourceVersion="9.3.2" device="&lt;&lt;HKDevice: 0x15a4af3f0&gt;, name:iPhone, manufacturer:Apple, model:iPhone, hardware:iPhone8,1, software:9.3.2&gt;" unit="count" creationDate="2016-06-02 12:27:46 -0400" startDate="2016-06-02 12:09:29 -0400" endDate="2016-06-02 12:09:29 -0400" value="1"/> это не сработало. Любые идеи? - person Ryan Praskievicz; 29.07.2016
comment
Знаете ли вы, хотите сохранить общие атрибуты или все? Вы заранее знаете, какие атрибуты оставить? - person Parfait; 29.07.2016
comment
да, я хотел бы сохранить все 9 строк из вектора и просто иметь NA для векторов с 7 строками. - person Ryan Praskievicz; 29.07.2016
comment
См. обновление, корректируя любые атрибуты, которые могут присутствовать или отсутствовать в 9. - person Parfait; 30.07.2016

Другой вариант — xmlAttrsToDataFrame, который должен обрабатывать отсутствующие атрибуты. Вы также можете получить теги с определенным атрибутом, таким как устройство.

XML:::xmlAttrsToDataFrame(xml["//Record"])
XML:::xmlAttrsToDataFrame(xml["//Record[@device]"])
person Chris S.    schedule 01.08.2016