The read_xml
函数于Pandas用于读取 XML(可扩展标记语言)文件并将其转换为 DataFrame。
让我们深入研究实际示例来了解它是如何工作的。
Pandas read_xml
Syntax
基本语法为pandas.read_xml
is:
pandas.read_xml(
path_or_buffer,
xpath=None,
namespaces=None,
elems_only=True,
attrs_only=False,
names=None,
encoding=None,
parser="lxml",
stylesheet=None,
compression="infer",
storage_options=None,
dtype_backend=None,
**kwargs
)
关键参数是:
-
路径或缓冲区:XML 文件、URL 或类似文件对象的路径。
-
xpath:XPath 表达式,用于选择要解析的 XML 的特定部分。
-
命名空间:包含 XML 命名空间的字典,用于使用 XPath 细化选择。
-
仅限元素: If
True
,仅解析元素的文本值。如果False
,元素和属性都被解析。
-
仅属性: If
True
,仅解析属性的值。如果False
,元素的值被解析。
-
names:生成的 DataFrame 的列名称列表。
-
encoding:XML 的编码类型。默认为 UTF-8。
-
parser:要使用的 XML 解析器(
lxml
or etree
).
-
样式表:XSLT 样式表文件的路径,用于在解析之前转换 XML 数据。
-
压缩:压缩类型(‘infer’、‘gzip’、‘bz2’、‘zip’、‘xz’、无)。如果是“infer”,则使用文件扩展名来确定压缩类型。
-
存储选项:如果需要,存储连接的额外选项。
-
dtype_后端:用于数据类型推断的后端(“python”或“lxml”)。
从各种来源阅读
无论您的本地磁盘上有 XML 文件、返回 XML 数据的 URL 还是类似文件的对象,read_xml
可以从这些源读取 XML。
从本地 XML 文件读取
让我们从最常见的场景开始:从本地 XML 文件读取。
import pandas as pd
xml_data = """
<data>
<row>
<shape>square</shape>
<degrees>360</degrees>
<sides>4.0</sides>
</row>
<row>
<shape>triangle</shape>
<degrees>180</degrees>
<sides>3.0</sides>
</row>
</data>
"""
with open("shapes.xml", "w") as file:
file.write(xml_data)
df = pd.read_xml("shapes.xml")
print(df)
Output:
shape degrees sides
0 square 360 4.0
1 triangle 180 3.0
在这里,我们将一个简单的 XML 字符串写入名为的文件中shapes.xml
然后使用将其读入 DataFrameread_xml
.
从 URL 读取
有时您的数据在线托管。在这种情况下,read_xml
可以直接从URL读取。
url = "https://example.com/data.xml"
df = pd.read_xml(url)
从类似文件的对象中读取
如果您有类似文件的对象(例如,来自请求或内存中的文件)。以下是如何使用阅读它们:
from io import StringIO
xml_data = """
<data>
<row>
<name>John</name>
<age>28</age>
</row>
<row>
<name>Jane</name>
<age>24</age>
</row>
</data>
"""
data_io = StringIO(xml_data)
df = pd.read_xml(data_io)
print(df)
Output:
name age
0 John 28
1 Jane 24
在这个例子中,我们使用模拟一个类似文件的对象StringIO
然后将其中的 XML 数据读取到 DataFrame 中。
选择 XML 解析器
Pandas’ read_xml
提供在不同 XML 解析器之间进行选择的灵活性。您选择的解析器会影响性能,因此值得了解可用的选项。
lxml 解析器
默认情况下,read_xml
使用“lxml”解析器。 ‘lxml’ 非常高效并且适合大型 XML 文件。
df = pd.read_xml("shapes.xml", parser="lxml")
print(df)
Output:
shape degrees sides
0 square 360 4.0
1 triangle 180 3.0
这段代码明确指定了lxml
解析器,尽管即使您没有指定它,它也是默认的。
etree 解析器
另一种选择是etree
来自 Python 标准库的解析器。
df = pd.read_xml("shapes.xml", parser="etree")
print(df)
Output:
shape degrees sides
0 square 360 4.0
1 triangle 180 3.0
基准解析器
为了演示目的,我将首先生成一个大型 XML 文件。然后,我将进行基准测试read_xml()
使用两个解析器的函数。
我们将生成一个包含 100 万个条目的大型 XML 文件来比较性能。
import pandas as pd
import random
import time
# Step 1: Generate a large XML file
num_entries = 1000000
shapes = ["triangle", "square", "pentagon", "hexagon"]
xml_data = ''
for _ in range(num_entries):
shape = random.choice(shapes)
xml_data += f'{shape}{random.randint(100, 400)}{random.randint(3, 6)}'
xml_data += ''
with open("large_sample.xml", "w") as f:
f.write(xml_data)
file_path ="large_sample.xml"
# Benchmark for lxml parser
start_time_lxml = time.time()
df_lxml = pd.read_xml(file_path, parser="lxml")
end_time_lxml = time.time()
lxml_duration = end_time_lxml - start_time_lxml
# Benchmark for etree parser
start_time_etree = time.time()
df_etree = pd.read_xml(file_path, parser="etree")
end_time_etree = time.time()
etree_duration = end_time_etree - start_time_etree
print(lxml_duration)
print(etree_duration)
Output:
44.94610905647278
18.623760223388672
The etree
解析器要快得多。
指定列名称
考虑一个名为persons.xml
包含以下内容:
<data>
<row>
<nm>John</nm>
<ag>28</ag>
</row>
<row>
<nm>Jane</nm>
<ag>24</ag>
</row>
</data>
请注意,列标签是缩写的(nm
对于名字和ag
对于年龄)。如果您想在将 XML 读入 DataFrame 时将这些列重命名为更具描述性的名称,您可以使用names
范围。
df = pd.read_xml("persons.xml", names=["name", "age"])
print(df)
Output:
name age
0 John 28
1 Jane 24
处理压缩的 XML 文件
Pandas’ read_xml
具有直接读取压缩XML文件的能力,无需手动解压。
读取 GZIP 压缩 XML
假设您有一个使用 GZIP 压缩的 XML 文件,文件名为data.xml.gz
.
要将其读入 DataFrame:
df = pd.read_xml("data.xml.gz", compression='gzip')
print(df)
读取 BZIP2 压缩 XML
如果您的 XML 文件是使用 BZIP2 压缩的,例如,data.xml.bz2
:
df = pd.read_xml("data.xml.bz2", compression='bz2')
print(df)
读取 ZIP 压缩的 XML
对于 ZIP 存档中的 XML 文件,例如data.xml.zip
:
df = pd.read_xml("data.xml.zip", compression='zip')
print(df)
自动检测压缩
在许多情况下,您甚至不必指定压缩类型。read_xml
足够智能,可以自动检测文件扩展名的压缩,例如.gz
, .bz2
, and .zip
.
df = pd.read_xml("data.xml.gz")
print(df)
即使没有compression
参数,因为.gz
文件扩展名。
高级存储连接选项
The storage_options
参数输入read_xml
允许您传递连接到云存储等存储系统所需的额外选项。
通过身份验证从 S3 存储桶中读取数据
假设您的 XML 数据存储在 S3 存储桶上,并且访问此数据需要特定的凭据。使用方法如下storage_options
:
s3_path = "s3://your_bucket_name/path_to_file.xml"
df = pd.read_xml(s3_path,
storage_options={"key": "your_access_key",
"secret": "your_secret_key"})
print(df)
与其他存储系统一起使用
storage_options
不限于S3。您可以使用它来传递各种存储系统所需的连接参数,例如 GCS(Google 云存储)、Azure Blob 存储等。
例如,连接到 GCS 将如下所示:
gcs_path = "gcs://your_bucket_name/path_to_file.xml"
df = pd.read_xml(gcs_path,
storage_options={"token": "your_gcs_token"})
print(df)
使用 XPath 表达式选择数据
XPath 是一种功能强大的查询语言,允许您处理需要提取特定部分或属性的复杂 XML 结构。
The xpath
参数输入read_xml
函数允许您使用 XPath 表达式。
基本 XPath 选择
考虑一个 XML 文件products.xml
具有以下结构:
<products>
<product category="electronics">
<name>Smartphone</name>
<price>500</price>
</product>
<product category="books">
<name>Python Guide</name>
<price>30</price>
</product>
</products>
仅选择“电子产品”类别中的产品:
df = pd.read_xml("products.xml", xpath="//product[@category='electronics']")
print(df)
Output:
category name price
0 electronics Smartphone 500
您可以进一步细化您的选择。仅提取“电子”类别中的产品名称:
df = pd.read_xml("data.xml", xpath="//product[@category='electronics']")
df_name = df['name']
print(df_name)
Output:
0 Smartphone
Name: name, dtype: object
在 XPath 中使用函数
XPath 提供了各种函数来使您的选择更加动态。例如,选择价格高于 100 的产品:
df = pd.read_xml("products.xml", xpath="//product[number(price)>100]")
print(df)
Output:
category name price
0 electronics Smartphone 500
使用 XML 命名空间
XML 命名空间用于区分具有相同名称但在不同 XML 词汇表中定义的元素。
它们对于避免 XML 文档中的命名冲突至关重要。
考虑以下 XML,另存为data.xml
,它使用命名空间:
<root xmlns:product="http://www.example.com/product" xmlns:price="http://www.example.com/price">
<product:item>
<product:name>Laptop</product:name>
<price:value>1000</price:value>
</product:item>
<product:item>
<product:name>Mouse</product:name>
<price:value>20</price:value>
</product:item>
</root>
注意xmlns:product
and xmlns:price
分别定义产品和价格的 XML 命名空间的属性。
使用 Pandas 中的命名空间解析 XML
为了使用命名空间解析 XML,我们需要将这些命名空间提供给read_xml
功能。
namespaces = {
"product": "http://www.example.com/product",
"price": "http://www.example.com/price"
}
df = pd.read_xml("data.xml", xpath="//product:item", namespaces=namespaces)
print(df)
Output:
name value
0 Laptop 1000
1 Mouse 20
在代码中,我们提供了一个命名空间字典并使用namespaces
范围。
这确保了 Pandas 可以正确解释 XML 命名空间并提取相关数据。
带有命名空间的 XPath
查询命名空间内的元素时,您将使用您定义的前缀:
namespaces = {
'product': 'http://www.example.com/product',
'price': 'http://www.example.com/price'
}
df = pd.read_xml("data.xml", xpath="//product:item", namespaces=namespaces)
product_names = df["name"]
print(product_names)
Output:
0 Laptop
1 Mouse
Name: name, dtype: object
此代码专门使用以下方式提取产品名称product
命名空间前缀。
只读元素值
The read_xml
函数提供了elems_only
参数仅读取元素值,不包括任何属性。
考虑这个 XML,另存为products.xml
:
<products>
<item type="electronics">
<name>Laptop</name>
<price>1000</price>
</item>
<item type="accessory">
<name>Mouse</name>
<price>20</price>
</item>
</products>
Each item
元素有一个type
属性以及产品名称和价格的子元素。
解析 XML 忽略属性
仅读取元素值并忽略属性,例如type
,你设置elems_only=True
:
df = pd.read_xml("products.xml", elems_only=True)
print(df)
Output:
name price
0 Laptop 1000
1 Mouse 20
为了进行比较,以下是当您不指定时会发生的情况elems_only
:
df_default = pd.read_xml("products.xml")
print(df_default)
Output:
type name price
0 electronics Laptop 1000
1 accessory Mouse 20
在默认行为中,两个属性type
和元素值name
and price
包含在生成的 DataFrame 中。
只读属性值
The attrs_only
参数输入read_xml
函数允许您仅从 XML 中提取属性值。
回想一下我们的products.xml
:
<products>
<item type="electronics">
<name>Laptop</name>
<price>1000</price>
</item>
<item type="accessory">
<name>Mouse</name>
<price>20</price>
</item>
</products>
在这里,我们有type
每个内的属性item
元素,以及产品名称和价格的子元素。
重点解析 XML 属性
要只读属性并忽略元素值,您可以设置attrs_only=True
:
df = pd.read_xml("products.xml", attrs_only=True)
print(df)
Output:
type
0 electronics
1 accessory
如果您不指定,这就是您将得到的结果attrs_only
:
df_default = pd.read_xml("products.xml")
print(df_default)
Output:
type name price
0 electronics Laptop 1000
1 accessory Mouse 20
默认情况下,两个type
属性和元素值name
and price
被提取。
指定列数据类型
The dtype
参数输入read_xml
函数允许您控制结果列的数据类型。
默认情况下,read_xml
尝试推断适当的类型:
df_default = pd.read_xml("products.xml")
print(df_default.dtypes)
Output:
type object
name object
price int64
dtype: object
The price
列被正确推断为整数(int64
).
手动设置数据类型
如果要显式指定列数据类型,可以使用dtype
范围:
df = pd.read_xml("products.xml", dtype={"price": "float64", "name": "string"})
print(df.dtypes)
Output:
type object
name string[python]
price float64
dtype: object
在这里,我们设置了price
列为浮点型 (float64
)和name
列作为字符串类型(string
).
特定列的自定义转换函数
随着converters
参数输入read_xml
,您可以定义自定义函数来在解析过程中转换特定列。
让我们考虑一个名为sales.xml
包含以下内容:
<sales>
<transaction>
<product>Laptop</product>
<amount>$1000.00</amount>
<date>10th Sep 2023</date>
</transaction>
<transaction>
<product>Mouse</product>
<amount>$20.00</amount>
<date>11th Sep 2023</date>
</transaction>
</sales>
The amount
值有美元符号,并且date
值具有序数指示符,这使得它们对于自动类型推断来说是非标准的。
定义自定义转换函数
出于我们的目的,我们将创建两个函数:
-
convert_currency
:删除美元符号并转换为浮点数。
-
convert_date
:解析非标准日期格式。
import pandas as pd
from datetime import datetime
def convert_currency(value):
return float(value.replace('$', ''))
def convert_date(value):
return datetime.strptime(value, "%dth %b %Y")
Using converters
in read_xml
现在,使用以下命令应用这些自定义函数converters
范围:
df = pd.read_xml(
"sales.xml",
converters={
"amount": convert_currency,
"date": convert_date
}
)
print(df)
Output:
product amount date
0 Laptop 1000.0 2023-09-10
1 Mouse 20.0 2023-09-11
The amount
列现在是 float 类型,并且date
列是一个日期时间对象,准备进一步分析。
将特定列解析为日期
虽然您可以使用前面所示的自定义转换器,但对于标准日期格式,parse_dates
参数输入read_xml
提供了更直接的机制。
考虑一个名为events.xml
:
<events>
<event>
<name>Conference A</name>
<date>2023-09-10</date>
</event>
<event>
<name>Workshop B</name>
<date>2023-09-12</date>
</event>
</events>
The date
值在YYYY-MM-DD
格式,这是标准的。
使用解析日期parse_dates
为确保date
列被解析为日期时间对象,使用parse_dates
范围:
df = pd.read_xml("events.xml", parse_dates=['date'])
print(df.dtypes)
Output:
name object
date datetime64[ns]
dtype: object
处理多个日期列
如果您的 XML 包含多个日期列,您可以通过提供列表轻松解析所有这些列:
df = pd.read_xml("events.xml", parse_dates=['date', 'another_date_column'])
推断和强制执行数据类型
虽然 Pandas 可以推断数据类型,但有时您需要更多的控制或特异性。
The dtype_backend
中的参数read_xml
函数通过提供后端特定的数据类型推断来提供此级别的详细信息。
默认情况下,Pandas 使用其数据类型推断机制。让我们将 XML 数据保存到名为sample.xml
:
<data>
<entry>
<value>1</value>
</entry>
<entry>
<value>1.5</value>
</entry>
</data>
阅读该文件,您可能会观察到:
import pandas as pd
df = pd.read_xml("sample.xml")
print(df.dtypes)
Output:
value float64
dtype: object
熊猫推断出value
列作为float64
。如果这不是所需的类型怎么办?
使用dtype_backend
范围
The dtype_backend
参数允许您选择一个后端("numpy_nullable"
or "pyarrow"
) 对于数据类型推断:
df_python = pd.read_xml("data.xml", dtype_backend="numpy_nullable")
df_lxml = pd.read_xml("data.xml", dtype_backend="pyarrow")
print("NumPy Nullable Backend:", df_python.dtypes)
print("pyarrow Backend:", df_lxml.dtypes)
Output:
NumPy Nullable Backend: value Float64
dtype: object
pyarrow Backend: value double[pyarrow]
dtype: object
随着pyarrow
后端,value
列被推断为double[pyarrow]
,这在某些场景下更相关。
使用 XSLT 样式表转换 XML
可扩展样式表语言转换 (XSLT) 是用于转换 XML 文档的强大工具。
The stylesheet
中的参数read_xml
函数允许您通过应用 XSLT 样式表来实现此目的。
假设您有一个 XML 文件,employees.xml
:
<employees>
<employee>
<name>John</name>
<role>Developer</role>
</employee>
<employee>
<name>Mary</name>
<role>Designer</role>
</employee>
</employees>
创建 XSLT 样式表
如果您想转换 XML 以仅保留员工姓名。此转换的 XSLT 可能如下所示:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="role"/>
</xsl:stylesheet>
将此样式表另存为transform.xslt
.
使用以下方法应用 XSLT 样式表stylesheet
要在读取 XML 数据时使用转换:
df = pd.read_xml("employees.xml", stylesheet="transform.xslt")
print(df)
Output:
name
0 John
1 Mary
如图所示,只有员工姓名保留在生成的 DataFrame 中。
流解析:加载大型 XML 文件
处理大型 XML 文件可能会占用大量资源,尤其是在将整个文档加载到内存中时。
流解析会在遇到 XML 元素时对其进行处理,而不会将整个文件加载到内存中。
Pandas 通过以下方式提供此功能iterparse
中的参数read_xml
功能。
Using iterparse
假设您有一个名为bigdata.xml
:
<data>
<polygon>
<shape>square</shape>
<degrees>360</degrees>
<sides>4.0</sides>
</polygon>
<polygon>
<shape>circle</shape>
<degrees>360</degrees>
</polygon>
<polygon>
<shape>triangle</shape>
<degrees>180</degrees>
<sides>3.0</sides>
</polygon>
</data>
不要一次读取整个文件,而是使用iterparse
逐步处理它:
df = pd.read_xml('sample.xml', iterparse={"polygon": ["shape", "degrees", "sides"]})
print(df.head())
Output:
shape degrees sides
0 square 360 4.0
1 circle 360 NaN
2 triangle 180 3.0
The interparse
参数用于指定 XML 的结构以及如何解析它。
基准解析
比较使用之间的性能iterparse
并且不使用它来读取大型 XML 文件。计划如下:
- 生成一个大的 XML 文件。
- 测量在不使用的情况下读取 XML 文件所花费的时间
iterparse
.
- 使用以下命令测量读取 XML 文件所需的时间
iterparse
.
import pandas as pd
import random
import time
from io import BytesIO
# Step 1: Generate a large XML file
num_entries = 1000000
shapes = ["triangle", "square", "pentagon", "hexagon"]
xml_data = ''
for _ in range(num_entries):
shape = random.choice(shapes)
xml_data += f'{shape}{random.randint(100, 400)}{random.randint(3, 6)}'
xml_data += ''
with open("large_sample.xml", "w") as f:
f.write(xml_data)
# Step 2: Measure time without iterparse
start_time = time.time()
df1 = pd.read_xml('large_sample.xml')
end_time = time.time()
without_iterparse_time = end_time - start_time
print(f"Time without iterparse: {without_iterparse_time:.4f} seconds")
# Step 3: Measure time with iterparse
start_time = time.time()
df2 = pd.read_xml('large_sample.xml', iterparse={"polygon": ["shape", "degrees", "sides"]})
end_time = time.time()
with_iterparse_time = end_time - start_time
print(f"Time with iterparse: {with_iterparse_time:.4f} seconds")
Output:
Time without iterparse: 42.4067 seconds
Time with iterparse: 35.7642 seconds
如你看到的,interparse
更快地解析大型 XML 文件,时间差仅为 100 万个 XML 条目。
Resource
https://pandas.pydata.org/docs/reference/api/pandas.read_xml.html