Home > Enterprise >  Flatten a nested xml with pandas
Flatten a nested xml with pandas

Time:06-11

I have the follow xml in this format:

<?xml version='1.0' encoding='UTF-8'?>
<ettevotjad>
  <ettevotja>
    <nimi>000 Holdings OÜ</nimi>
    <ariregistri_kood>16372442</ariregistri_kood>
    <ettevotja_oiguslik_vorm>Osaühing</ettevotja_oiguslik_vorm>
    <ettevotja_oigusliku_vormi_alaliik/>
    <kmkr_nr/>
    <ettevotja_staatus>R</ettevotja_staatus>
    <ettevotja_staatus_tekstina>Registrisse kantud</ettevotja_staatus_tekstina>
    <ettevotja_esmakande_kpv>23.11.2021</ettevotja_esmakande_kpv>
    <ettevotja_aadress>
      <asukoht_ettevotja_aadressis/>
      <asukoha_ehak_kood/>
      <asukoha_ehak_tekstina></asukoha_ehak_tekstina>
      <indeks_ettevotja_aadressis/>
      <ads_adr_id></ads_adr_id>
      <ads_ads_oid></ads_ads_oid>
      <ads_normaliseeritud_taisaadress/>
    </ettevotja_aadress>
    <teabesysteemi_link>https://ariregister.rik.ee/est/company/16372442</teabesysteemi_link>
  </ettevotja>
  <ettevotja>
    <nimi>001 group OÜ</nimi>
    <ariregistri_kood>12754230</ariregistri_kood>
    <ettevotja_oiguslik_vorm>Osaühing</ettevotja_oiguslik_vorm>
    <ettevotja_oigusliku_vormi_alaliik/>
    <kmkr_nr/>
    <ettevotja_staatus>R</ettevotja_staatus>
    <ettevotja_staatus_tekstina>Registrisse kantud</ettevotja_staatus_tekstina>
    <ettevotja_esmakande_kpv>17.11.2014</ettevotja_esmakande_kpv>
    <ettevotja_aadress>
      <asukoht_ettevotja_aadressis>Õismäe tee 78-9</asukoht_ettevotja_aadressis>
      <asukoha_ehak_kood>0176</asukoha_ehak_kood>
      <asukoha_ehak_tekstina>Haabersti linnaosa, Tallinn, Harju maakond</asukoha_ehak_tekstina>
      <indeks_ettevotja_aadressis>13513</indeks_ettevotja_aadressis>
      <ads_adr_id>2182337</ads_adr_id>
      <ads_ads_oid></ads_ads_oid>
      <ads_normaliseeritud_taisaadress>Harju maakond, Tallinn, Haabersti linnaosa, Õismäe tee 78-9</ads_normaliseeritud_taisaadress>
    </ettevotja_aadress>
    <teabesysteemi_link>https://ariregister.rik.ee/est/company/12754230</teabesysteemi_link>
  </ettevotja>
</ettevotjad>

Using pandas' .read_xml() yields:

 import pandas as pd

 data = pd.read_xml('test_file.xml')

 print(data.head(2).to_string())
              nimi  ariregistri_kood ettevotja_oiguslik_vorm  ettevotja_oigusliku_vormi_alaliik kmkr_nr ettevotja_staatus ettevotja_staatus_tekstina ettevotja_esmakande_kpv  ettevotja_aadress                               teabesysteemi_link
0  000 Holdings OÜ          16372442                Osaühing                                NaN    None                 R         Registrisse kantud              23.11.2021                NaN  https://ariregister.rik.ee/est/company/16372442
1     001 group OÜ          12754230                Osaühing                                NaN    None                 R         Registrisse kantud              17.11.2014                NaN  https://ariregister.rik.ee/est/company/12754230

Notice in the dataframe 'ettevotja_aadress' is NaN, but in fact if you look at the xml structure, it's nested with those sub columns/headers. How do I expand out those nested columns into the dataframe?

I thought one way to do it was to simply read in the file, remove the <ettevotja_aadress> and <ettevotja_aadress> tags, then read into pandas, but it seems like there should be direct way to do this, similar to pandas' .json_normalize().

CodePudding user response:

So one work around is you can clarify a path to the node, in this case the ettevotja_aadress. Then can merge/concat to the original dataframe. If anyone has a solution to do this directly with the .read_xml(), please do share:

data = pd.read_xml(xml_data_source)
dataAddress = pd.read_xml(xml_data_source, xpath=".//ettevotja_aadress")
data = pd.concat([data, dataAddress], axis=1)
data = data.drop('ettevotja_aadress', axis=1)

Output:

print(data.head(2).to_string())
              nimi  ariregistri_kood ettevotja_oiguslik_vorm  ettevotja_oigusliku_vormi_alaliik kmkr_nr ettevotja_staatus ettevotja_staatus_tekstina ettevotja_esmakande_kpv  ettevotja_aadress                               teabesysteemi_link asukoht_ettevotja_aadressis  asukoha_ehak_kood                       asukoha_ehak_tekstina  indeks_ettevotja_aadressis  ads_adr_id  ads_ads_oid                              ads_normaliseeritud_taisaadress
0  000 Holdings OÜ          16372442                Osaühing                                NaN    None                 R         Registrisse kantud              23.11.2021                NaN  https://ariregister.rik.ee/est/company/16372442                        None                NaN                                        None                         NaN         NaN          NaN                                                         None
1     001 group OÜ          12754230                Osaühing                                NaN    None                 R         Registrisse kantud              17.11.2014                NaN  https://ariregister.rik.ee/est/company/12754230             Õismäe tee 78-9              176.0  Haabersti linnaosa, Tallinn, Harju maakond                     13513.0   2182337.0          NaN  Harju maakond, Tallinn, Haabersti linnaosa, Õismäe tee 78-9

CodePudding user response:

This could be done by providing an XSL stylesheet to flatten the original XML. Code will look like following:

xml = '''<?xml version='1.0' encoding='UTF-8'?>
<ettevotjad>
  <ettevotja>
    <nimi>000 Holdings OÜ</nimi>
    <ariregistri_kood>16372442</ariregistri_kood>
    <ettevotja_oiguslik_vorm>Osaühing</ettevotja_oiguslik_vorm>
    <ettevotja_oigusliku_vormi_alaliik/>
    <kmkr_nr/>
    <ettevotja_staatus>R</ettevotja_staatus>
    <ettevotja_staatus_tekstina>Registrisse kantud</ettevotja_staatus_tekstina>
    <ettevotja_esmakande_kpv>23.11.2021</ettevotja_esmakande_kpv>
    <ettevotja_aadress>
      <asukoht_ettevotja_aadressis/>
      <asukoha_ehak_kood/>
      <asukoha_ehak_tekstina></asukoha_ehak_tekstina>
      <indeks_ettevotja_aadressis/>
      <ads_adr_id></ads_adr_id>
      <ads_ads_oid></ads_ads_oid>
      <ads_normaliseeritud_taisaadress/>
    </ettevotja_aadress>
    <teabesysteemi_link>https://ariregister.rik.ee/est/company/16372442</teabesysteemi_link>
  </ettevotja>
  <ettevotja>
    <nimi>001 group OÜ</nimi>
    <ariregistri_kood>12754230</ariregistri_kood>
    <ettevotja_oiguslik_vorm>Osaühing</ettevotja_oiguslik_vorm>
    <ettevotja_oigusliku_vormi_alaliik/>
    <kmkr_nr/>
    <ettevotja_staatus>R</ettevotja_staatus>
    <ettevotja_staatus_tekstina>Registrisse kantud</ettevotja_staatus_tekstina>
    <ettevotja_esmakande_kpv>17.11.2014</ettevotja_esmakande_kpv>
    <ettevotja_aadress>
      <asukoht_ettevotja_aadressis>Õismäe tee 78-9</asukoht_ettevotja_aadressis>
      <asukoha_ehak_kood>0176</asukoha_ehak_kood>
      <asukoha_ehak_tekstina>Haabersti linnaosa, Tallinn, Harju maakond</asukoha_ehak_tekstina>
      <indeks_ettevotja_aadressis>13513</indeks_ettevotja_aadressis>
      <ads_adr_id>2182337</ads_adr_id>
      <ads_ads_oid></ads_ads_oid>
      <ads_normaliseeritud_taisaadress>Harju maakond, Tallinn, Haabersti linnaosa, Õismäe tee 78-9</ads_normaliseeritud_taisaadress>
    </ettevotja_aadress>
    <teabesysteemi_link>https://ariregister.rik.ee/est/company/12754230</teabesysteemi_link>
  </ettevotja>
</ettevotjad>
'''

import pandas as pd

stylesheet = '''<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/">
    <ettevotjad>
        <xsl:apply-templates select="//ettevotja"/>
    </ettevotjad>
</xsl:template>
<xsl:template match="ettevotja">
    <ettevotja>
      <xsl:copy-of select="node()[not(self::ettevotja_aadress)]"/>
      <xsl:apply-templates select="./ettevotja_aadress"/>
    </ettevotja>
 </xsl:template>
 <xsl:template match="ettevotja_aadress">
    <xsl:copy-of select="node()"/>
  </xsl:template>
 </xsl:stylesheet>
'''

df = pd.read_xml(xml, xpath="//ettevotja", stylesheet = stylesheet)

df.head()

The idea behind the code is that XSL transformation is applied to the XML document, flattening its structure. The result of the transformation will be:

<?xml version="1.0" encoding="UTF-8"?>
<ettevotjad>
   <ettevotja>
      <nimi>000 Holdings OÜ</nimi>
      <ariregistri_kood>16372442</ariregistri_kood>
      <ettevotja_oiguslik_vorm>Osaühing</ettevotja_oiguslik_vorm>
      <ettevotja_oigusliku_vormi_alaliik/>
      <kmkr_nr/>
      <ettevotja_staatus>R</ettevotja_staatus>
      <ettevotja_staatus_tekstina>Registrisse kantud</ettevotja_staatus_tekstina>
      <ettevotja_esmakande_kpv>23.11.2021</ettevotja_esmakande_kpv>
      <teabesysteemi_link>https://ariregister.rik.ee/est/company/16372442</teabesysteemi_link>
  
      <asukoht_ettevotja_aadressis/>
      <asukoha_ehak_kood/>
      <asukoha_ehak_tekstina/>
      <indeks_ettevotja_aadressis/>
      <ads_adr_id/>
      <ads_ads_oid/>
      <ads_normaliseeritud_taisaadress/>
    </ettevotja>
   <ettevotja>
      <nimi>001 group OÜ</nimi>
      <ariregistri_kood>12754230</ariregistri_kood>
      <ettevotja_oiguslik_vorm>Osaühing</ettevotja_oiguslik_vorm>
      <ettevotja_oigusliku_vormi_alaliik/>
      <kmkr_nr/>
      <ettevotja_staatus>R</ettevotja_staatus>
      <ettevotja_staatus_tekstina>Registrisse kantud</ettevotja_staatus_tekstina>
      <ettevotja_esmakande_kpv>17.11.2014</ettevotja_esmakande_kpv>
      <teabesysteemi_link>https://ariregister.rik.ee/est/company/12754230</teabesysteemi_link>
  
      <asukoht_ettevotja_aadressis>Õismäe tee 78-9</asukoht_ettevotja_aadressis>
      <asukoha_ehak_kood>0176</asukoha_ehak_kood>
      <asukoha_ehak_tekstina>Haabersti linnaosa, Tallinn, Harju maakond</asukoha_ehak_tekstina>
      <indeks_ettevotja_aadressis>13513</indeks_ettevotja_aadressis>
      <ads_adr_id>2182337</ads_adr_id>
      <ads_ads_oid/>
      <ads_normaliseeritud_taisaadress>Harju maakond, Tallinn, Haabersti linnaosa, Õismäe tee 78-9</ads_normaliseeritud_taisaadress>
    </ettevotja>
</ettevotjad>

After the transformation an XPath //ettevotja is applied to the transformation result, taking children of ettevotja elements as dataframe row fields.

  • Related