Como analisar XML para quadro de dados R

103

Tentei analisar XML para frame de dados R, este link me ajudou muito:

como criar um quadro de dados R a partir de um arquivo xml

Mas ainda não fui capaz de descobrir meu problema:

Aqui está o meu código:

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"")]

O quadro de dados que quero ter é assim:

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

Estou preso no xmlToDataFrame(), Qualquer ajuda seria muito apreciada, obrigado.

Rosa
fonte

Respostas:

103

Os dados em formato XML raramente são organizados de uma maneira que permita que a xmlToDataFramefunção funcione. É melhor você extrair tudo nas listas e, em seguida, vincular as listas em um quadro de dados:

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

xml_data <- xmlToList(data)

No caso de seus dados de exemplo, obter a localização e a hora de início é bastante simples:

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"])

Os dados de temperatura são um pouco mais complicados. Primeiro você precisa chegar ao nó que contém as listas de temperatura. Em seguida, você precisa extrair as duas listas, olhar dentro de cada uma e escolher aquela que tem "hora" como um de seus valores. Em seguida, você precisa selecionar apenas essa lista, mas apenas manter os valores que têm o rótulo "valor":

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
fonte
94

Use o xpath mais diretamente para desempenho e clareza.

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"))

levando a

> 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
Martin Morgan
fonte
12
Essa realmente deveria ser a resposta aceita. É mais conciso e o xpath tem um desempenho muito melhor do que iterar em listas.
SchaunW
40

Aqui está uma solução parcial usando xml2. Dividir a solução em pedaços menores geralmente torna mais fácil garantir que tudo esteja alinhado:

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()
Hadley
fonte
8
Resposta útil. Se alguém mais se deparar com ele, aqui está o link para um tutorial de Hadley sobre o uso de xml2: blog.rstudio.com/2015/04/21/xml2
Richard Erickson
9

Você pode tentar o código abaixo:

# 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)
abhishek dandona
fonte