So analysieren Sie XML in R-Datenrahmen

102

Ich habe versucht, XML in R-Datenrahmen zu analysieren. Dieser Link hat mir sehr geholfen:

So erstellen Sie einen R-Datenrahmen aus einer XML-Datei

Trotzdem konnte ich mein Problem nicht herausfinden:

Hier ist mein Code:

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

Der Datenrahmen, den ich haben möchte, ist folgender:

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

Ich stecke fest bei xmlToDataFrame(), jede Hilfe wäre sehr dankbar, danke.

Rosa
quelle

Antworten:

103

Daten im XML-Format sind selten so organisiert, dass die xmlToDataFrameFunktion funktioniert. Sie sollten besser alles in Listen extrahieren und dann die Listen in einem Datenrahmen zusammenbinden:

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

xml_data <- xmlToList(data)

Bei Ihren Beispieldaten ist es ziemlich einfach, Standort und Startzeit zu ermitteln:

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

Temperaturdaten sind etwas komplizierter. Zuerst müssen Sie zu dem Knoten gelangen, der die Temperaturlisten enthält. Dann müssen Sie beide Listen extrahieren, in jede schauen und diejenige auswählen, deren Wert "stündlich" ist. Dann müssen Sie nur diese Liste auswählen, aber nur die Werte mit der Bezeichnung "Wert" beibehalten:

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
quelle
92

Verwenden Sie xpath direkter für Leistung und Klarheit.

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

führt zu

> 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
quelle
12
Dies sollte wirklich die akzeptierte Antwort sein. Es ist prägnanter und xpath bietet eine viel bessere Leistung als das Durchlaufen von Listen.
SchaunW
39

Hier ist eine Teillösung mit xml2. Das Aufteilen der Lösung in kleinere Stücke erleichtert im Allgemeinen die Sicherstellung, dass alles in einer Reihe steht:

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
quelle
8
Hilfreiche Antwort. Wenn jemand anderes darauf stößt, ist hier der Link zu einem Tutorial von Hadley über die Verwendung von xml2: blog.rstudio.com/2015/04/21/xml2
Richard Erickson
9

Sie können den folgenden Code ausprobieren:

# 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
quelle