Сбой ОЗУ для функции преобразования XML в DataFrame - PullRequest
0 голосов
/ 05 августа 2020

Я создал следующую функцию, которая преобразует файл XML в DataFrame. Эта функция хорошо работает для файлов размером менее 1 ГБ, для всего, что превышает этот размер ОЗУ (13 ГБ ОЗУ Google Colab) дает сбой. То же самое произойдет, если я попробую локально на ноутбуке Jupyter (4 ГБ ОЗУ для ноутбука). Есть ли способ оптимизировать код?

Код

#Libraries
import pandas as pd
import xml.etree.cElementTree as ET

#Function to convert XML file to Pandas Dataframe    
def xml2df(file_path):

  #Parsing XML File and obtaining root
  tree = ET.parse(file_path)
  root = tree.getroot()

  dict_list = []

  for _, elem in ET.iterparse(file_path, events=("end",)):
      if elem.tag == "row":
        dict_list.append(elem.attrib)      # PARSE ALL ATTRIBUTES
        elem.clear()

  df = pd.DataFrame(dict_list)
  return df

Часть файла XML ('Badges. xml')

<badges>
  <row Id="82946" UserId="3718" Name="Teacher" Date="2008-09-15T08:55:03.923" Class="3" TagBased="False" />
  <row Id="82947" UserId="994" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82949" UserId="3893" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82950" UserId="4591" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82951" UserId="5196" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82952" UserId="2635" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82953" UserId="1113" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />

Я тоже пробовал код SAX, но получаю ту же ошибку исчерпания ОЗУ. импорт xml .sax

import xml.sax    

class BadgeHandler(xml.sax.ContentHandler):
    def __init__(self):
        self.row = None
        self.row_data = []
        self.df = None

    # Call when an element starts
    def startElement(self, tag, attributes):
        if tag == 'row':
            self.row = attributes._attrs

    # Call when an elements ends
    def endElement(self, tag):
        if self.row and tag == 'row':
            self.row_data.append(self.row)

    def endDocument(self):
        self.df = pd.DataFrame(self.row_data)

LOAD_FROM_FILE = True

handler = BadgeHandler()
if LOAD_FROM_FILE:
    print('loading from file')
    # 'rows.xml' is a file that contains your XML example
    xml.sax.parse('/content/Badges.xml', handler)
else:
    print('loading from string')
    xml.sax.parseString(xml_str, handler)
print(handler.df)

Ответы [ 3 ]

1 голос
/ 05 августа 2020

Вы оба загружаете файл в память и повторяете его.

Переключитесь на lxml s iterparse:

import pandas as pd
from lxml import etree


def xml2df(file_path):
    dict_list = []
    with open(file_path, "rb") as f:
        for _, elem in etree.iterparse(f, events=("end",)):
            if elem.tag == "row":
                dict_list.append(elem.attrib)
                #elem.clear()

    return pd.DataFrame(dict_list)
1 голос
/ 07 августа 2020

Я решил вникнуть в это глубже.

Оказывается, Pandas - это очень неэффективно с точки зрения памяти при создании фреймов данных из списка-диктов, неизвестно почему.

Вы можете найти мой полный код эксперимента (который генерирует гигабайт XML и читает его) на GitHub , но суть его такова (на моем Python 3.8, macOS)

  • чтение документа XML в фрейм данных с кодом, адаптированным из ответа @balderman (read_xml_to_pd.py):

    • занимает 6 838 556 КБ (~ 7 ГБ) до 10 508 892 КБ (~ 10 ГБ) памяти (кто знает, почему он меняется) и около 52 секунд для чтения данных в память
    • 12 128 400 КБ (12,1 ГБ) памяти для хранения этих данных и фрейма данных
  • чтение документа XML в файл CSV (с SAX):

    • занимает 16-17 мегабайт памяти и около 1,5 минут для запись 400 мегабайт badges.csv (python read_xml_to_csv.py)
    • занимает до 2 989 080 КБ (2,9 ГБ) памяти и около 10 секунд для чтения CSV с использованием pd.read_csv() (read_csv_to_pd.py)
    • наконец, 2,033,208 КБ (2,0 ГБ) памяти требуется только для хранения фрейма данных

Двоичный промежуточный формат, вероятно, будет еще быстрее и эффективнее.

1 голос
/ 05 августа 2020

Да. есть способ оптимизировать код.

Используйте SAX .

С SAX вы не будете загружать все XML в RAM.

См. Здесь пример: https://www.tutorialspoint.com/python/python_xml_processing.htm

Код ниже:

import xml.sax

import pandas as pd

xml_str = '''<badges>
  <row Id="82946" UserId="3718" Name="Teacher" Date="2008-09-15T08:55:03.923" Class="3" TagBased="False" />
  <row Id="82947" UserId="994" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82949" UserId="3893" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82950" UserId="4591" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82951" UserId="5196" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82952" UserId="2635" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  <row Id="82953" UserId="1113" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" />
  </badges>'''


class BadgeHandler(xml.sax.ContentHandler):
    def __init__(self):
        self.row = None
        self.row_data = []
        self.df = None

    # Call when an element starts
    def startElement(self, tag, attributes):
        if tag == 'row':
            self.row = attributes._attrs

    # Call when an elements ends
    def endElement(self, tag):
        if self.row and tag == 'row':
            self.row_data.append(self.row)

    def endDocument(self):
        self.df = pd.DataFrame(self.row_data)


LOAD_FROM_FILE = True

handler = BadgeHandler()
if LOAD_FROM_FILE:
    print('loading from file')
    # 'rows.xml' is a file that contains your XML example
    xml.sax.parse('rows.xml', handler)
else:
    print('loading from string')
    xml.sax.parseString(xml_str, handler)
print(handler.df)

вывод

      Id UserId     Name                     Date Class TagBased
0  82946   3718  Teacher  2008-09-15T08:55:03.923     3    False
1  82947    994  Teacher  2008-09-15T08:55:03.957     3    False
2  82949   3893  Teacher  2008-09-15T08:55:03.957     3    False
3  82950   4591  Teacher  2008-09-15T08:55:03.957     3    False
4  82951   5196  Teacher  2008-09-15T08:55:03.957     3    False
5  82952   2635  Teacher  2008-09-15T08:55:03.957     3    False
6  82953   1113  Teacher  2008-09-15T08:55:03.957     3    False
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...