Pandas 2.2 中文官方教程和指南(十·一)(2)https://developer.aliyun.com/article/1509772
HTML
读取 HTML 内容
警告
我们强烈建议您阅读下面关于 BeautifulSoup4/html5lib/lxml 解析器的 HTML 表格解析陷阱。
顶级的 read_html()
函数可以接受一个 HTML 字符串/文件/URL,并将 HTML 表格解析为 pandas DataFrame
的列表。让我们看一些例子。
注意
read_html
返回一个 DataFrame
对象的 list
,即使在 HTML 内容中只包含一个表格。
无选项读取 URL:
In [320]: url = "https://www.fdic.gov/resources/resolutions/bank-failures/failed-bank-list" In [321]: pd.read_html(url) Out[321]: [ Bank NameBank CityCity StateSt ... Acquiring InstitutionAI Closing DateClosing FundFund 0 Almena State Bank Almena KS ... Equity Bank October 23, 2020 10538 1 First City Bank of Florida Fort Walton Beach FL ... United Fidelity Bank, fsb October 16, 2020 10537 2 The First State Bank Barboursville WV ... MVB Bank, Inc. April 3, 2020 10536 3 Ericson State Bank Ericson NE ... Farmers and Merchants Bank February 14, 2020 10535 4 City National Bank of New Jersey Newark NJ ... Industrial Bank November 1, 2019 10534 .. ... ... ... ... ... ... ... 558 Superior Bank, FSB Hinsdale IL ... Superior Federal, FSB July 27, 2001 6004 559 Malta National Bank Malta OH ... North Valley Bank May 3, 2001 4648 560 First Alliance Bank & Trust Co. Manchester NH ... Southern New Hampshire Bank & Trust February 2, 2001 4647 561 National State Bank of Metropolis Metropolis IL ... Banterra Bank of Marion December 14, 2000 4646 562 Bank of Honolulu Honolulu HI ... Bank of the Orient October 13, 2000 4645 [563 rows x 7 columns]]
注意
上述 URL 的数据每个星期一都会更改,因此上面生成的数据可能会略有不同。
在 HTTP 请求中传递标题时读取 URL:
In [322]: url = 'https://www.sump.org/notes/request/' # HTTP request reflector In [323]: pd.read_html(url) Out[323]: [ 0 1 0 Remote Socket: 51.15.105.256:51760 1 Protocol Version: HTTP/1.1 2 Request Method: GET 3 Request URI: /notes/request/ 4 Request Query: NaN, 0 Accept-Encoding: identity 1 Host: www.sump.org 2 User-Agent: Python-urllib/3.8 3 Connection: close] In [324]: headers = { In [325]: 'User-Agent':'Mozilla Firefox v14.0', In [326]: 'Accept':'application/json', In [327]: 'Connection':'keep-alive', In [328]: 'Auth':'Bearer 2*/f3+fe68df*4' In [329]: } In [340]: pd.read_html(url, storage_options=headers) Out[340]: [ 0 1 0 Remote Socket: 51.15.105.256:51760 1 Protocol Version: HTTP/1.1 2 Request Method: GET 3 Request URI: /notes/request/ 4 Request Query: NaN, 0 User-Agent: Mozilla Firefox v14.0 1 AcceptEncoding: gzip, deflate, br 2 Accept: application/json 3 Connection: keep-alive 4 Auth: Bearer 2*/f3+fe68df*4]
注意
我们可以看到上面我们传递的标题反映在 HTTP 请求中。
从上述 URL 中读取文件内容,并将其作为字符串传递给 read_html
:
In [331]: html_str = """ .....: <table> .....: <tr> .....: <th>A</th> .....: <th colspan="1">B</th> .....: <th rowspan="1">C</th> .....: </tr> .....: <tr> .....: <td>a</td> .....: <td>b</td> .....: <td>c</td> .....: </tr> .....: </table> .....: """ .....: In [332]: with open("tmp.html", "w") as f: .....: f.write(html_str) .....: In [333]: df = pd.read_html("tmp.html") In [334]: df[0] Out[334]: A B C 0 a b c
如果您愿意,甚至可以传递一个 StringIO
的实例:
In [335]: dfs = pd.read_html(StringIO(html_str)) In [336]: dfs[0] Out[336]: A B C 0 a b c
注意
由于具有如此多的网络访问功能会减慢文档构建速度,因此 IPython 评估器未运行以下示例。如果您发现错误或无法运行的示例,请毫不犹豫地在 pandas GitHub 问题页面 上报告。
读取一个包含特定文本的表格的 URL:
match = "Metcalf Bank" df_list = pd.read_html(url, match=match)
指定一个标题行(默认情况下, 中的 或 元素用于形成列索引,如果 中包含多行,则会创建一个 MultiIndex);如果指定了,则标题行取自数据减去已解析的标题元素( 元素)。
dfs = pd.read_html(url, header=0)
指定一个索引列:
dfs = pd.read_html(url, index_col=0)
指定要跳过的行数:
dfs = pd.read_html(url, skiprows=0)
使用列表指定要跳过的行数(range
也适用):
dfs = pd.read_html(url, skiprows=range(2))
指定 HTML 属性:
dfs1 = pd.read_html(url, attrs={"id": "table"}) dfs2 = pd.read_html(url, attrs={"class": "sortable"}) print(np.array_equal(dfs1[0], dfs2[0])) # Should be True
指定应转换为 NaN 的值:
dfs = pd.read_html(url, na_values=["No Acquirer"])
指定是否保留默认的 NaN 值集合:
dfs = pd.read_html(url, keep_default_na=False)
为列指定转换器。这对于具有前导零的数值文本数据非常有用。默认情况下,数值列会转换为数值类型,前导零会丢失。为了避免这种情况,我们可以将这些列转换为字符串。
url_mcc = "https://en.wikipedia.org/wiki/Mobile_country_code?oldid=899173761" dfs = pd.read_html( url_mcc, match="Telekom Albania", header=0, converters={"MNC": str}, )
使用上述某种组合:
dfs = pd.read_html(url, match="Metcalf Bank", index_col=0)
读取 pandas to_html
输出(会损失浮点数精度):
df = pd.DataFrame(np.random.randn(2, 2)) s = df.to_html(float_format="{0:.40g}".format) dfin = pd.read_html(s, index_col=0)
如果 lxml
后端在提供唯一解析器的情况下解析失败,则会引发错误。如果您只有一个解析器,可以只提供一个字符串,但是,如果函数期望一个字符串序列,那么传递一个包含一个字符串的列表被认为是一种良好的做法。您可以使用:
dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor=["lxml"])
或者您可以不带列表传递 flavor='lxml'
:
dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor="lxml")
但是,如果你已经安装了 bs4 和 html5lib,并且传递了 None
或 ['lxml', 'bs4']
,那么解析很可能会成功。请注意,一旦解析成功,函数将立即返回。
dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor=["lxml", "bs4"])
可以使用 extract_links="all"
从单元格中提取链接和文本。
In [337]: html_table = """ .....: <table> .....: <tr> .....: <th>GitHub</th> .....: </tr> .....: <tr> .....: <td><a href="https://github.com/pandas-dev/pandas">pandas</a></td> .....: </tr> .....: </table> .....: """ .....: In [338]: df = pd.read_html( .....: StringIO(html_table), .....: extract_links="all" .....: )[0] .....: In [339]: df Out[339]: (GitHub, None) 0 (pandas, https://github.com/pandas-dev/pandas) In [340]: df[("GitHub", None)] Out[340]: 0 (pandas, https://github.com/pandas-dev/pandas) Name: (GitHub, None), dtype: object In [341]: df[("GitHub", None)].str[1] Out[341]: 0 https://github.com/pandas-dev/pandas Name: (GitHub, None), dtype: object
版本 1.5.0 中的新功能。 ### 写入 HTML 文件
DataFrame
对象具有一个实例方法 to_html
,它将 DataFrame
的内容呈现为 HTML 表格。函数参数与上面描述的 to_string
方法相同。
注意
由于篇幅限制,这里没有显示 DataFrame.to_html
的所有可能选项。有关完整选项集,请参阅 DataFrame.to_html()
。
注意
在支持 HTML 渲染的环境(如 Jupyter Notebook)中,`display(HTML(…))`` 将把原始 HTML 渲染到环境中。
In [342]: from IPython.display import display, HTML In [343]: df = pd.DataFrame(np.random.randn(2, 2)) In [344]: df Out[344]: 0 1 0 -0.345352 1.314232 1 0.690579 0.995761 In [345]: html = df.to_html() In [346]: print(html) # raw html <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>0</th> <th>1</th> </tr> </thead> <tbody> <tr> <th>0</th> <td>-0.345352</td> <td>1.314232</td> </tr> <tr> <th>1</th> <td>0.690579</td> <td>0.995761</td> </tr> </tbody> </table> In [347]: display(HTML(html)) <IPython.core.display.HTML object>
columns
参数将限制显示的列:
In [348]: html = df.to_html(columns=[0]) In [349]: print(html) <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>0</th> </tr> </thead> <tbody> <tr> <th>0</th> <td>-0.345352</td> </tr> <tr> <th>1</th> <td>0.690579</td> </tr> </tbody> </table> In [350]: display(HTML(html)) <IPython.core.display.HTML object>
float_format
接受一个 Python 可调用对象来控制浮点数值的精度:
In [351]: html = df.to_html(float_format="{0:.10f}".format) In [352]: print(html) <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>0</th> <th>1</th> </tr> </thead> <tbody> <tr> <th>0</th> <td>-0.3453521949</td> <td>1.3142323796</td> </tr> <tr> <th>1</th> <td>0.6905793352</td> <td>0.9957609037</td> </tr> </tbody> </table> In [353]: display(HTML(html)) <IPython.core.display.HTML object>
bold_rows
默认会使行标签加粗,但你可以关闭这个选项:
In [354]: html = df.to_html(bold_rows=False) In [355]: print(html) <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>0</th> <th>1</th> </tr> </thead> <tbody> <tr> <td>0</td> <td>-0.345352</td> <td>1.314232</td> </tr> <tr> <td>1</td> <td>0.690579</td> <td>0.995761</td> </tr> </tbody> </table> In [356]: display(HTML(html)) <IPython.core.display.HTML object>
classes
参数提供了为生成的 HTML 表格添加 CSS 类的功能。请注意,这些类会 追加 到现有的 'dataframe'
类中。
In [357]: print(df.to_html(classes=["awesome_table_class", "even_more_awesome_class"])) <table border="1" class="dataframe awesome_table_class even_more_awesome_class"> <thead> <tr style="text-align: right;"> <th></th> <th>0</th> <th>1</th> </tr> </thead> <tbody> <tr> <th>0</th> <td>-0.345352</td> <td>1.314232</td> </tr> <tr> <th>1</th> <td>0.690579</td> <td>0.995761</td> </tr> </tbody> </table>
render_links
参数提供了向包含 URL 的单元格添加超链接的功能。
In [358]: url_df = pd.DataFrame( .....: { .....: "name": ["Python", "pandas"], .....: "url": ["https://www.python.org/", "https://pandas.pydata.org"], .....: } .....: ) .....: In [359]: html = url_df.to_html(render_links=True) In [360]: print(html) <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>name</th> <th>url</th> </tr> </thead> <tbody> <tr> <th>0</th> <td>Python</td> <td><a href="https://www.python.org/" target="_blank">https://www.python.org/</a></td> </tr> <tr> <th>1</th> <td>pandas</td> <td><a href="https://pandas.pydata.org" target="_blank">https://pandas.pydata.org</a></td> </tr> </tbody> </table> In [361]: display(HTML(html)) <IPython.core.display.HTML object>
最后,escape
参数允许您控制结果 HTML 中是否转义 “<”,“>” 和 “&” 字符(默认情况下为 True
)。因此,要获得不带转义字符的 HTML,请传递 escape=False
。
In [362]: df = pd.DataFrame({"a": list("&<>"), "b": np.random.randn(3)})
已转义:
In [363]: html = df.to_html() In [364]: print(html) <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>a</th> <th>b</th> </tr> </thead> <tbody> <tr> <th>0</th> <td>&</td> <td>2.396780</td> </tr> <tr> <th>1</th> <td><</td> <td>0.014871</td> </tr> <tr> <th>2</th> <td>></td> <td>3.357427</td> </tr> </tbody> </table> In [365]: display(HTML(html)) <IPython.core.display.HTML object>
未转义:
In [366]: html = df.to_html(escape=False) In [367]: print(html) <table border="1" class="dataframe"> <thead> <tr style="text-align: right;"> <th></th> <th>a</th> <th>b</th> </tr> </thead> <tbody> <tr> <th>0</th> <td>&</td> <td>2.396780</td> </tr> <tr> <th>1</th> <td><</td> <td>0.014871</td> </tr> <tr> <th>2</th> <td>></td> <td>3.357427</td> </tr> </tbody> </table> In [368]: display(HTML(html)) <IPython.core.display.HTML object>
注意
一些浏览器可能无法显示前两个 HTML 表格的渲染差异。 ### HTML 表格解析的陷阱
在解析顶级 pandas io 函数 read_html
中用于解析 HTML 表格的库的版本存在一些问题。
与 lxml 有关的问题
- 优点
- 缺点
使用BeautifulSoup4 使用lxml 作为后端的问题
- 由于BeautifulSoup4本质上只是一个围绕解析器后端的包装器,因此上述问题在这里同样存在。
使用BeautifulSoup4 使用html5lib 作为后端的问题
- 优点
- 缺点
- 使用html5lib的最大缺点是速度极慢。但是请考虑到许多网页上的表格都不足以使解析算法运行时间成为问题。更可能的是瓶颈将出现在通过网络从 URL 读取原始文本的过程中,即 IO(输入输出)。对于非常大的表格,这可能不成立。## LaTeX
在版本 1.3.0 中新增。
目前没有从 LaTeX 读取的方法,只有输出方法。
编写到 LaTeX 文件
注意
DataFrame 和 Styler 对象目前具有to_latex
方法。我们建议使用 Styler.to_latex()方法,因为它在条件样式方面更灵活,而后者可能会在将来被弃用。
请查阅 Styler.to_latex 的文档,其中提供了条件样式的示例并解释了其关键字参数的操作。
对于简单的应用程序,以下模式已足够。
In [369]: df = pd.DataFrame([[1, 2], [3, 4]], index=["a", "b"], columns=["c", "d"]) In [370]: print(df.style.to_latex()) \begin{tabular}{lrr} & c & d \\ a & 1 & 2 \\ b & 3 & 4 \\ \end{tabular}
要在输出之前格式化值,请链式调用 Styler.format 方法。
In [371]: print(df.style.format("€ {}").to_latex()) \begin{tabular}{lrr} & c & d \\ a & € 1 & € 2 \\ b & € 3 & € 4 \\ \end{tabular}
XML
读取 XML
版本 1.3.0 中的新功能。
顶级的 read_xml()
函数可以接受 XML 字符串/文件/URL,并将节点和属性解析到 pandas 的 DataFrame
中。
注意
由于没有标准的 XML 结构,设计类型可以以多种方式变化,read_xml
最适用于较平坦、较浅的版本。如果 XML 文档嵌套层级较深,则使用 stylesheet
功能将 XML 转换为较平坦的版本。
让我们看几个例子。
读取 XML 字符串:
In [372]: from io import StringIO In [373]: xml = """<?xml version="1.0" encoding="UTF-8"?> .....: <bookstore> .....: <book category="cooking"> .....: <title lang="en">Everyday Italian</title> .....: <author>Giada De Laurentiis</author> .....: <year>2005</year> .....: <price>30.00</price> .....: </book> .....: <book category="children"> .....: <title lang="en">Harry Potter</title> .....: <author>J K. Rowling</author> .....: <year>2005</year> .....: <price>29.99</price> .....: </book> .....: <book category="web"> .....: <title lang="en">Learning XML</title> .....: <author>Erik T. Ray</author> .....: <year>2003</year> .....: <price>39.95</price> .....: </book> .....: </bookstore>""" .....: In [374]: df = pd.read_xml(StringIO(xml)) In [375]: df Out[375]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 2 web Learning XML Erik T. Ray 2003 39.95
读取无选项的 URL:
In [376]: df = pd.read_xml("https://www.w3schools.com/xml/books.xml") In [377]: df Out[377]: category title author year price cover 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 None 1 children Harry Potter J K. Rowling 2005 29.99 None 2 web XQuery Kick Start Vaidyanathan Nagarajan 2003 49.99 None 3 web Learning XML Erik T. Ray 2003 39.95 paperback
读取 “books.xml” 文件的内容并将其作为字符串传递给 read_xml
:
In [378]: file_path = "books.xml" In [379]: with open(file_path, "w") as f: .....: f.write(xml) .....: In [380]: with open(file_path, "r") as f: .....: df = pd.read_xml(StringIO(f.read())) .....: In [381]: df Out[381]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 2 web Learning XML Erik T. Ray 2003 39.95
将 “books.xml” 的内容读取为 StringIO
或 BytesIO
实例,并将其传递给 read_xml
:
In [382]: with open(file_path, "r") as f: .....: sio = StringIO(f.read()) .....: In [383]: df = pd.read_xml(sio) In [384]: df Out[384]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 2 web Learning XML Erik T. Ray 2003 39.95
In [385]: with open(file_path, "rb") as f: .....: bio = BytesIO(f.read()) .....: In [386]: df = pd.read_xml(bio) In [387]: df Out[387]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 2 web Learning XML Erik T. Ray 2003 39.95
甚至可以从 AWS S3 存储桶读取 XML,例如 NIH NCBI PMC Article Datasets,提供生物医学和生命科学期刊:
In [388]: df = pd.read_xml( .....: "s3://pmc-oa-opendata/oa_comm/xml/all/PMC1236943.xml", .....: xpath=".//journal-meta", .....: ) .....: In [389]: df Out[389]: journal-id journal-title issn publisher 0 Cardiovasc Ultrasound Cardiovascular Ultrasound 1476-7120 NaN
使用 lxml 作为默认的 parser
,您可以访问扩展了 Python 的 ElementTree API 的功能齐全的 XML 库。其中一个强大的工具是能够使用更具表达力的 XPath 有选择地或有条件地查询节点:
In [390]: df = pd.read_xml(file_path, xpath="//book[year=2005]") In [391]: df Out[391]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99
仅指定要解析的元素或属性:
In [392]: df = pd.read_xml(file_path, elems_only=True) In [393]: df Out[393]: title author year price 0 Everyday Italian Giada De Laurentiis 2005 30.00 1 Harry Potter J K. Rowling 2005 29.99 2 Learning XML Erik T. Ray 2003 39.95
In [394]: df = pd.read_xml(file_path, attrs_only=True) In [395]: df Out[395]: category 0 cooking 1 children 2 web
XML 文档可以具有带有前缀的命名空间和不带前缀的默认命名空间,两者都用特殊属性 xmlns
表示。为了在命名空间上下文中按节点解析,xpath
必须引用一个前缀。
例如,下面的 XML 包含一个带有前缀 doc
和 URI 为 https://example.com
的命名空间。为了解析 doc:row
节点,必须使用 namespaces
。
In [396]: xml = """<?xml version='1.0' encoding='utf-8'?> .....: <doc:data > .....: <doc:row> .....: <doc:shape>square</doc:shape> .....: <doc:degrees>360</doc:degrees> .....: <doc:sides>4.0</doc:sides> .....: </doc:row> .....: <doc:row> .....: <doc:shape>circle</doc:shape> .....: <doc:degrees>360</doc:degrees> .....: <doc:sides/> .....: </doc:row> .....: <doc:row> .....: <doc:shape>triangle</doc:shape> .....: <doc:degrees>180</doc:degrees> .....: <doc:sides>3.0</doc:sides> .....: </doc:row> .....: </doc:data>""" .....: In [397]: df = pd.read_xml(StringIO(xml), .....: xpath="//doc:row", .....: namespaces={"doc": "https://example.com"}) .....: In [398]: df Out[398]: shape degrees sides 0 square 360 4.0 1 circle 360 NaN 2 triangle 180 3.0
类似地,XML 文档可以具有没有前缀的默认命名空间。未分配临时前缀将返回零个节点并引发 ValueError
。但是,分配 任何 临时名称以更正 URI 允许按节点解析。
In [399]: xml = """<?xml version='1.0' encoding='utf-8'?> .....: <data > .....: <row> .....: <shape>square</shape> .....: <degrees>360</degrees> .....: <sides>4.0</sides> .....: </row> .....: <row> .....: <shape>circle</shape> .....: <degrees>360</degrees> .....: <sides/> .....: </row> .....: <row> .....: <shape>triangle</shape> .....: <degrees>180</degrees> .....: <sides>3.0</sides> .....: </row> .....: </data>""" .....: In [400]: df = pd.read_xml(StringIO(xml), .....: xpath="//pandas:row", .....: namespaces={"pandas": "https://example.com"}) .....: In [401]: df Out[401]: shape degrees sides 0 square 360 4.0 1 circle 360 NaN 2 triangle 180 3.0
但是,如果 XPath 不引用默认的节点名称,例如 /*
,则不需要 namespaces
。
注意
由于 xpath
标识要解析的内容的父级,因此仅解析包含子节点或当前属性的直接后代。因此,read_xml
将不会解析孙子节点或其他后代的文本,并且不会解析任何后代的属性。要检索更低级别的内容,请将 xpath 调整为更低级别。例如,
In [402]: xml = """ .....: <data> .....: <row> .....: <shape sides="4">square</shape> .....: <degrees>360</degrees> .....: </row> .....: <row> .....: <shape sides="0">circle</shape> .....: <degrees>360</degrees> .....: </row> .....: <row> .....: <shape sides="3">triangle</shape> .....: <degrees>180</degrees> .....: </row> .....: </data>""" .....: In [403]: df = pd.read_xml(StringIO(xml), xpath="./row") In [404]: df Out[404]: shape degrees 0 square 360 1 circle 360 2 triangle 180
显示在 shape
元素上的属性 sides
未按预期解析,因为此属性位于 row
元素的子节点而不是 row
元素本身。换句话说,sides
属性是 row
元素的孙级后代。但是,xpath
目标是 row
元素,仅涵盖其子节点和属性。
使用 lxml 作为解析器,您可以使用 XSLT 脚本展平嵌套的 XML 文档,该脚本也可以是字符串/文件/URL 类型。作为背景,XSLT 是一种特殊用途的语言,写在一个特殊的 XML 文件中,可以使用 XSLT 处理器将原始 XML 文档转换为其他 XML、HTML,甚至文本(CSV、JSON 等)。
例如,考虑芝加哥“L”列车的稍微嵌套的结构,其中 station
和 rides
元素将数据封装在各自的部分中。使用下面的 XSLT,lxml
可以将原始的嵌套文档转换为更扁平的输出(如下所示,仅用于演示),以便更容易解析为 DataFrame
:
In [405]: xml = """<?xml version='1.0' encoding='utf-8'?> .....: <response> .....: <row> .....: <station id="40850" name="Library"/> .....: <month>2020-09-01T00:00:00</month> .....: <rides> .....: <avg_weekday_rides>864.2</avg_weekday_rides> .....: <avg_saturday_rides>534</avg_saturday_rides> .....: <avg_sunday_holiday_rides>417.2</avg_sunday_holiday_rides> .....: </rides> .....: </row> .....: <row> .....: <station id="41700" name="Washington/Wabash"/> .....: <month>2020-09-01T00:00:00</month> .....: <rides> .....: <avg_weekday_rides>2707.4</avg_weekday_rides> .....: <avg_saturday_rides>1909.8</avg_saturday_rides> .....: <avg_sunday_holiday_rides>1438.6</avg_sunday_holiday_rides> .....: </rides> .....: </row> .....: <row> .....: <station id="40380" name="Clark/Lake"/> .....: <month>2020-09-01T00:00:00</month> .....: <rides> .....: <avg_weekday_rides>2949.6</avg_weekday_rides> .....: <avg_saturday_rides>1657</avg_saturday_rides> .....: <avg_sunday_holiday_rides>1453.8</avg_sunday_holiday_rides> .....: </rides> .....: </row> .....: </response>""" .....: In [406]: xsl = """<xsl:stylesheet version="1.0" > .....: <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/> .....: <xsl:strip-space elements="*"/> .....: <xsl:template match="/response"> .....: <xsl:copy> .....: <xsl:apply-templates select="row"/> .....: </xsl:copy> .....: </xsl:template> .....: <xsl:template match="row"> .....: <xsl:copy> .....: <station_id><xsl:value-of select="station/@id"/></station_id> .....: <station_name><xsl:value-of select="station/@name"/></station_name> .....: <xsl:copy-of select="month|rides/*"/> .....: </xsl:copy> .....: </xsl:template> .....: </xsl:stylesheet>""" .....: In [407]: output = """<?xml version='1.0' encoding='utf-8'?> .....: <response> .....: <row> .....: <station_id>40850</station_id> .....: <station_name>Library</station_name> .....: <month>2020-09-01T00:00:00</month> .....: <avg_weekday_rides>864.2</avg_weekday_rides> .....: <avg_saturday_rides>534</avg_saturday_rides> .....: <avg_sunday_holiday_rides>417.2</avg_sunday_holiday_rides> .....: </row> .....: <row> .....: <station_id>41700</station_id> .....: <station_name>Washington/Wabash</station_name> .....: <month>2020-09-01T00:00:00</month> .....: <avg_weekday_rides>2707.4</avg_weekday_rides> .....: <avg_saturday_rides>1909.8</avg_saturday_rides> .....: <avg_sunday_holiday_rides>1438.6</avg_sunday_holiday_rides> .....: </row> .....: <row> .....: <station_id>40380</station_id> .....: <station_name>Clark/Lake</station_name> .....: <month>2020-09-01T00:00:00</month> .....: <avg_weekday_rides>2949.6</avg_weekday_rides> .....: <avg_saturday_rides>1657</avg_saturday_rides> .....: <avg_sunday_holiday_rides>1453.8</avg_sunday_holiday_rides> .....: </row> .....: </response>""" .....: In [408]: df = pd.read_xml(StringIO(xml), stylesheet=xsl) In [409]: df Out[409]: station_id station_name ... avg_saturday_rides avg_sunday_holiday_rides 0 40850 Library ... 534.0 417.2 1 41700 Washington/Wabash ... 1909.8 1438.6 2 40380 Clark/Lake ... 1657.0 1453.8 [3 rows x 6 columns]
对于非常大的 XML 文件,其大小可能在几百兆字节到几十个字节之间,pandas.read_xml()
支持使用 lxml 的 iterparse 和 etree 的 iterparse 解析这些庞大文件,并且这些方法是内存高效的方法,可以遍历 XML 树并提取特定的元素和属性,而无需将整个树保留在内存中。
新功能,版本 1.5.0。
要使用此功能,必须将物理 XML 文件路径传递给 read_xml
并使用 iterparse
参数。文件不应该被压缩或指向在线源,而应存储在本地磁盘上。此外,iterparse
应该是一个字典,其中键是文档中的重复节点(它们成为行),值是任何重复节点的后代(即,子节点、孙子节点)的元素或属性的列表。由于此方法不使用 XPath,因此后代不需要彼此共享相同的关系。下面显示了读取维基百科非常大(12 GB+)的最新文章数据转储的示例。
In [1]: df = pd.read_xml( ... "/path/to/downloaded/enwikisource-latest-pages-articles.xml", ... iterparse = {"page": ["title", "ns", "id"]} ... ) ... df Out[2]: title ns id 0 Gettysburg Address 0 21450 1 Main Page 0 42950 2 Declaration by United Nations 0 8435 3 Constitution of the United States of America 0 8435 4 Declaration of Independence (Israel) 0 17858 ... ... ... ... 3578760 Page:Black cat 1897 07 v2 n10.pdf/17 104 219649 3578761 Page:Black cat 1897 07 v2 n10.pdf/43 104 219649 3578762 Page:Black cat 1897 07 v2 n10.pdf/44 104 219649 3578763 The History of Tom Jones, a Foundling/Book IX 0 12084291 3578764 Page:Shakespeare of Stratford (1926) Yale.djvu/91 104 21450 [3578765 rows x 3 columns] ```### 编写 XML 新功能,版本 1.3.0。 `DataFrame` 对象具有一个名为 `to_xml` 的实例方法,它将 `DataFrame` 的内容呈现为 XML 文档。 注意 此方法不支持 XML 的特殊属性,包括 DTD、CData、XSD 模式、处理指令、注释等。只支持根级别的命名空间。但是,`stylesheet` 允许在初始输出之后进行设计更改。 让我们看几个示例。 编写没有选项的 XML: ```py In [410]: geom_df = pd.DataFrame( .....: { .....: "shape": ["square", "circle", "triangle"], .....: "degrees": [360, 360, 180], .....: "sides": [4, np.nan, 3], .....: } .....: ) .....: In [411]: print(geom_df.to_xml()) <?xml version='1.0' encoding='utf-8'?> <data> <row> <index>0</index> <shape>square</shape> <degrees>360</degrees> <sides>4.0</sides> </row> <row> <index>1</index> <shape>circle</shape> <degrees>360</degrees> <sides/> </row> <row> <index>2</index> <shape>triangle</shape> <degrees>180</degrees> <sides>3.0</sides> </row> </data>
编写具有新根和行名称的 XML:
In [412]: print(geom_df.to_xml(root_name="geometry", row_name="objects")) <?xml version='1.0' encoding='utf-8'?> <geometry> <objects> <index>0</index> <shape>square</shape> <degrees>360</degrees> <sides>4.0</sides> </objects> <objects> <index>1</index> <shape>circle</shape> <degrees>360</degrees> <sides/> </objects> <objects> <index>2</index> <shape>triangle</shape> <degrees>180</degrees> <sides>3.0</sides> </objects> </geometry>
编写基于属性的 XML:
In [413]: print(geom_df.to_xml(attr_cols=geom_df.columns.tolist())) <?xml version='1.0' encoding='utf-8'?> <data> <row index="0" shape="square" degrees="360" sides="4.0"/> <row index="1" shape="circle" degrees="360"/> <row index="2" shape="triangle" degrees="180" sides="3.0"/> </data>
编写混合元素和属性:
In [414]: print( .....: geom_df.to_xml( .....: index=False, .....: attr_cols=['shape'], .....: elem_cols=['degrees', 'sides']) .....: ) .....: <?xml version='1.0' encoding='utf-8'?> <data> <row shape="square"> <degrees>360</degrees> <sides>4.0</sides> </row> <row shape="circle"> <degrees>360</degrees> <sides/> </row> <row shape="triangle"> <degrees>180</degrees> <sides>3.0</sides> </row> </data>
任何具有分层列的 DataFrame
将被展平为以下划线分隔的 XML 元素名称:
In [415]: ext_geom_df = pd.DataFrame( .....: { .....: "type": ["polygon", "other", "polygon"], .....: "shape": ["square", "circle", "triangle"], .....: "degrees": [360, 360, 180], .....: "sides": [4, np.nan, 3], .....: } .....: ) .....: In [416]: pvt_df = ext_geom_df.pivot_table(index='shape', .....: columns='type', .....: values=['degrees', 'sides'], .....: aggfunc='sum') .....: In [417]: pvt_df Out[417]: degrees sides type other polygon other polygon shape circle 360.0 NaN 0.0 NaN square NaN 360.0 NaN 4.0 triangle NaN 180.0 NaN 3.0 In [418]: print(pvt_df.to_xml()) <?xml version='1.0' encoding='utf-8'?> <data> <row> <shape>circle</shape> <degrees_other>360.0</degrees_other> <degrees_polygon/> <sides_other>0.0</sides_other> <sides_polygon/> </row> <row> <shape>square</shape> <degrees_other/> <degrees_polygon>360.0</degrees_polygon> <sides_other/> <sides_polygon>4.0</sides_polygon> </row> <row> <shape>triangle</shape> <degrees_other/> <degrees_polygon>180.0</degrees_polygon> <sides_other/> <sides_polygon>3.0</sides_polygon> </row> </data>
使用默认命名空间编写 XML:
In [419]: print(geom_df.to_xml(namespaces={"": "https://example.com"})) <?xml version='1.0' encoding='utf-8'?> <data > <row> <index>0</index> <shape>square</shape> <degrees>360</degrees> <sides>4.0</sides> </row> <row> <index>1</index> <shape>circle</shape> <degrees>360</degrees> <sides/> </row> <row> <index>2</index> <shape>triangle</shape> <degrees>180</degrees> <sides>3.0</sides> </row> </data>
使用命名空间前缀编写 XML:
In [420]: print( .....: geom_df.to_xml(namespaces={"doc": "https://example.com"}, .....: prefix="doc") .....: ) .....: <?xml version='1.0' encoding='utf-8'?> <doc:data > <doc:row> <doc:index>0</doc:index> <doc:shape>square</doc:shape> <doc:degrees>360</doc:degrees> <doc:sides>4.0</doc:sides> </doc:row> <doc:row> <doc:index>1</doc:index> <doc:shape>circle</doc:shape> <doc:degrees>360</doc:degrees> <doc:sides/> </doc:row> <doc:row> <doc:index>2</doc:index> <doc:shape>triangle</doc:shape> <doc:degrees>180</doc:degrees> <doc:sides>3.0</doc:sides> </doc:row> </doc:data>
编写没有声明或美化打印的 XML:
In [421]: print( .....: geom_df.to_xml(xml_declaration=False, .....: pretty_print=False) .....: ) .....: <data><row><index>0</index><shape>square</shape><degrees>360</degrees><sides>4.0</sides></row><row><index>1</index><shape>circle</shape><degrees>360</degrees><sides/></row><row><index>2</index><shape>triangle</shape><degrees>180</degrees><sides>3.0</sides></row></data>
编写 XML 并使用样式表进行转换:
In [422]: xsl = """<xsl:stylesheet version="1.0" > .....: <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/> .....: <xsl:strip-space elements="*"/> .....: <xsl:template match="/data"> .....: <geometry> .....: <xsl:apply-templates select="row"/> .....: </geometry> .....: </xsl:template> .....: <xsl:template match="row"> .....: <object index="{index}"> .....: <xsl:if test="shape!='circle'"> .....: <xsl:attribute name="type">polygon</xsl:attribute> .....: </xsl:if> .....: <xsl:copy-of select="shape"/> .....: <property> .....: <xsl:copy-of select="degrees|sides"/> .....: </property> .....: </object> .....: </xsl:template> .....: </xsl:stylesheet>""" .....: In [423]: print(geom_df.to_xml(stylesheet=xsl)) <?xml version="1.0"?> <geometry> <object index="0" type="polygon"> <shape>square</shape> <property> <degrees>360</degrees> <sides>4.0</sides> </property> </object> <object index="1"> <shape>circle</shape> <property> <degrees>360</degrees> <sides/> </property> </object> <object index="2" type="polygon"> <shape>triangle</shape> <property> <degrees>180</degrees> <sides>3.0</sides> </property> </object> </geometry>
XML 最终注意事项
- 所有 XML 文档遵循W3C 规范。
etree
和lxml
解析器将无法解析任何不符合规范或遵循 XML 语法规则的标记文档。请注意,除非遵循 XHTML 规范,否则 HTML 不是 XML 文档。然而,其他流行的标记类型,包括 KML、XAML、RSS、MusicML、MathML 都符合XML 模式。 - 出于上述原因,如果您的应用在 pandas 操作之前构建 XML,请使用适当的 DOM 库(如
etree
和lxml
)构建必要的文档,而不是通过字符串连接或正则表达式调��。请始终记住,XML 是一个带有标记规则的特殊文本文件。 - 对于非常大的 XML 文件(几百 MB 到 GB),XPath 和 XSLT 可能会成为占用大量内存的操作。确保有足够的可用 RAM 来读取和写入大型 XML 文件(大约是文本大小的 5 倍)。
- 因为 XSLT 是一种编程语言,请谨慎使用,因为这样的脚本可能在您的环境中构成安全风险,并且可能运行大型或无限递归操作。始终在小片段上测试脚本,然后再进行完整运行。
- etree解析器支持
read_xml
和to_xml
的所有功能,除了复杂的 XPath 和任何 XSLT。尽管功能有限,etree
仍然是一个可靠且功能强大的解析器和树构建器。对于较大的文件,其性能可能略逊于lxml
,但在小到中等大小的文件上相对不易察觉。
Pandas 2.2 中文官方教程和指南(十·一)(4)https://developer.aliyun.com/article/1509775