【问题标题】:RAM crashed for XML to DataFrame conversion function用于 XML 到 DataFrame 转换功能的 RAM 崩溃
【发布时间】:2025-12-02 02:15:01
【问题描述】:

我创建了以下将 XML 文件转换为 DataFrame 的函数。此功能适用于小于 1 GB 的文件,任何大于 RAM(13GB Google Colab RAM)崩溃的文件。如果我在 Jupyter Notebook(4GB 笔记本电脑 RAM)上本地尝试,也会发生同样的情况。有没有办法优化代码?

代码

#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 代码,但我得到了同样的 RAM Exhausted 错误。 导入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)

【问题讨论】:

  • 如果您从dict_list 中删除创建数据框的尝试,它会崩溃吗?
  • 另外,请显示您得到的实际回溯/错误。
  • @AKX 我没有得到回溯,RAM 只是崩溃并且会话重新启动。除了dict_list,我没有任何其他方法可以创建数据框。

标签: python xml pandas dataframe dictionary


【解决方案1】:

您将文件加载到内存中对其进行迭代。

切换到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)

【讨论】:

  • @IshanDutta 已编辑,再试一次。您需要安装 lxml。
  • 我得到了回溯'lxml' has no attribute 'iterparse'
  • 糟糕,我的错。这是lxml.etree.iterparse。固定。
  • 我试过lxml.etree.iterparse,输出只有index,除此之外没有其他列。
  • 如果我删除 elem.clear() 那么 RAM 会再次崩溃。
【解决方案2】:

是的。有办法优化代码。

使用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

【讨论】:

  • OP 正在使用 iterparse。它也不会将文件加载到 RAM 中。
  • @AKX 请参阅docs.python.org/3.8/library/… "将 XML 部分逐步解析为元素树..."
  • 啊,你是对的,它看起来像。 LXML 的 iterparse 不会产生最终的树 :)
  • @balderman 我研究了 SAX,但我无法编写相同的代码,我对使用 XML 文件很陌生。可以提供代码吗?
  • @IshanDutta 提供 XML 的小 sn-p(确保 XML 有效)
【解决方案3】:

我决定深入研究一下。

事实证明,当谁知道为什么从字典列表创建数据帧时,Pandas 内存效率非常低

You can find my full experiment code (that generates a gigabyte of XML and reads it) on GitHub,但它的要点是(在我的 Python 3.8,macOS 上)

  • 使用改编自@balderman 的答案 (read_xml_to_pd.py) 的代码将 XML 文档读取到数据框:

    • 需要 6,838,556k (~7 GB) 到 10,508,892k (~10 GB) 内存(谁知道它为什么会变化),将数据读入内存大约需要 52 秒
    • 12,128,400k (12.1 GB) 内存用于保存该数据和数据帧
  • 将 XML 文档读入 CSV 文件(使用 SAX):

    • 需要 16-17 兆字节的内存和大约 1.5 分钟来写入 400 兆字节的badges.csv (python read_xml_to_csv.py)
    • 使用pd.read_csv() (read_csv_to_pd.py) 读取 CSV 最多需要 2,989,080k (2.9 GB) 内存和大约 10 秒时间
    • 最后需要 2,033,208k (2.0 GB) 内存来保存数据帧

二进制中间格式可能会更快,更有效。

【讨论】:

  • 有趣。感谢分享。
  • @AKX read_xml_to_csv.py 文件出现以下错误 ImportError: cannot import name 'start_memory_usage_thread'。我写了utils,而不是util
  • 我提交的代码对我来说很好用。但是您不需要跟踪线程来使用。
  • 如果我使用小于 2 GB 的文件,此代码可以工作,当我将它用于更大的文件时,代码单元只会一直运行下去。导致问题的文件是 4 GB 和 15 GB 的“压缩大小”。有什么建议@AKX?
  • 那么您将不得不找出另一种处理数据的方法,而不是尝试将其加载到单个数据帧中。 (或者使用内存更大的机器。)