Pandas 2.2 中文官方教程和指南(十·一)(3)

简介: Pandas 2.2 中文官方教程和指南(十·一)

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>&amp;</td>
 <td>2.396780</td>
 </tr>
 <tr>
 <th>1</th>
 <td>&lt;</td>
 <td>0.014871</td>
 </tr>
 <tr>
 <th>2</th>
 <td>&gt;</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 有关的问题

  • 优点
  • lxml 非常快速。

  • lxml 需要正确安装 Cython 才能安装。
  • 缺点
  • lxml 在没有提供 严格有效的标记 的情况下, 对其解析结果做出任何保证。

  • 综上所述,我们选择允许您,用户,使用lxml后端,但是如果lxml无法解析,则将使用html5lib

  • 因此,强烈建议您安装BeautifulSoup4html5lib,这样即使lxml失败,您仍将获得有效结果(假设其他一切有效)。

使用BeautifulSoup4 使用lxml 作为后端的问题

  • 由于BeautifulSoup4本质上只是一个围绕解析器后端的包装器,因此上述问题在这里同样存在。

使用BeautifulSoup4 使用html5lib 作为后端的问题

  • 优点
  • html5liblxml宽容得多,因此以更理智的方式处理现实中的标记,而不仅仅是,例如,删除一个元素而不通知您。

  • html5lib 会自动从无效标记生成有效的 HTML5 标记。这对于解析 HTML 表格非常重要,因为它保证了一个有效的文档。但是,这并不意味着它是“正确的”,因为修复标记的过程没有一个单一的定义。

  • html5lib是纯 Python 的,除了自己的安装之外,不需要额外的构建步骤。
  • 缺点
  • 使用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” 的内容读取为 StringIOBytesIO 实例,并将其传递给 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”列车的稍微嵌套的结构,其中 stationrides 元素将数据封装在各自的部分中。使用下面的 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 的 iterparseetree 的 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 规范etreelxml解析器将无法解析任何不符合规范或遵循 XML 语法规则的标记文档。请注意,除非遵循 XHTML 规范,否则 HTML 不是 XML 文档。然而,其他流行的标记类型,包括 KML、XAML、RSS、MusicML、MathML 都符合XML 模式
  • 出于上述原因,如果您的应用在 pandas 操作之前构建 XML,请使用适当的 DOM 库(如etreelxml)构建必要的文档,而不是通过字符串连接或正则表达式调��。请始终记住,XML 是一个带有标记规则的特殊文本文件。
  • 对于非常大的 XML 文件(几百 MB 到 GB),XPath 和 XSLT 可能会成为占用大量内存的操作。确保有足够的可用 RAM 来读取和写入大型 XML 文件(大约是文本大小的 5 倍)。
  • 因为 XSLT 是一种编程语言,请谨慎使用,因为这样的脚本可能在您的环境中构成安全风险,并且可能运行大型或无限递归操作。始终在小片段上测试脚本,然后再进行完整运行。
  • etree解析器支持read_xmlto_xml的所有功能,除了复杂的 XPath 和任何 XSLT。尽管功能有限,etree仍然是一个可靠且功能强大的解析器和树构建器。对于较大的文件,其性能可能略逊于lxml,但在小到中等大小的文件上相对不易察觉。


Pandas 2.2 中文官方教程和指南(十·一)(4)https://developer.aliyun.com/article/1509775

相关文章
|
10月前
|
SQL 数据采集 数据挖掘
Pandas 教程
10月更文挑战第25天
208 2
|
存储 JSON 数据格式
Pandas 使用教程 CSV - CSV 转 JSON
Pandas 使用教程 CSV - CSV 转 JSON
119 0
|
JSON 数据格式 Python
Pandas 使用教程 JSON
Pandas 使用教程 JSON
137 0
|
SQL 数据采集 JSON
Pandas 使用教程 Series、DataFrame
Pandas 使用教程 Series、DataFrame
216 0
|
数据采集 存储 数据可视化
Pandas高级教程:数据清洗、转换与分析
Pandas是Python的数据分析库,提供Series和DataFrame数据结构及数据分析工具,便于数据清洗、转换和分析。本教程涵盖Pandas在数据清洗(如缺失值、重复值和异常值处理)、转换(数据类型转换和重塑)和分析(如描述性统计、分组聚合和可视化)的应用。通过学习Pandas,用户能更高效地处理和理解数据,为数据分析任务打下基础。
1352 3
|
索引 Python
Pandas 2.2 中文官方教程和指南(一)(4)
Pandas 2.2 中文官方教程和指南(一)
138 0
|
存储 SQL JSON
Pandas 2.2 中文官方教程和指南(一)(3)
Pandas 2.2 中文官方教程和指南(一)
197 0
|
XML 关系型数据库 PostgreSQL
Pandas 2.2 中文官方教程和指南(一)(2)
Pandas 2.2 中文官方教程和指南(一)
378 0
|
XML 关系型数据库 MySQL
Pandas 2.2 中文官方教程和指南(一)(1)
Pandas 2.2 中文官方教程和指南(一)
496 0
|
C++ 索引 Python
Pandas 2.2 中文官方教程和指南(五)(4)
Pandas 2.2 中文官方教程和指南(五)
115 0