【问题标题】:Nested XML to Pandas dataframe将 XML 嵌套到 Pandas 数据框
【发布时间】:2020-02-12 16:53:03
【问题描述】:

我正在尝试创建一个脚本来将嵌套的 XML 文件转换为 Pandas 数据框。我找到了这篇文章https://medium.com/@robertopreste/from-xml-to-pandas-dataframes-9292980b1c1c,它在进入第二级(父母、孩子)方面做得很好,但我既不知道如何进入更深层次(例如孙子),也不知道如何进入孩子(例如“邻居”->“名字”)。

这是我的 XML 结构:

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank>1</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
            <neighbor2 name="Italy" direction="S"/>
    </country>
    <country name="Singapore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

这是我的代码:

import pandas as pd
import xml.etree.ElementTree as et

def parse_XML(xml_file, df_cols): 

    xtree = et.parse(xml_file)
    xroot = xtree.getroot()
    rows = []

    for node in xroot: 
        res = []
        res.append(node.attrib.get(df_cols[0]))
        for el in df_cols[1:]: 
            if node is not None and node.find(el) is not None:
                res.append(node.find(el).text)
            else: 
                res.append(None)
        rows.append({df_cols[i]: res[i] 
                     for i, _ in enumerate(df_cols)})

    out_df = pd.DataFrame(rows, columns=df_cols)

    return out_df

xml_file= "example.xml"
df_cols = ["name","year","direction"]

out_df=parse_XML(xml_file, df_cols)
out_df

我想要得到的是如下结构:

| name          | year | neighbor name 1 | neighbor direction 1 | neighbor2 name 1 |
|---------------|------|-----------------|----------------------|------------------|
| Liechtenstein | 2008 | Austria         | E                    | Italy            |
|               |      |                 |                      |                  |
|               |      |                 |                      |                  |

结构需要尽可能灵活,以便与不同的文件一起使用时需要很少的编辑。我正在获取具有不同数据结构的 XML 文件,因此我希望每次都能够进行一些最少的编辑。

非常感谢!!

【问题讨论】:

    标签: python python-3.x xml pandas


    【解决方案1】:

    我已经为类似的用例制作了一个包。它也可以在这里工作。

    pip install pandas_read_xml
    

    你可以做类似的事情

    import pandas_read_xml as pdx
    
    df = pdx.read_xml('filename.xml', ['data'])
    

    要变平,你可以

    df = pdx.flatten(df)
    

    df = pdx.fully_flatten(df)
    

    【讨论】:

    • 看起来非常有用的库 - 希望看到开发继续!
    • 啊这让 XML 变得简单多了。 fully_flatten 应该在原生 pandas 中!
    【解决方案2】:

    您需要一个递归函数来展平行,以及一个处理重复数据的机制。

    这很混乱,并且取决于数据和嵌套,您最终可能会得到相当奇怪的数据帧。

    import xml.etree.ElementTree as et
    from collections import defaultdict
    import pandas as pd
    
    
    def flatten_xml(node, key_prefix=()):
        """
        Walk an XML node, generating tuples of key parts and values.
        """
    
        # Copy tag content if any
        text = (node.text or '').strip()
        if text:
            yield key_prefix, text
    
        # Copy attributes
        for attr, value in node.items():
            yield key_prefix + (attr,), value
    
        # Recurse into children
        for child in node:
            yield from flatten_xml(child, key_prefix + (child.tag,))
    
    
    def dictify_key_pairs(pairs, key_sep='-'):
        """
        Dictify key pairs from flatten_xml, taking care of duplicate keys.
        """
        out = {}
    
        # Group by candidate key.
        key_map = defaultdict(list)
        for key_parts, value in pairs:
            key_map[key_sep.join(key_parts)].append(value)
    
        # Figure out the final dict with suffixes if required.
        for key, values in key_map.items():
            if len(values) == 1:  # No need to suffix keys.
                out[key] = values[0]
            else:  # More than one value for this key.
                for suffix, value in enumerate(values, 1):
                    out[f'{key}{key_sep}{suffix}'] = value
    
        return out
    
    
    # Parse XML with etree
    tree = et.XML("""<?xml version="1.0"?>
    <data>
        <country name="Liechtenstein">
            <rank>1</rank>
            <year>2008</year>
            <gdppc>141100</gdppc>
            <neighbor name="Austria" direction="E"/>
            <neighbor name="Switzerland" direction="W"/>
            <neighbor2 name="Italy" direction="S"/>
        </country>
        <country name="Singapore">
            <rank>4</rank>
            <year>2011</year>
            <gdppc>59900</gdppc>
            <neighbor name="Malaysia" direction="N"/>
            <cities>
                <city name="Chargin" population="1234" />
                <city name="Firin" population="4567" />
            </cities>
        </country>
        <country name="Panama">
            <rank>68</rank>
            <year>2011</year>
            <gdppc>13600</gdppc>
            <neighbor name="Costa Rica" direction="W"/>
            <neighbor name="Colombia" direction="E"/>
        </country>
    </data>
    """)
    
    # Generate flat rows out of the root nodes in the tree
    rows = [dictify_key_pairs(flatten_xml(row)) for row in tree]
    df = pd.DataFrame(rows)
    print(df)
    

    输出

                name rank  year   gdppc neighbor-name-1 neighbor-name-2 neighbor-direction-1 neighbor-direction-2 neighbor2-name neighbor2-direction neighbor-name neighbor-direction cities-city-name-1 cities-city-name-2 cities-city-population-1 cities-city-population-2
    0  Liechtenstein    1  2008  141100         Austria     Switzerland                    E                    W          Italy                   S           NaN                NaN                NaN                NaN                      NaN                      NaN
    1      Singapore    4  2011   59900             NaN             NaN                  NaN                  NaN            NaN                 NaN      Malaysia                  N            Chargin              Firin                     1234                     4567
    2         Panama   68  2011   13600      Costa Rica        Colombia                    W                    E            NaN                 NaN           NaN                NaN                NaN                NaN                      NaN                      NaN
    

    【讨论】:

    • 太棒了,谢谢!现在我只想解析 XML 文件,而不必将 XML 文本复制并粘贴到代码中。我尝试用 'tree=et.parse('example.xml') 替换 'tree=et.XML ()' 但我收到以下错误 'ElementTree' object is not iterable。我应该做些什么不同的事情?
    • tree = et.parse(...).getroot()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-26
    • 2016-01-25
    • 2018-06-01
    相关资源
    最近更新 更多