文章目录
本节介绍使用 Styler 类来可视化表格数据。有关使用图表进行可视化的信息,请参见 图表可视化。本文档是以 Jupyter Notebook 的形式编写的,可以在 这里 查看或下载。
Styler 对象和自定义显示
在对 DataFrame 的数据进行处理之后,应该执行样式和输出显示的自定义操作。如果对 DataFrame 进行进一步更改,Styler 不会动态更新。DataFrame.style
属性是一个返回 Styler 对象的属性。它在 Jupyter Notebook 中自动呈现,因为它在其上定义了 _repr_html_
方法。
Styler 可用于大型数据,但主要设计用于小型数据,目前可以输出以下格式:
- HTML
- LaTeX
- 字符串(通过扩展支持 CSV)
- Excel
- (JSON 目前不可用)
前三种格式都有用于格式化和自定义输出的显示自定义方法。这些方法包括:
- 使用 .format() 和 .format_index() 格式化值、索引和列标题,
- 使用 .relabel_index() 重命名索引或列标题标签,
- 使用 .hide() 隐藏某些列、索引和/或列标题,或索引名称,
- 使用 .concat() 连接相似的 DataFrame。
格式化显示
格式化值
Styler 将显示值与实际值区分开来,包括数据值和索引或列标题。为了控制显示值,将每个单元格中的文本打印为字符串,并且可以使用 .format() 和 .format_index() 方法根据 格式规范字符串 或一个接受单个值并返回字符串的可调用对象来进行操作。可以为整个表格、索引或单个列或 MultiIndex 级别定义此操作。还可以覆盖索引名称。
此外,格式化函数具有 precision 参数,特别用于格式化浮点数,还有 decimal 和 thousands 分隔符,以支持其他语言环境,na_rep 参数用于显示缺失数据,escape 和 hyperlinks 参数用于显示安全的 HTML 或 LaTeX。默认格式化程序配置为采用 pandas 的全局选项,例如 styler.format.precision
选项,可以使用 with pd.option_context('format.precision', 2):
进行控制:
import pandas as pd
import numpy as np
import matplotlib as mpl
df = pd.DataFrame({
"strings": ["Adam", "Mike"],
"ints": [1, 3],
"floats": [1.123, 1000.23]
})
df.style \
.format(precision=3, thousands=".", decimal=",") \
.format_index(str.upper, axis=1) \
.relabel_index(["row 1", "row 2"], axis=0)
使用 Styler 来操作显示是一个有用的功能,因为保留索引和数据值以供其他用途使用可以更好地控制显示。您不必覆盖 DataFrame 以按照自己的喜好显示它。下面是一个更全面的示例,演示了在仍然依赖底层数据进行索引和计算的情况下使用格式化函数。
weather_df = pd.DataFrame(np.random.rand(10,2)*5,
index=pd.date_range(start="2021-01-01", periods=10),
columns=["Tokyo", "Beijing"])
def rain_condition(v):
if v < 1.75:
return "干燥"
elif v < 2.75:
return "下雨"
return "大雨"
def make_pretty(styler):
styler.set_caption("天气状况")
styler.format(rain_condition)
styler.format_index(lambda v: v.strftime("%A"))
styler.background_gradient(axis=None, vmin=1, vmax=5, cmap="YlGnBu")
return styler
weather_df
weather_df.loc["2021-01-04":"2021-01-08"].style.pipe(make_pretty)
隐藏数据
可以完全隐藏索引和列标题,以及希望排除的行或列的子选择。这两个选项使用相同的方法执行。
可以通过调用 .hide() 而不带任何参数来隐藏索引的渲染,这在索引基于整数时可能很有用。类似地,可以通过调用 .hide(axis=“columns”) 而不带任何其他参数来隐藏列标题。
可以通过调用相同的 .hide() 方法并传入行/列标签、类似列表或行/列标签的切片来隐藏渲染的特定行或列,作为 subset
参数。
隐藏不会更改 CSS 类的整数排列,例如隐藏 DataFrame 的前两列意味着列类索引仍将从 col2
开始,因为 col0
和 col1
简单地被忽略。
df = pd.DataFrame(np.random.randn(5, 5))
df.style \
.hide(subset=[0, 2, 4], axis=0) \
.hide(subset=[0, 2, 4], axis=1)
要将功能反转为 显示 功能,最佳实践是组合一个隐藏项列表。
show = [0, 2, 4]
df.style \
.hide([row for row in df.index if row not in show], axis=0) \
.hide([col for col in df.columns if col not in show], axis=1)
连接 DataFrame 输出
由于连接的对象是 Stylers,它们可以独立地进行样式设置,如下所示,并且它们的连接保留了这些样式。
summary_styler = df.agg(["sum", "mean"]).style \
.format(precision=3) \
.relabel_index(["Sum", "Average"])
df.style.format(precision=1).concat(summary_styler)
Styler 对象和 HTML
Styler 最初是为了支持各种 HTML 格式选项而构建的。它的 HTML 输出创建了一个 HTML <table>
,并利用 CSS 样式语言来操作许多参数,包括颜色、字体、边框、背景等。在这里可以了解有关样式化 HTML 表格的更多信息。这使得 Styler 在开箱即用的同时也具有很大的灵活性,甚至可以让 Web 开发人员将 DataFrame 集成到他们现有的用户界面设计中。
下面我们展示了默认输出,它看起来非常类似于标准的 DataFrame HTML 表示。但是这里的 HTML 已经为每个单元格附加了一些 CSS 类,即使我们还没有创建任何样式。我们可以通过调用 .to_html() 方法来查看这些类,它返回原始的 HTML 字符串,对于进一步处理或添加到文件中非常有用 - 在更多关于 CSS 和 HTML中继续阅读。本节还将提供一个演示如何将此默认输出转换为更具表达力的 DataFrame 输出的步骤。例如,我们如何构建 s
:
df = pd.DataFrame([[38.0, 2.0, 18.0, 22.0, 21, np.nan],[19, 439, 6, 452, 226,232]],
index=pd.Index(['Tumour (Positive)', 'Non-Tumour (Negative)'], name='Actual Label:'),
columns=pd.MultiIndex.from_product([['Decision Tree', 'Regression', 'Random'],['Tumour', 'Non-Tumour']], names=['Model:', 'Predicted:']))
df.style
s
我们首先创建了 Styler 对象,然后使用 .hide() 隐藏不需要的列,选择感兴趣的范围。
s = df.style.format('{:.0f}').hide([('Random', 'Tumour'), ('Random', 'Non-Tumour')], axis="columns")
s
添加样式的方法
有3种主要方法可以向 Styler 添加自定义 CSS 样式:
- 使用 .set_table_styles() 来控制指定内部 CSS 的表的更广泛区域。尽管表样式允许灵活添加控制表的所有单独部分的 CSS 选择器和属性,但对于单个单元格的规范指定来说,它们是笨重的。此外,需要注意的是,表样式无法导出到 Excel。
- 使用 .set_td_classes() 将外部 CSS 类直接链接到数据单元格,或者将 .set_table_styles() 创建的内部 CSS 类链接到数据单元格。在这里可以了解更多信息。它们不能用于列标题行或索引,并且也无法导出到 Excel。
- 使用 .apply() 和 .map() 函数向特定数据单元格直接添加内部 CSS。在这里可以了解更多信息。从 v1.4.0 开始,还有一些直接作用于列标题行或索引的方法:.apply_index() 和 .map_index()。请注意,只有这些方法添加的样式才会导出到 Excel。这些方法的工作方式与 DataFrame.apply() 和 DataFrame.map() 类似。
表样式
表样式足够灵活,可以控制表的所有单独部分,包括列标题和索引。但是,对于单个数据单元格或任何类型的条件格式,它们可能会很难处理,因此我们建议将表样式用于广泛的样式设置,例如一次性设置整行或整列。
表样式还用于控制可以一次应用于整个表的功能,例如创建通用的悬停功能。:hover
伪选择器以及其他伪选择器只能以这种方式使用。
要复制 CSS 选择器和属性(属性值对)的正常格式,例如:
tr:hover {
background-color: #ffff99;
}
将样式传递给 .set_table_styles() 的必要格式是一个字典列表,每个字典都有一个 CSS 选择器标签和 CSS 属性。属性可以是一个 2 元组的列表,也可以是常规的 CSS 字符串,例如:
cell_hover = { # for row hover use <tr> instead of <td>
'selector': 'td:hover',
'props': [('background-color', '#ffffb3')]
}
index_names = {
'selector': '.index_name',
'props': 'font-style: italic; color: darkgrey; font-weight:normal;'
}
headers = {
'selector': 'th:not(.index_name)',
'props': 'background-color: #000066; color: white;'
}
s.set_table_styles([cell_hover, index_names, headers])
接下来,我们只需添加一些针对表格特定部分的样式修饰。在这里要小心,因为我们正在链式调用方法,我们需要明确地指示方法不要overwrite
现有的样式。
s.set_table_styles([
{'selector': 'th.col_heading', 'props': 'text-align: center;'},
{'selector': 'th.col_heading.level0', 'props': 'font-size: 1.5em;'},
{'selector': 'td', 'props': 'text-align: center; font-weight: bold;'},
], overwrite=False)
作为方便的方法(自 1.2.0 版本起),我们还可以将一个包含行或列键的字典传递给.set_table_styles(),该字典包含行或列的键。在幕后,Styler只是索引这些键,并根据需要向给定的 CSS 选择器添加相关的.col<m>
或.row<n>
类。
s.set_table_styles({
('Regression', 'Tumour'): [{'selector': 'th', 'props': 'border-left: 1px solid white'},
{'selector': 'td', 'props': 'border-left: 1px solid #000066'}]
}, overwrite=False, axis=0)
设置类和链接到外部 CSS
如果您设计了一个网站,那么您可能已经有一个控制表格和单元格对象样式的外部 CSS 文件。您可能希望使用这些原生文件,而不是在 Python 中复制所有的 CSS(并复制任何维护工作)。
表格属性
使用.set_table_attributes()方法非常容易向主要的<table>
添加一个class
。该方法还可以附加内联样式 - 在CSS 层次结构中阅读更多信息。
out = s.set_table_attributes('class="my-table-cls"').to_html()
print(out[out.find('<table'):][:109])
<table id="T_xyz01" class="my-table-cls">
<thead>
<tr>
<th class="index_name level0" >模型:</th>
数据单元格 CSS 类
自 1.2.0 版本起
.set_td_classes()方法接受一个与底层Styler的 DataFrame 具有匹配索引和列的 DataFrame。该 DataFrame 将包含字符串作为要添加到各个数据单元格(即<td>
元素)的 CSS 类。我们将在内部创建自己的类,并将它们添加到表格样式中。我们将在工具提示部分添加边框。
s.set_table_styles([ # 创建内部 CSS 类
{'selector': '.true', 'props': 'background-color: #e6ffe6;'},
{'selector': '.false', 'props': 'background-color: #ffe6e6;'},
], overwrite=False)
cell_color = pd.DataFrame([['true ', 'false ', 'true ', 'false '],
['false ', 'true ', 'false ', 'true ']],
index=df.index,
columns=df.columns[:4])
s.set_td_classes(cell_color)
Styler 函数
对数据进行操作
我们使用以下方法来传递样式函数。这些方法都接受一个函数(和一些其他关键字参数),并以某种方式将其应用于 DataFrame,从而呈现 CSS 样式。
- .map()(逐个元素):接受一个函数,该函数接受一个值并返回一个带有 CSS 属性-值对的字符串。
- .apply()(按列/行/表格):接受一个函数,该函数接受一个 Series 或 DataFrame,并返回一个具有相同形状的 Series、DataFrame 或 numpy 数组,其中每个元素都是带有 CSS 属性-值对的字符串。此方法根据
axis
关键字参数,逐个列或行传递您的 DataFrame,或者一次传递整个表格。对于按列使用axis=0
,按行使用axis=1
,对于一次传递整个表格,请使用axis=None
。
此方法对于对数据单元格应用多个复杂逻辑非常有用。我们创建一个新的 DataFrame 来演示这一点。
np.random.seed(0)
df2 = pd.DataFrame(np.random.randn(10,4), columns=['A','B','C','D'])
df2.style
例如,我们可以构建一个函数,如果文本为负,则为其着色,并将其与部分淡化微不足道的值的函数链接在一起。由于这个函数逐个元素地查看,我们使用map
。
def style_negative(v, props=''):
return props if v < 0 else None
s2 = df2.style.map(style_negative, props='color:red;')\
.map(lambda v: 'opacity: 20%;' if (v < 0.3) and (v > -0.3) else None)
s2
我们还可以构建一个函数,一次性在行、列和 DataFrame 中同时突出显示最大值。在这种情况下,我们使用 apply
。下面我们突出显示一列中的最大值。
def highlight_max(s, props=''):
return np.where(s == np.nanmax(s.values), props, '')
s2.apply(highlight_max, props='color:white;background-color:darkblue', axis=0)
我们可以在不同的轴上使用相同的函数,这里用紫色突出显示 DataFrame 的最大值,用粉色突出显示行的最大值。
s2.apply(highlight_max, props='color:white;background-color:pink;', axis=1)\
.apply(highlight_max, props='color:white;background-color:purple', axis=None)
最后一个示例展示了一些样式被其他样式覆盖的情况。通常情况下,最近应用的样式是有效的,但你可以在 CSS 层次结构 部分中了解更多信息。你还可以将这些样式应用于 DataFrame 的更精细的部分 - 在 subset slicing 部分中了解更多信息。
只使用类来复制一些功能也是可能的,但可能更麻烦。参见 优化 的第 3) 项。
调试提示:如果你在编写样式函数时遇到问题,尝试将其直接传递给 DataFrame.apply
。在内部,Styler.apply
使用 DataFrame.apply
,因此结果应该是相同的,并且使用 DataFrame.apply
,你将能够检查每个单元格中预期函数的 CSS 字符串输出。
对索引和列标题进行操作
通过使用以下方法,可以对标题进行类似的应用:
- .map_index()(逐个元素):接受一个接受单个值并返回带有 CSS 属性-值对的字符串的函数。
- .apply_index()(按级别):接受一个接受 Series 并返回一个具有相同形状的 Series 或 numpy 数组的函数,其中每个元素都是带有 CSS 属性-值对的字符串。此方法逐个传递索引的每个级别。要为索引使用样式,请使用
axis=0
,要为列标题使用样式,请使用axis=1
。
可以选择 MultiIndex
的 level
,但目前这些方法没有类似的 subset
应用程序。
s2.map_index(lambda v: "color:pink;" if v>4 else "color:darkblue;", axis=0)
s2.apply_index(lambda s: np.where(s.isin(["A", "B"]), "color:pink;", "color:darkblue;"), axis=1)
工具提示和标题
可以使用 .set_caption() 方法添加表格标题。你可以使用表格样式来控制与标题相关的 CSS。
s.set_caption("Confusion matrix for multiple cancer prediction models.")\
.set_table_styles([{
'selector': 'caption',
'props': 'caption-side: bottom; font-size:1.25em;'
}], overwrite=False)
可以使用 .set_tooltips() 方法添加工具提示(自版本 1.3.0 起),方法与通过提供基于字符串的 DataFrame 来将 CSS 类添加到数据单元格相同。你不必为工具提示指定 css_class
名称或任何 css props
,因为有标准默认值,但如果你想要更多的视觉控制,可以使用该选项。
tt = pd.DataFrame([['This model has a very strong true positive rate',
"This model's total number of false negatives is too high"]],
index=['Tumour (Positive)'], columns=df.columns[[0,3]])
s.set_tooltips(tt, props='visibility: hidden; position: absolute; z-index: 1; border: 1px solid #000066;'
'background-color: white; color: #000066; font-size: 0.8em;'
'transform: translate(0px, -24px); padding: 0.6em; border-radius: 0.5em;')
我们表格中唯一剩下的事情是添加突出显示的边框,以吸引观众注意工具提示。我们将像以前一样创建内部 CSS 类,使用表格样式。设置类总是会覆盖,因此我们需要确保添加先前的类。
s.set_table_styles([ # create internal CSS classes
{'selector': '.border-red', 'props': 'border: 2px dashed red;'},
{'selector': '.border-green', 'props': 'border: 2px dashed green;'},
], overwrite=False)
cell_border = pd.DataFrame([['border-green ', ' ', ' ', 'border-red '],
[' ', ' ', ' ', ' ']],
index=df.index,
columns=df.columns[:4])
s.set_td_classes(cell_color + cell_border)
通过切片进行更精细的控制
到目前为止,我们展示的 Styler.apply
和 Styler.map
函数的示例都没有演示 subset
参数的使用。这是一个非常有用的参数,它允许您对特定的行或列应用样式,而无需将该逻辑编码到您的 style
函数中。
传递给 subset
的值的行为类似于对 DataFrame 进行切片:
- 标量被视为列标签
- 列表(或 Series 或 NumPy 数组)被视为多个列标签
- 元组被视为
(row_indexer, column_indexer)
考虑使用 pd.IndexSlice
来构造最后一个元组。我们将创建一个多级索引的 DataFrame 来演示这个功能。
df3 = pd.DataFrame(np.random.randn(4,4),
pd.MultiIndex.from_product([['A', 'B'], ['r1', 'r2']]),
columns=['c1','c2','c3','c4'])
df3
$
我们将使用 subset 来突出显示第三列和第四列中的最大值,并用红色文本标记。我们将用黄色突出显示切片的子集区域。
slice_ = ['c3', 'c4']
df3.style.apply(highlight_max, props='color:red;', axis=0, subset=slice_)\
.set_properties(**{'background-color': '#ffffb3'}, subset=slice_)
如果与建议的 IndexSlice
结合使用,它可以在两个维度上进行更灵活的索引。
idx = pd.IndexSlice
slice_ = idx[idx[:,'r1'], idx['c2':'c4']]
df3.style.apply(highlight_max, props='color:red;', axis=0, subset=slice_)\
.set_properties(**{'background-color': '#ffffb3'}, subset=slice_)
这也提供了在 axis=1
的情况下对行进行子选择的灵活性。
slice_ = idx[idx[:,'r2'], :]
df3.style.apply(highlight_max, props='color:red;', axis=1, subset=slice_)\
.set_properties(**{'background-color': '#ffffb3'}, subset=slice_)
还可以进行条件过滤。
假设我们只想在第 2 列和第 4 列中突出显示最大值,但仅当第 1 列和第 3 列的总和小于 -2.0 时(基本上排除行 (:,'r2')
)。
slice_ = idx[idx[(df3['c1'] + df3['c3']) < -2.0], ['c2', 'c4']]
df3.style.apply(highlight_max, props='color:red;', axis=1, subset=slice_)\
.set_properties(**{'background-color': '#ffffb3'}, subset=slice_)
目前只支持基于标签的切片,不支持基于位置的切片,也不支持可调用对象。
如果您的样式函数使用了 subset
或 axis
关键字参数,请考虑将函数包装在 functools.partial
中,将该关键字部分化。
my_func2 = functools.partial(my_func, subset=42)
优化
通常情况下,对于较小的表格和大多数情况,渲染的 HTML 不需要进行优化,我们也不建议这样做。只有在以下两种情况下才值得考虑:
- 如果您正在渲染和样式化一个非常大的 HTML 表格,某些浏览器可能会出现性能问题。
- 如果您正在使用
Styler
动态创建在线用户界面的一部分,并希望提高网络性能。
在这里,我们建议采取以下步骤来实现优化:
1. 删除 UUID 和 cell_ids
忽略 uuid
并将 cell_ids
设置为 False
。这将防止不必要的 HTML。
这是次优的:
df4 = pd.DataFrame([[1,2],[3,4]])
s4 = df4.style
这是更好的:
from pandas.io.formats.style import Styler
s4 = Styler(df4, uuid_len=0, cell_ids=False)
2. 使用表格样式
在可能的情况下使用表格样式(例如,同时为所有单元格、行或列) ,因为 CSS 几乎总是比其他格式更高效。
这是次优的:
props = 'font-family: "Times New Roman", Times, serif; color: #e83e8c; font-size:1.3em;'
df4.style.map(lambda x: props, subset=[1])
这是更好的:
df4.style.set_table_styles([{'selector': 'td.col1', 'props': props}])
3. 设置类而不是使用 Styler 函数
对于大型的 DataFrame,如果将相同的样式应用于许多单元格,将样式声明为类,然后将这些类应用于数据单元格,可能比直接将样式应用于单元格更高效。然而,当您不关心优化时,使用 Styler 函数 api 可能更容易。
这是次优的:
df2.style.apply(highlight_max, props='color:white;background-color:darkblue;', axis=0)\
.apply(highlight_max, props='color:white;background-color:pink;', axis=1)\
.apply(highlight_max, props='color:white;background-color:purple', axis=None)
这是更好的:
build = lambda x: pd.DataFrame(x, index=df2.index, columns=df2.columns)
cls1 = build(df2.apply(highlight_max, props='cls-1 ', axis=0))
cls2 = build(df2.apply(highlight_max, props='cls-2 ', axis=1, result_type='expand').values)
cls3 = build(highlight_max(df2, props='cls-3 '))
df2.style.set_table_styles([
{'selector': '.cls-1', 'props': 'color:white;background-color:darkblue;'},
{'selector': '.cls-2', 'props': 'color:white;background-color:pink;'},
{'selector': '.cls-3', 'props': 'color:white;background-color:purple;'}
]).set_td_classes(cls1 + cls2 + cls3)
4. 不要使用工具提示
工具提示需要 cell_ids
来工作,并且会为每个数据单元格生成额外的 HTML 元素。
5. 如果每个字节都很重要,请使用字符串替换
您可以删除不必要的 HTML,或者通过替换默认的类名来缩短默认的类名。您可以在下面的 CSS 中阅读更多关于 CSS 的信息。
my_css = {
"row_heading": "",
"col_heading": "",
"index_name": "",
"col": "c",
"row": "r",
"col_trim": "",
"row_trim": "",
"level": "l",
"data": "",
"blank": "",
}
html = Styler(df4, uuid_len=0, cell_ids=False)
html.set_table_styles([{'selector': 'td', 'props': props},
{'selector': '.c1', 'props': 'color:green;'},
{'selector': '.l0', 'props': 'color:blue;'}],
css_class_names=my_css)
print(html.to_html())
<style type="text/css">
#T_ td {
font-family: "Times New Roman", Times, serif;
color: #e83e8c;
font-size: 1.3em;
}
#T_ .c1 {
color: green;
}
#T_ .l0 {
color: blue;
}
</style>
<table id="T_">
<thead>
<tr>
<th class=" l0" > </th>
<th class=" l0 c0" >0</th>
<th class=" l0 c1" >1</th>
</tr>
</thead>
<tbody>
<tr>
<th class=" l0 r0" >0</th>
<td class=" r0 c0" >1</td>
<td class=" r0 c1" >2</td>
</tr>
<tr>
<th class=" l0 r1" >1</th>
<td class=" r1 c0" >3</td>
<td class=" r1 c1" >4</td>
</tr>
</tbody>
</table>
html
内置样式
一些常用的样式函数已经内置到 Styler
中,因此您不需要自己编写和应用它们。目前这些函数的列表如下:
- .highlight_null:用于识别缺失数据。
- .highlight_min 和 .highlight_max:用于识别数据的极值。
- .highlight_between 和 .highlight_quantile:用于识别数据中的类别。
- .background_gradient:根据数值范围在单元格背景上突出显示单元格。
- .text_gradient:根据数值范围在文本上突出显示文本。
- .bar:在单元格背景中显示小型图表。
每个函数的详细文档通常会提供更多关于其参数的示例。
高亮空值
df2.iloc[0,2] = np.nan
df2.iloc[4,3] = np.nan
df2.loc[:4].style.highlight_null(color='yellow')
高亮最小值或最大值
df2.loc[:4].style.highlight_max(axis=1, props='color:white; font-weight:bold; background-color:darkblue;')
高亮介于某个范围的值
该方法接受浮点数或 NumPy 数组或 Series 作为范围,前提是索引匹配。
left = pd.Series([1.0, 0.0, 1.0], index=["A", "B", "D"])
df2.loc[:4].style.highlight_between(left=left, right=1.5, axis=1, props='color:white; background-color:purple;')
高亮分位数
用于检测最高或最低百分位数值
df2.loc[:4].style.highlight_quantile(q_left=0.85, axis=None, color='yellow')
背景渐变和文本渐变
您可以使用 background_gradient
和 text_gradient
方法创建“热图”。这些方法需要 matplotlib,我们将使用 Seaborn 来获取一个漂亮的颜色映射。
import seaborn as sns
cm = sns.light_palette("green", as_cmap=True)
df2.style.background_gradient(cmap=cm)
df2.style.text_gradient(cmap=cm)
.background_gradient 和 .text_gradient 有许多关键字参数可以自定义渐变和颜色。请参阅文档。
设置属性
当样式实际上不依赖于值时,可以使用 Styler.set_properties
。这只是 .map
的一个简单包装,其中函数对所有单元格返回相同的属性。
df2.loc[:4].style.set_properties(**{'background-color': 'black',
'color': 'lawngreen',
'border-color': 'white'})
条形图
您可以在 DataFrame 中包含“条形图”。
df2.style.bar(subset=['A', 'B'], color='#d65f5f')
通过附加关键字参数,可以更好地控制居中和定位,并且可以传递 [color_negative, color_positive]
的列表以突出显示较低和较高的值,或者使用 matplotlib 的 colormap。
为了展示一个例子,下面的代码演示了如何使用新的 align
选项来改变上述样式,结合设置 vmin
和 vmax
的限制,图形的 width
,以及单元格的底层 css props
,留出空间来显示文本和条形图。我们还使用 text_gradient
使用 matplotlib 的 colormap 来将文本着色为与条形图相同的颜色(尽管在这种情况下,可能最好不使用此附加效果)。
df2.style.format('{:.3f}', na_rep="")\
.bar(align=0, vmin=-2.5, vmax=2.5, cmap="bwr", height=50,
width=60, props="width: 120px; border-right: 1px solid black;")\
.text_gradient(cmap="bwr", vmin=-2.5, vmax=2.5)
下面的示例旨在突出新的对齐选项的行为:
HTML(head)
共享样式
假设您已经创建了一个漂亮的 DataFrame 样式,并且现在想将相同的样式应用于第二个 DataFrame。使用 df1.style.export
导出样式,并使用 df1.style.set
在第二个 DataFrame 上导入它。
style1 = df2.style .map(style_negative, props='color:red;') .map(lambda v: 'opacity: 20%;' if (v < 0.3) and (v > -0.3) else None) .set_table_styles([{"selector": "th", "props": "color: blue;"}]) .hide(axis="index")
style1
style2 = df3.style
style2.use(style1.export())
style2
请注意,即使这些样式是数据感知的,您仍然可以共享它们。这些样式会在应用于新的 DataFrame 时重新评估。
限制
- 仅适用于 DataFrame(使用
Series.to_frame().style
) - 索引和列不需要是唯一的,但某些样式函数只能与唯一索引一起使用。
- 没有大型 repr,并且构建性能不是很好;尽管我们有一些 HTML 优化
- 您只能应用样式,无法插入新的 HTML 实体,除非通过子类化。
其他有趣且有用的功能
以下是一些有趣的示例。
小部件
Styler
与小部件的交互非常好。如果您在在线查看此内容而不是自己运行笔记本,则无法交互地调整颜色调色板。
from ipywidgets import widgets
@widgets.interact
def f(h_neg=(0, 359, 1), h_pos=(0, 359), s=(0., 99.9), l=(0., 99.9)):
return df2.style.background_gradient(
cmap=sns.palettes.diverging_palette(h_neg=h_neg, h_pos=h_pos, s=s, l=l,
as_cmap=True)
)
放大
def magnify():
return [dict(selector="th",
props=[("font-size", "4pt")]),
dict(selector="td",
props=[('padding', "0em 0em")]),
dict(selector="th:hover",
props=[("font-size", "12pt")]),
dict(selector="tr:hover td:hover",
props=[('max-width', '200px'),
('font-size', '12pt')])
]
np.random.seed(25)
cmap = cmap=sns.diverging_palette(5, 250, as_cmap=True)
bigdf = pd.DataFrame(np.random.randn(20, 25)).cumsum()
bigdf.style.background_gradient(cmap, axis=1)\
.set_properties(**{'max-width': '80px', 'font-size': '1pt'})\
.set_caption("鼠标悬停放大")\
.format(precision=2)\
.set_table_styles(magnify())
固定表头
如果你在笔记本中显示一个大的矩阵或DataFrame,但是你希望始终看到列和行的表头,你可以使用 .set_sticky 方法来操作表格样式的 CSS。
bigdf = pd.DataFrame(np.random.randn(16, 100))
bigdf.style.set_sticky(axis="index")
还可以固定多级索引,甚至只固定特定的级别。
bigdf.index = pd.MultiIndex.from_product([["A","B"],[0,1],[0,1,2,3]])
bigdf.style.set_sticky(axis="index", pixel_size=18, levels=[1,2])
HTML 转义
假设您需要在 HTML 中显示 HTML,当渲染器无法区分时,这可能会有点麻烦。您可以使用 escape
格式选项来处理这个问题,甚至可以在包含 HTML 的格式化程序中使用它。
df4 = pd.DataFrame([['<div></div>', '"&other"', '<span></span>']])
df4.style
df4.style.format(escape="html")
df4.style.format('<a href="https://pandas.pydata.org" target="_blank">{}</a>', escape="html")
导出到 Excel#
自版本 0.20.0 起,可以使用 OpenPyXL
或 XlsxWriter
引擎将带样式的 DataFrames
导出到 Excel 工作表。支持的 CSS2.2 属性包括:
background-color
(背景颜色)border-style
属性border-width
属性border-color
属性color
(颜色)font-family
(字体)font-style
(字体样式)font-weight
(字体粗细)text-align
(文本对齐方式)text-decoration
(文本修饰)vertical-align
(垂直对齐方式)white-space: nowrap
(不换行)- 支持缩写和特定边框属性(例如
border-style
和border-left-style
),以及所有边的border
缩写(border: 1px solid green
)或指定边的缩写(border-left: 1px solid green
)。使用border
缩写将覆盖之前设置的任何边框属性(有关详细信息,请参见 CSS Working Group) - 目前仅支持 CSS2 命名颜色和形式为
#rgb
或#rrggbb
的十六进制颜色。 - 还可以使用以下伪 CSS 属性设置 Excel 特定的样式属性:
number-format
(数字格式)border-style
(Excel 特定样式的边框样式:“hair”、“mediumDashDot”、“dashDotDot”、“mediumDashDotDot”、“dashDot”、“slantDashDot”或“mediumDashed”)
导出到 Excel 不包括表级样式和数据单元格的 CSS 类。必须使用 Styler.apply
和/或 Styler.map
方法将单个单元格的属性映射到 Excel。
df2.style. map(style_negative, props='color:red;'). highlight_max(axis=0). to_excel('styled.xlsx', engine='openpyxl')
输出的截图如下:
导出到 LaTeX
自版本 1.3.0 起,支持将 Styler
导出到 LaTeX。有关 .to_latex 方法的文档提供了更多详细信息和示例。
关于 CSS 和 HTML 的更多信息
层叠样式表(CSS)语言旨在影响浏览器如何呈现 HTML 元素,具有其自身的特点。它从不报告错误:它只是默默地忽略它们,并且不会按照您的意图呈现对象,因此有时可能令人沮丧。以下是关于 Styler
如何创建 HTML 并与 CSS 交互的简要介绍,以及避免常见陷阱的建议。
CSS 类和 ID
每个单元格附加的 CSS class
的精确结构如下。
- 包含索引和列名称的单元格包括
index_name
和level<k>
,其中k
是在 MultiIndex 中的级别 - 索引标签单元格包括
row_heading
level<k>
,其中k
是在 MultiIndex 中的级别row<m>
,其中m
是行的数字位置
- 列标签单元格包括
col_heading
level<k>
,其中k
是在 MultiIndex 中的级别col<n>
,其中n
是列的数字位置
- 数据单元格包括
data
row<m>
,其中m
是单元格的数字位置col<n>
,其中n
是单元格的数字位置
- 空单元格包括
blank
- 裁剪的单元格包括
col_trim
或row_trim
id
的结构是 T_uuid_level<k>_row<m>_col<n>
,其中 level<k>
仅用于标题,标题只会有 row<m>
或 col<n>
中的一个。默认情况下,我们还在每个行/列标识符前面添加了一个 UUID,以确保同一笔记本或页面中的一个 DataFrame 的样式不会与另一个 DataFrame 的样式冲突。您可以在 Optimization 中了解有关 UUID 使用的更多信息。
我们可以通过调用 .to_html() 方法来查看 HTML 的示例。
print(pd.DataFrame([[1,2],[3,4]], index=['i1', 'i2'], columns=['c1', 'c2']).style.to_html())
<style type="text/css">
</style>
<table id="T_f4082">
<thead>
<tr>
<th class="blank level0" > </th>
<th id="T_f4082_level0_col0" class="col_heading level0 col0" >c1</th>
<th id="T_f4082_level0_col1" class="col_heading level0 col1" >c2</th>
</tr>
</thead>
<tbody>
<tr>
<th id="T_f4082_level0_row0" class="row_heading level0 row0" >i1</th>
<td id="T_f4082_row0_col0" class="data row0 col0" >1</td>
<td id="T_f4082_row0_col1" class="data row0 col1" >2</td>
</tr>
<tr>
<th id="T_f4082_level0_row1" class="row_heading level0 row1" >i2</th>
<td id="T_f4082_row1_col0" class="data row1 col0" >3</td>
<td id="T_f4082_row1_col1" class="data row1 col1" >4</td>
</tr>
</tbody>
</table>
CSS 层次结构
示例显示,当 CSS 样式重叠时,HTML 渲染中最后出现的样式优先。因此,以下代码会产生不同的结果:
df4 = pd.DataFrame([['text']])
df4.style.map(lambda x: 'color:green;') .map(lambda x: 'color:red;')
df4.style.map(lambda x: 'color:red;') .map(lambda x: 'color:green;')
这仅适用于层次结构或重要性相等的 CSS 规则。您可以在这里了解有关 CSS 特异性的更多信息,但对于我们的目的,总结关键点就足够了:
每个 HTML 元素的 CSS 重要性分数从零开始,然后按以下方式累加:
- 对于内联样式属性,加 1000
- 对于每个 ID,加 100
- 对于每个属性、类或伪类,加 10
- 对于每个元素名称或伪元素,加 1
让我们使用这个规则来描述以下配置的行为:
df4.style.set_uuid('a_') .set_table_styles([{'selector': 'td', 'props': 'color:red;'}]) .map(lambda x: 'color:green;')
这段文本是红色的,因为生成的选择器 #T_a_ td
的值为 101(ID 加元素),而 #T_a_row0_col0
的值仅为 100(ID),因此尽管在 HTML 中它出现在前面,但被认为是次要的。
df4.style.set_uuid('b_') .set_table_styles([{'selector': 'td', 'props': 'color:red;'},
{'selector': '.cls-1', 'props': 'color:blue;'}]) .map(lambda x: 'color:green;') .set_td_classes(pd.DataFrame([['cls-1']]))
在上述情况下,文本是蓝色的,因为选择器 #T_b_ .cls-1
的值为 110(ID 加类),这具有优先权。
df4.style.set_uuid('c_') .set_table_styles([{'selector': 'td', 'props': 'color:red;'},
{'selector': '.cls-1', 'props': 'color:blue;'},
{'selector': 'td.data', 'props': 'color:yellow;'}]) .map(lambda x: 'color:green;') .set_td_classes(pd.DataFrame([['cls-1']]))
现在我们创建了另一个表格样式,这次选择器 T_c_ td.data
(ID 加元素加类)被提升为 111。
如果您的样式未能应用,并且这真的很令人沮丧,请尝试使用 !important
来打出王牌。
df4.style.set_uuid('d_') .set_table_styles([{'selector': 'td', 'props': 'color:red;'},
{'selector': '.cls-1', 'props': 'color:blue;'},
{'selector': 'td.data', 'props': 'color:yellow;'}]) .map(lambda x: 'color:green !important;') .set_td_classes(pd.DataFrame([['cls-1']]))
终于得到了那个绿色的文本!
可扩展性
pandas 的核心是“高性能、易于使用的数据结构”。考虑到这一点,我们希望 DataFrame.style
实现两个目标:
- 提供一个令人愉快的交互式使用的 API,并且对于许多任务来说“足够好”
- 为专门的库提供基础
如果你在这方面构建了一个很棒的库,请告诉我们,我们将链接到它。
子类化
如果默认模板不完全符合您的需求,您可以子类化 Styler 并扩展或覆盖模板。我们将展示一个例子,将自定义标题插入到每个表格之前。
from jinja2 import Environment, ChoiceLoader, FileSystemLoader
from IPython.display import HTML
from pandas.io.formats.style import Styler
我们将使用以下模板:
with open("templates/myhtml.tpl") as f:
print(f.read())
{% extends "html_table.tpl" %}
{% block table %}
<h1>{{ table_title|default("My Table") }}</h1>
{{ super() }}
{% endblock table %}
现在,我们已经创建了一个模板,我们需要设置一个 Styler 的子类,让它知道这个模板。
class MyStyler(Styler):
env = Environment(
loader=ChoiceLoader([
FileSystemLoader("templates"), # 包含我们的模板
Styler.loader, # 默认模板
])
)
template_html_table = env.get_template("myhtml.tpl")
注意,我们在环境的加载器中包含了原始加载器。这是因为我们扩展了原始模板,所以 Jinja 环境需要能够找到它。
现在我们可以使用这个自定义的 styler。它的 __init__
方法接受一个 DataFrame。
MyStyler(df3)
My Table
我们的自定义模板接受一个 table_title
关键字。我们可以在 .to_html
方法中提供该值。
HTML(MyStyler(df3).to_html(table_title="扩展示例"))
扩展示例
为了方便起见,我们提供了 Styler.from_custom_template
方法,它与自定义子类相同。
EasyStyler = Styler.from_custom_template("templates", "myhtml.tpl")
HTML(EasyStyler(df3).to_html(table_title="另一个标题"))
另一个标题
模板结构
以下是样式生成模板和表格生成模板的结构:
样式模板:
HTML(style_structure)
表格模板:
HTML(table_structure)
有关更多详细信息,请参见 GitHub 仓库 中的模板。
Pandas 2 使用指南导读
Pandas 2 使用指南:4、IO工具(文本、CSV、HDF5等)
Pandas 2 使用指南:8、写时复制(Copy-on-Write,CoW)
Pandas 2 使用指南:10、重塑和透视表ReShapingand Pivot Tables
Pandas 2 使用指南:11、处理文本数据 Working with text data
Pandas 2 使用指南:12、处理缺失数据Working with missing data
Pandas 2 使用指南: 13、重复标签 Duplicate Labels
Pandas 2 使用指南:14、分类数据 Categorical data
Pandas 2 使用指南:15、可空整数数据类型、可空布尔数据类型
Pandas 2 使用指南:18、Groupby:拆分-应用-合并 split-apply-combine
Pandas 2 使用指南:19、窗口操作 Windowing operations
Pandas 2 使用指南:20、时间序列/日期功能
Pandas 2 使用指南:21、时间差 Timedelta
Pandas 2 使用指南:23、提升性能 Enhancing performance