pandas 将 XML 提取到以父属性为列标题的数据框中

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16991691/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 20:53:33  来源:igfitidea点击:

Extracting XML into data frame with parent attribute as column title

pythonpandaslxml

提问by jessi

I have thousands of XML files that I will be processing, and they have a similar format, but different parent names and different numbers of parents. Through books, google, tutorials, and just trying out codes, I've been able to pull out all of this data. See, for example: Parsing xml to pandas data frame throws memory errorand Dynamic search through xml attributes using lxml and xpath in python

我将处理数以千计的 XML 文件,它们具有相似的格式,但父名称和父数量不同。通过书籍、谷歌、教程和尝试代码,我已经能够提取所有这些数据。参见,例如:Parsing xml to pandas data frame throws memory errorand Dynamic search through xml attributes using lxml and xpath in python

However, I realized that I was extracting the data poorly, with a child "Time" repeated for each parent.

但是,我意识到我提取的数据很差,每个父母都重复了一个孩子的“时间”。

Here is what I am trying to get.

这是我想要得到的。

Time   blah   abc
1200   100   2
1300   30    4
1400   70    2

Here is what I know how to get. But my current method is clunky (I'll show below the example XML)

这是我知道如何获得的。但是我目前的方法很笨拙(我将在示例 XML 下方展示)

    child      Time   grandchild
0     blah     1200    100
1     blah     1300    30
...
n-2   abc      1200    2
n-1   abc      1300    4
n     abc      1400    2

Example XML format

XML 格式示例

<outer>
   <inner>
      <parent name = "blah" id = "1"> 
         <child Time = "1200"> 
            <grandchild>100</grandchild>  
         </child>
         <child Time = "1300">
            <grandchild>30</grandchild>
         </child>
         <child Time = "1400">
            <grandchild>70</grandchild>
         </child>
      </parent>
      <parent name = "abc" id = "2"> 
         <child Time = "1200">   
            <grandchild>2</grandchild> 
         </child>
         <child Time = "1300">
            <grandchild>4</grandchild>
         </child>
         <child Time = "1400">
            <grandchild>2</grandchild>
         </child>
      </parent>      
      <parent name = "1234" id = "7734"> 
         <other> 12 </other>
      </parent> 
   </inner>
</outer>

Here is how I can get my output:

这是我获得输出的方法:

from lxml import etree, objectify
from pandas import *
dTime=[]
dparent = []
dgrandchild=[]
for df in root.xpath('/*/*/*/parent/child'):
    dparent.append(df.getparent().attrib['name'])
    ## Iterate over attributes of time for specific parent
    for attrib in df.attrib:
    dTime.append(df.attrib[attrib])
        ## grandchild is a child of time, and iterate
        subfields = df.getchildren()
        for subfield in subfields:
         dgrandchild.append(subfield.text)
df=DataFrame({'Parent': dparent,'Time':dTime,'grandchild':dgrandchld})

I could just take this output and re-shape it, but that seems inefficient and a very clunky approach.

我可以只获取这个输出并重新塑造它,但这似乎效率低下,而且是一种非常笨拙的方法。

I think I need something of the flavor:

我想我需要一些味道:

#this does not work
data = []
for elem in root.xpath('/*/*/*/parent/child'):
   elem_data = {}
   for attrib in elem.attrib:
       elem_data['Time'] = elem.attrib[attrib])
   for child in elem.getchildren():
       elem_data[getparent().attrib['name'])] = child.text
       data.append(elem_data)
ndata = DataFrame(data)

采纳答案by Andy Hayden

I recommend just parsing to a DataFrame first, similar to how you are already (see below for my implementation) and then tweaking it to your requirements.

我建议先解析到 DataFrame,类似于您已经的方式(见下文我的实现),然后根据您的要求对其进行调整。

Then you're looking for a pivot:

那么你正在寻找一个pivot

In [11]: df
Out[11]:
  child  Time  grandchild
0  blah  1200         100
1  blah  1300          30
2   abc  1200           2
3   abc  1300           4
4   abc  1400           2

In [12]: df.pivot('Time', 'child', 'grandchild')
Out[12]:
child  abc  blah
Time
1200     2   100
1300     4    30
1400     2   NaN

I recommend first parse from a fileand take out the things you want into a list of tuples:

我建议首先从文件中解析并将您想要的内容取出到元组列表中:

from lxml import etree
root = etree.parse(file_name)

parents = root.getchildren()[0].getchildren()

In [21]: elems = [(p.attrib['name'], int(c.attrib['Time']), int(gc.text))
                      for p in parents
                      for c in p
                      for gc in c]

In [22]: elems
Out[22]:
[('blah', 1200, 100),
 ('blah', 1300, 30),
 ('blah', 1400, 70),
 ('abc', 1200, 2),
 ('abc', 1300, 4),
 ('abc', 1400, 2)]

For multiple files you could just whack it in an even longer list comprehension. Which shouldn't be too slow unless you have a huge number of xmls (here filesis the list of xmls)...

对于多个文件,您可以在更长的列表理解中对其进行重击。除非你有大量的 xmls(这里files是 xmls 列表),否则应该不会太慢......

elems = [(p.attrib['name'], int(c.attrib['Time']), int(gc.text))
            for f in files
            for p in etree.parse(f).getchildren()[0].getchildren()
            for c in p
            for gc in c]

Put them in a DataFrame:

将它们放入 DataFrame 中:

In [23]: pd.DataFrame(elems, columns=['child', 'Time', 'grandchild'])
Out[23]:
  child  Time grandchild
0  blah  1200        100
1  blah  1300         30
2  blah  1400         70
3   abc  1200          2
4   abc  1300          4
5   abc  1400          2

thendo the pivot. :)

然后做枢轴。:)