2016-04-13 4 views
0

Ich möchte bestimmte Daten aus einer XML-Datei in einen R-Datenframe extrahieren. Ich möchte die Daten später dazu verwenden, digitalisierte Stiftbewegungen mit einem Anoto-Stift zu rekonstruieren. Bisher mache ich das mit der Bibliothek rvest. R & XML - Zuweisen von Daten zu korrekten übergeordneten Knoten, die in einem Datenrahmen gleich benannt sind

library(rvest) 

file <- read_xml("1.xml") 

#The interesting data is in the stroke nodes. 
stroke <- xml_nodes(file, "stroke") 

#One example for extracting data I am interested in. 
bounds <- xml_nodes(stroke, "bounds") 
x <- xml_text(xml_nodes(bounds, "x")) 
y <- xml_text(xml_nodes(bounds, "y")) 
width <- xml_text(xml_nodes(bounds, "width")) 
height <- xml_text(xml_nodes(bounds, "height")) 

#Putting this data into a Dataframe. 
df <- data.frame(x, y, width, height) 

So weit so gut (Beispiel XML-Datei kann unten). Mein Problem sind jetzt die <sample> Knoten. Ich habe eine minimale Anzahl von <stroke> Knoten in den XML-Dateien, bis zu ca. gehen. 100 bei max. Jeder <stroke> Knoten hat seine eigenen <sample> Knoten. Ich möchte die X-, Y- und Zeitdaten aus den Beispielknoten extrahieren, so dass ich sie dem entsprechenden Strich im Datenrahmen zuordnen kann. Zum Beispiel, wenn ich

mysamples <- xml_nodes(stroke, "sample") 

nur erhalte ich alle Proben aus allen Schlägen, aber ich habe zwischen unterschiedlichen Hüben zu unterscheiden. Ich dachte daran, eine Funktion zu schreiben, die eine For-Schleife verwendet, um über die verschiedenen Striche zu iterieren, aber ich konnte das nicht in Gang bringen.

Hier ist eine verkürzte XML-Datei Beispiel mit zwei <stroke> Knoten.

<?xml version="1.0" encoding="UTF-8" ?> 
 
<page> 
 
    <UnassignedStrokes> 
 
    <starttime>1459867893629</starttime> 
 
    <endtime>1459867896812</endtime> 
 
    <stroke> 
 
     <starttime>1459867893629</starttime> 
 
     <endtime>1459867894815</endtime> 
 
     <linewidth>1.0</linewidth> 
 
     <color>-14090101</color> 
 
     <bounds> 
 
     <x>260.0</x> 
 
     <y>750.0</y> 
 
     <width>217.0</width> 
 
     <height>18.0</height> 
 
     </bounds> 
 
     <sample> 
 
     <x>260.625</x> 
 
     <y>766.0</y> 
 
     <time>1459867893629</time> 
 
     <force>108</force> 
 
     </sample> 
 
     <sample> 
 
     <x>260.625</x> 
 
     <y>763.625</y> 
 
     <time>1459867893722</time> 
 
     <force>120</force> 
 
     </sample> 
 
     <sample> 
 
     <x>262.875</x> 
 
     <y>762.0</y> 
 
     <time>1459867893775</time> 
 
     <force>122</force> 
 
     </sample> 
 
    </stroke> 
 
    <stroke> 
 
     <starttime>1459867895892</starttime> 
 
     <endtime>1459867896812</endtime> 
 
     <linewidth>1.0</linewidth> 
 
     <color>-14090101</color> 
 
     <bounds> 
 
     <x>364.0</x> 
 
     <y>701.0</y> 
 
     <width>10.0</width> 
 
     <height>125.0</height> 
 
     </bounds> 
 
     <sample> 
 
     <x>364.5</x> 
 
     <y>701.0</y> 
 
     <time>1459867895892</time> 
 
     <force>32</force> 
 
     </sample> 
 
     <sample> 
 
     <x>366.0</x> 
 
     <y>702.0</y> 
 
     <time>1459867895905</time> 
 
     <force>106</force> 
 
     </sample> 
 
     <sample> 
 
     <x>367.25</x> 
 
     <y>702.625</y> 
 
     <time>1459867895958</time> 
 
     <force>120</force> 
 
     </sample> 
 
    </stroke> 
 
    </UnassignedStrokes> 
 
</page>

Ich schätze jede Hilfe!

+0

Momentan erfasst der Datenrahmen von bounds keinen Strich. Benötigen Sie zwei Datenrahmen: Grenzen und Stichproben mit Strichdaten, die als Spalten gekennzeichnet sind? In XML sind beide Geschwister zueinander. Bitte zeigen Sie das gewünschte Endergebnis an. – Parfait

Antwort

1

Diese Lösung generiert einen einzelnen Datenrahmen, der den Datenrahmen "Grenzen" mit den Beispielinformationen aller untergeordneten Knoten verbindet. Es ist ein wenig rau an den Rändern durch es funktioniert:

#Putting this data into a Dataframe. 
df<-data.frame(x, y, width, height, stringsAsFactors=FALSE) 

#list of of subnodes 
samples<-sapply(stroke, FUN=xml_nodes, xpath="sample") 
#find list of lists for x, y, time and force from each subnode of interest 
sx<-sapply(samples, FUN=function(x) {xml_text(xml_nodes(x, xpath="x"))}) 
sy<-sapply(samples, FUN=function(x) {xml_text(xml_nodes(x, xpath="y"))}) 
stime<-sapply(samples, FUN=function(x) {xml_text(xml_nodes(x, xpath="time"))}) 
sforce<-sapply(samples, FUN=function(x) {xml_text(xml_nodes(x, xpath="force"))}) 

#create dataframe from the parent df and the list of lists of subnodes 
results<-lapply(seq(1:length(sx)), function(i){data.frame(df[i,],sx=unlist(sx[i]), 
     sy=unlist(sy[i]), force=unlist(sforce[i]), time=unlist(stime[i]), 
                   stringsAsFactors=FALSE)}) 
#create a single df 
finaldf<-do.call(rbind, results) 
#convert all columns to numeric values 
finaldf[,1:ncol(finaldf)]<-lapply(finaldf[,1:ncol(finaldf)], as.numeric) 

Dies wird einige Warnungen erzeugen, aber die ignoriert werden kann. Damit do.call (rbind) funktionieren kann, ist es wichtig, dass die Werte im gesamten Prozess entweder numerisch oder Zeichen und keine Faktoren sind, also die stringsAsFactors = FALSE-Parameter in data.frame-Definitionen. Das war eine gute Lernerfahrung.

+0

Das hat super für mich funktioniert! Vielen Dank! Obwohl die as.numeric-Funktion meine Zeitwerte auf etw. 1.523526e + 12 Werte umrechnen lässt, was nicht so schön ist ... habt eine Idee, wie man das verhindern kann? – Flugmango

+0

Mit so großen Zahlen sind sie höchstwahrscheinlich die Zahl von Millisekunden seit Beginn der Zeit 1. Januar 1970, nach UNIX. Ich würde durch 1000 dividieren und einstecken in: as.POSIXct (x, origin = "1970-01-01"). Möglicherweise müssen Sie die Zeitzone anpassen. – Dave2e

1

Nicht sicher, ob es ein einfacher Weg ist, aber das ist die beste Lösung, die ich habe kommen mit:

require(rvest) 
require(data.table) 

strokes <- read_xml("test.xml") %>% xml_nodes("stroke") 

# iterate over stroke nodes 
tmp <- lapply(strokes, function(x){ 

    # get all sample nodes 
    samples <- x %>% xml_nodes("sample") 

    # iterate over samples in stroke and extract information 
    tmp.s <- lapply(samples, function(s){ 
    children <- xml_children(s) 
    data.frame(name = xml_name(children), text = xml_text(children)) 
    }) 

    # bind samples together and give them the appropriate ID 
    tmp.s <- rbindlist(tmp.s, idcol = "sample") 
    tmp.s 
}) 

# bind strokes together and give them the appropriate ID 
tmp <- rbindlist(tmp, idcol = "stroke") 

tmp 
    stroke sample name   text 
1:  1  1  x  260.625 
2:  1  1  y   766.0 
3:  1  1 time 1459867893629 
4:  1  1 force   108 
5:  1  2  x  260.625 
6:  1  2  y  763.625 
7:  1  2 time 1459867893722 
8:  1  2 force   120 
9:  1  3  x  262.875 
10:  1  3  y   762.0 
11:  1  3 time 1459867893775 
12:  1  3 force   122 
13:  2  1  x   364.5 
14:  2  1  y   701.0 
15:  2  1 time 1459867895892 
16:  2  1 force   32 
17:  2  2  x   366.0 
18:  2  2  y   702.0 
19:  2  2 time 1459867895905 
20:  2  2 force   106 
21:  2  3  x  367.25 
22:  2  3  y  702.625 
23:  2  3 time 1459867895958 
24:  2  3 force   120 
    stroke sample name   text 

hoffte, das hilft!

Verwandte Themen