XML-Datei mit Python auslesen und in ein Pandas DataFrame einfügen

Code-Stücke können hier veröffentlicht werden.
Antworten
codyycode
User
Beiträge: 5
Registriert: Freitag 24. November 2017, 10:24

Hallo liebe Mitglieder,

ich bin neu in Python und habe folgendes Problem. Ich muss aus einer ziemlich umständlichen XML-Datei (eine von vielen ihrer Art) die Werte auslesen.

Die Datei sieht folgendermaßen aus:

Code: Alles auswählen

<change user="123" timestamp="2017-09-04T13:58:46.190Z">
    <log id="333" action="create">
        <property id="52122">
            <old/>
            <new>
                <item id="562622" toString="Test"/>
                <item id="033362" toString="Test2"/>
            </new>
        </property>
        <property id="1000503">
              <new>false</new>
         </property>
        <property id="33563">
            <new>
                <item id="44322" toString="Test3"/>
            </new>
        </property>
        <property id="21733">
            <old/>
            <new id="12341212" toString="Test4"/>
        </property>
    </log>
</change>
Folgender Ansatz wurde bis jetzt befolgt:

Code: Alles auswählen

for test in root.iter('change'):
change_user_id.append(test.attrib['user'])
timestamp.append(test.attrib['timestamp'])
for log in test:
    log_id.append(log.attrib['id'])
    action.append(log.attrib['action'])
    #Hier sollte der restliche Teil in Arrays gesteckt werden...

    #Nach dem kompletten Durchlauf...

d = {'changer_user':change_user_id,'timestamp':timestamp,'log_id':log_id,'action':action#und viele mehr...}
#Anschließend das ganze in ein Pandas Dataframe
a = pd.DataFrame.from_dict(d, orient='index')
Mein eigentliches Problem besteht darin, dass im <new>-tag verschiedene Formate von Werten drin stehen könne. Des Weiteren fehlen mir noch die richtigen Kenntnisse wie ich diese Daten richtig einordnen kann. Ich habe vorher mit anderen XML-Dateien rumprobiert und es keine große Sache. Da hier aber über verschiedene Attribute und Formate Schleifen gesetzt werden sollen und ich erst seit einem Monat mit Python arbeite ist es für mich noch relativ schwierig. Ich hoffe jemand hat einen Rat und vielen Dank schon mal im Voraus:)
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@codyycode: Deine Einrückungen sind kaputt. So kann man nur raten, wie der Code aussehen soll.
log_id und action werden nicht definiert, gibt es jeweils pro <change> nur ein <log>, dann ist die for-Schleife irreführend, oder mehrere, dann Funktioniert die ganze Datenstruktur nicht.
Definitiv gibt es mehrere <property> pro <log> und innerhalb dessen <new> mit mir noch nicht ganz klarem Aufbau; mal Text, mal <item>s mal zusätzliche Attribute :K .

Wie soll die resultierende Datenstruktur aussehen? Was willst Du damit machen? Warum Pandas?
codyycode
User
Beiträge: 5
Registriert: Freitag 24. November 2017, 10:24

Hallo Sirius,

nun so grausig es auch ist so ist auch die Struktur bis auf die eine einrückung, die ich manuell falsch beim kopieren gemacht habe. Ja es gibt mehrere <propertys> für ein <log> und auch da hast du das Problem gut erkannt. Also es ist kein Fehler, dass bei <new> mal 'false' drin steht und mal id-values usw...

Ziel dabei ist diese Informationen aus diesem File(wovon es Tausende gibt) an ein großes Dataframe anzuheften(mergen) damit daraus ein über Scikit-Learn eine lernfähige Datenbasis besitzt. Dieses Modell ist schon fertig nur happerts wie so oft an der Datenvorbereitung. Als Index hab ich einen Multiindex in Betracht gezogen mit ' change user' und 'timestamp'. Wenn ich wüsste wie hier eine Tabelle erstellt wird könnte ich dir eine gewünschte Datenstruktur entwerfen. Aber danke schon mal für das Interesse!

Edit: Ungefähr so sollten zumindest die Spaltennamen geordnet sein(das es viele NaNs innerhalb dieser entstehen ist mir zur Zeit schnuppe^^)

Code: Alles auswählen

Change_User|Timestamp|Log_id|Action|property_ID|New_Property_ID|Item_ID|To_String
Edit_2:Log_id und action und das ganze andere Zeug sind nur leere Arrays

Code: Alles auswählen

change_user_id = []
timestamp = []
action = []
log_id = []
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@codyycode: Du willst also pro Item-Eintrag eine komplette Zeile mit Change_User, Timestamp, ... Dazu erzeugst Du EINE Liste die Tuple mit den Spalteninhalten enthält. In den äußersten for-Schleifen setzt Du einfach passende Variablen und in der innersten erzeugst Du das Tuple mit den Variablen und hast Du das .append .
codyycode
User
Beiträge: 5
Registriert: Freitag 24. November 2017, 10:24

Genau das will ich. Also für jeden Eintrag der im <new>-tag oder <old>-tag steht eine eigene Zeile. Könntest du mir eventuell einen Ansatz geben?
Insbesondere bei dem Teil wo sich verschiedene Formate von Einträgen befinden. Ein kleines Beispiel würde schon reichen :)
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Edit: hast du deine Antwort bearbeitet? Da war eine Frage mit Tupeln, die ist jetzt weg. Darauf bezog sich

"Das ist nicht so schwer.

Code: Alles auswählen

a = wert
b = wert
c = wert
tupel = (a, b, c)
"

Dein eigentliches Problem scheint mir eher die Logik fuer die Bestimmung der verschiedenen Werte. Du bist" aber der einzige, der alle Faelle kennt, und die Abbildungsvorschrift von verschiedenen XML-Fragmenten auf eben diese Werte fuer das Tupel.

Du musst dir halt Unterscheidungskriterien fuer die Faelle ueberlegen. Ich persoenlich wuerde hier einen Testgetriebenen Ansatz waehlen: ein Fragment als Text hinschreiben, durch deine Funktion zur Erstellung eines Tupels jagen, und pruefen, das die Werte stimmen. Auf die Art und Weise kannst du dich durch die verschiedenen Faelle hangeln, und vor allem auch sicherstellen, dass durch einfuehren eines neuen Falls keine alten beeintraechtigt wurden.
codyycode
User
Beiträge: 5
Registriert: Freitag 24. November 2017, 10:24

Hallo deeds,

wie das 'durcharbeiten' beim bauen eines Parsers funktioniert weiß ich ja. Mir gehts darum, dass ich die Problemfälle wie

Code: Alles auswählen

          <property id="33563">
            <new>
                <item id="44322" toString="Test3"/>
            </new>
        </property>
und

Code: Alles auswählen

<property id="1000503">
<new>false</new>
</property>
noch nicht fachlich abdecken kann, da ich erst seit einem Monat mit Python arbeite. Aber danke für das Beispiel. Wie kriege ich denn in deinem Beispiel ein Referenz von diesen 'Werten' innerhalb einer Schleife hin?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@codyycode: ich verstehe nicht, wo da das Problem ist? Du mußt Tests schreiben, um herauszufinden, welcher Fall es ist und dann entsprechend reagieren, also die passenden Werte für das Tuple ermitteln.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mit Python hat das ja erstmal nix zu tun. Sondern mit programmieren, beziehungsweise dem formulieren eines Algorithmus. Die Details der Programmiersprache kommen da ja nur bedingt zum tragen, und gerade Python ist da sehr nah dran an dem, was gemeinhin als "Pseudocode" verstanden wird - also einem klaren Ausdruck von dem, was man will, ohne viel syntaktischen Zucker oder semantische Besonderheiten.

Du musst zuerstmal alle deine Faell definieren. Das kann ja auch als reiner text sein.

wenn das property-tag ein kind-tag "new" hat, und wenn dieses new-tag einen text beinhaltet der "false" lautet -> Fall 1
wenn das property-tag ein kind-tag "new" hat, und wenn dieses new-tag genau einen tag "item" beinhaltet -> Fall 2

etc.

Daraus leitet sich dann recht natuerlich eine Struktur des Codes ab, bei dem man zb Gemeinsamkeiten von Faellen nach vorne ziehen kann, um danach nur noch die Unterfaelle zu uenterscheiden, etc.
codyycode
User
Beiträge: 5
Registriert: Freitag 24. November 2017, 10:24

Ach so meinst du das :D Sorry hab ich es falsch verstanden. Alles klar danke ich werde mal ein paar IF-Statements durchprobieren.
Antworten