Как разобрать XML в кадр данных R

103

Я попытался разобрать XML на фрейм данных R, эта ссылка мне очень помогла:

как создать фрейм данных R из файла xml

Но все же я не смог разобраться в своей проблеме:

Вот мой код:

data <- xmlParse("http://forecast.weather.gov/MapClick.php?lat=29.803&lon=-82.411&FcstType=digitalDWML")
xmlToDataFrame(nodes=getNodeSet(data1,"//data"))[c("location","time-layout")]
step1 <- xmlToDataFrame(nodes=getNodeSet(data1,"//location/point"))[c("latitude","longitude")]
step2 <- xmlToDataFrame(nodes=getNodeSet(data1,"//time-layout/start-valid-time"))
step3 <- xmlToDataFrame(nodes=getNodeSet(data1,"//parameters/temperature"))[c("type="hourly"")]

Фрейм данных, который я хочу получить, выглядит следующим образом:

latitude  longitude   start-valid-time   hourly_temperature
29.803     -82.411  2013-06-19T15:00:00-04:00    91
29.803     -82.411  2013-06-19T16:00:00-04:00    90

Я застрял в xmlToDataFrame(), любая помощь будет очень признательна, спасибо.

Роза
источник

Ответы:

103

Данные в формате XML редко организованы таким образом, чтобы xmlToDataFrameфункция могла работать. Вам лучше извлечь все в списки, а затем связать списки во фрейме данных:

require(XML)
data <- xmlParse("http://forecast.weather.gov/MapClick.php?lat=29.803&lon=-82.411&FcstType=digitalDWML")

xml_data <- xmlToList(data)

В случае данных вашего примера получить местоположение и время начала довольно просто:

location <- as.list(xml_data[["data"]][["location"]][["point"]])

start_time <- unlist(xml_data[["data"]][["time-layout"]][
    names(xml_data[["data"]][["time-layout"]]) == "start-valid-time"])

Данные о температуре немного сложнее. Для начала нужно добраться до узла, содержащего списки температур. Затем вам нужно извлечь оба списка, просмотреть каждый и выбрать тот, у которого в качестве одного из значений указано «ежечасно». Затем вам нужно выбрать только этот список, но оставить только те значения, которые имеют метку «значение»:

temps <- xml_data[["data"]][["parameters"]]
temps <- temps[names(temps) == "temperature"]
temps <- temps[sapply(temps, function(x) any(unlist(x) == "hourly"))]
temps <- unlist(temps[[1]][sapply(temps, names) == "value"])

out <- data.frame(
  as.list(location),
  "start_valid_time" = start_time,
  "hourly_temperature" = temps)

head(out)
  latitude longitude          start_valid_time hourly_temperature
1    29.81    -82.42 2013-06-19T16:00:00-04:00                 91
2    29.81    -82.42 2013-06-19T17:00:00-04:00                 90
3    29.81    -82.42 2013-06-19T18:00:00-04:00                 89
4    29.81    -82.42 2013-06-19T19:00:00-04:00                 85
5    29.81    -82.42 2013-06-19T20:00:00-04:00                 83
6    29.81    -82.42 2013-06-19T21:00:00-04:00                 80
SchaunW
источник
95

Используйте xpath более прямо для повышения производительности и ясности.

time_path <- "//start-valid-time"
temp_path <- "//temperature[@type='hourly']/value"

df <- data.frame(
    latitude=data[["number(//point/@latitude)"]],
    longitude=data[["number(//point/@longitude)"]],
    start_valid_time=sapply(data[time_path], xmlValue),
    hourly_temperature=as.integer(sapply(data[temp_path], as, "integer"))

ведущий к

> head(df, 2)
  latitude longitude          start_valid_time hourly_temperature
1    29.81    -82.42 2014-02-14T18:00:00-05:00                 60
2    29.81    -82.42 2014-02-14T19:00:00-05:00                 55
Мартин Морган
источник
13
Это действительно должен быть принятый ответ. Он более краток, а xpath имеет гораздо лучшую производительность, чем итерация по спискам.
SchaunW
Отличный ответ. Обратите внимание, что руководство W3schools может быть лучшим местом для изучения xpath, чем официальная страница xpath.
Лоран Берже,
40

Вот частичное решение с использованием xml2. Разбив раствор на более мелкие части, как правило, легче выровнять все:

library(xml2)
data <- read_xml("http://forecast.weather.gov/MapClick.php?lat=29.803&lon=-82.411&FcstType=digitalDWML")

# Point locations
point <- data %>% xml_find_all("//point")
point %>% xml_attr("latitude") %>% as.numeric()
point %>% xml_attr("longitude") %>% as.numeric()

# Start time
data %>% 
  xml_find_all("//start-valid-time") %>% 
  xml_text()

# Temperature
data %>% 
  xml_find_all("//temperature[@type='hourly']/value") %>% 
  xml_text() %>% 
  as.integer()
Хэдли
источник
8
Полезный ответ. Если кто-то еще наткнется на это, вот ссылка на учебник Хэдли по использованию xml2: blog.rstudio.com/2015/04/21/xml2
Ричард Эриксон
9

Вы можете попробовать приведенный ниже код:

# Load the packages required to read XML files.
library("XML")
library("methods")

# Convert the input xml file to a data frame.
xmldataframe <- xmlToDataFrame("input.xml")
print(xmldataframe)
Абхишек Дандона
источник