Как преобразовать файл XML в файл данных Pandas - PullRequest
1 голос
/ 24 января 2020

Я хочу преобразовать следующий XML файл:

<data>
  <level_1 name="employment">
    <level_2 name="sub-employment">
      <indicator>ind1</indicator>
      <indicator>ind2</indicator>
    </level_2>
    <level_2 name="sub-employment2">
      <indicator>ind3</indicator>
    </level_2>
  </level_1>
  <level_1 name="health">
    <level_2 name="sub-health">
      <level_3 name="sub-sub-health">
        <indicator>ind4</indicator>
      </level_3>
    </level_2>
  </level_1>
</data>

в Pandas фрейм данных с результатом, похожим на:

  level_1   level_2         level_3        indicator

0  employment  sub-employment   None             ind1
1  employment  sub-employment   None             ind2    
2  employment  sub-employment2  None             ind3 
3  health      sub-health       sub-sub-health   ind4

Я использовал следующий код после импорта xml .etree.cElementTree как et и импорта pandas как pd:

def getvalueofnode(node):
    """ return node text or None """
    return node.text if node is not None else None          
def main():
    """ main """
    parsed_xml = et.parse("tree.xml")
    dfcols = ['level_1', 'level_2', 'level_3', 'indicator']
    df_xml = pd.DataFrame(columns=dfcols)

    for node in parsed_xml.getroot():
        name = node.attrib.get('name')
        level_2 = node.find('level_2')
        level_3 = node.find('level_3')
        indicator = node.find('indicator')

        df_xml = df_xml.append(
            pd.Series([name, getvalueofnode(level_2), getvalueofnode(level_3),
                       getvalueofnode(indicator)], index=dfcols),
            ignore_index=True)     
    print(df_xml)     
main()

но я получаю неправильный результат:

      level_1   level_2 level_3 indicator
0  employment  \n          None      None
1      health  \n          None      None

Что я делаю неправильно здесь

1 Ответ

1 голос
/ 25 января 2020

Определите следующую функцию, создав словарь предков, начиная с узла и выше:

def parNames(node, root):
    names = {}
    while True:
        node = parentMap[node]
        if node is root:
            return names
        names[node.tag] = node.attrib['name']

Это понадобится позже. Он использует parentMap словарь, который будет создан в ближайшее время.

Прочитайте ваш входной файл:

tree = et.parse('tree.xml')
root = tree.getroot()

Фактическая обработка должна начаться с создания parent map - словарь, который для каждого узла возвращает своего родителя:

parentMap = {}
for parent in root.iter():
    for child in parent:
        parentMap[child] = parent

Чтобы создать исходные данные для вашего DataFrame, выполните:

rows = []
for it in root.iter('indicator'):
    row = parNames(it, root)
    row[it.tag] = it.text
    rows.append(row)

Этот l oop создает список словарей (данные для каждой строки). Каждая строка (словарь) содержит:

  • под iterator key - текст соответствующего узла,
  • под «родительскими» ключами ( level_. .. ) name атрибуты всех родителей (возвращается функцией parNames ).

Следующим шагом является создание DataFrame:

df2 = pd.DataFrame(rows).fillna('').sort_index(axis=1)

И единственный шаг, который нужно сделать, - переместить столбец индикатор в последнюю позицию:

df2 = df2.reindex(df2.columns.drop('indicator')
    .append(pd.Index(['indicator'])),axis=1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...