Style¶
版本0.17.1中的新功能
临时:这是一个新功能,仍在开发中。我们将添加功能,并可能在未来的版本中进行突破性更改。我们很乐意听取您的反馈。 t>
本文档是作为Jupyter Notebook编写的,可以在此处查看或下载。
您可以使用DataFrame.style
属性,应用条件格式(根据数据内部的数据确定DataFrame的视觉样式)。这是一个返回pandas.Styler
对象的属性,它具有用于格式化和显示DataFrames的有用方法。
样式是使用CSS完成的。您可以编写取得标量DataFrame
或系列
的“样式函数”,并返回like-indexed DataFrames或Series with CSS 属性:值“
对。这些函数可以递增地传递到样式器
,样式器在渲染之前收集样式。
Contents¶
Building Styles¶
将您的样式函数传递到以下方法之一:
Styler.applymap
:elementwiseStyler.apply
:column- / row- / table-wise
这两个方法都有一个函数(和一些其他关键字参数),并以某种方式将你的函数应用到DataFrame。Styler.applymap
按元素方式处理DataFrame。Styler.apply
根据axis
关键字参数,将每个列或行一次性传递到您的DataFrame或整个表格。对于逐列使用axis = 0
,按行使用axis = 1
,并且对于整个表立即使用axis = None
。
对于Styler.applymap
,您的函数应采用标量,并返回一个具有CSS属性值对的字符串。
对于Styler.apply
,您的函数应采用Series或DataFrame(取决于轴参数),并返回具有相同形状的Series或DataFrame,其中每个值都是具有CSS属性值对的字符串。
让我们看一些例子。
import pandas as pd
import numpy as np
np.random.seed(24)
df = pd.DataFrame({'A': np.linspace(1, 10, 10)})
df = pd.concat([df, pd.DataFrame(np.random.randn(10, 4), columns=list('BCDE'))],
axis=1)
df.iloc[0, 2] = np.nan
这里是一个无聊的例子,渲染一个DataFrame,没有任何(可见)样式:
df.style
注意:DataFrame.style
属性是返回Styler
对象的propetry。样式器
具有定义的_repr_html _
方法,因此它们会自动呈现。如果你想要实际的HTML返回进一步处理或写入文件调用.render()
方法返回一个字符串。
上面的输出看起来非常类似于标准的DataFrame HTML表示。但是我们在幕后做了一些工作,将CSS类附加到每个单元格。我们可以通过调用.render
方法查看这些。
df.style.highlight_null().render().split('\n')[:10]
row0_col2
是特定单元格的标识符。我们还为每个行/列标识符添加了每个DataFrame唯一的UUID,以便一个样式不会与同一笔记本或页面中另一个样式发生冲突(您可以设置uuid t0 >如果你想把两个DataFrames的样式绑在一起)。
在编写样式函数时,您需要处理生成所需的CSS属性/值对。Pandas匹配那些标识每个单元格的CSS类。
让我们写一个简单的样式函数,将负数红色和正数黑色。
def color_negative_red(val):
"""
Takes a scalar and returns a string with
the css property `'color: red'` for negative
strings, black otherwise.
"""
color = 'red' if val < 0 else 'black'
return 'color: %s' % color
在这种情况下,单元格的样式仅取决于它自己的值。这意味着我们应该使用元素方式工作的Styler.applymap
方法。
s = df.style.applymap(color_negative_red)
s
注意与标准df.applymap
的相似性,它在元素方面对DataFrames进行操作。我们希望您能够重新使用您现有的如何与DataFrames交互的知识。
还要注意,我们的函数返回一个包含CSS属性和值的字符串,用冒号分隔,就像在&lt; style&gt;
标签中。这将是一个共同的主题。
最后,输入形状匹配。Styler.applymap
在每个标量输入上调用函数,函数返回标量输出。
现在假设您想突出显示每列中的最大值。我们不能使用.applymap
,因为它是按元素操作的。相反,我们将转到.apply
,它按列方式操作(或使用轴
关键字按行操作)。稍后我们会看到highlight_max
已在Styler
上定义,因此您不需要自己编写。
def highlight_max(s):
'''
highlight the maximum in a Series yellow.
'''
is_max = s == s.max()
return ['background-color: yellow' if v else '' for v in is_max]
df.style.apply(highlight_max)
在这种情况下,输入为Series
,一次一列。请注意,highlight_max
的输出形状与输入形状匹配,即具有len(s)
项的数组。
我们鼓励您使用方法链在分段式构建样式,最后在链的末尾呈现。
df.style.\
applymap(color_negative_red).\
apply(highlight_max)
上面我们使用Styler.apply
每次传递一列一个。
*Debugging Tip*: If you're having trouble writing your style function, try just passing it into DataFrame.apply
. 在内部,Styler.apply
使用DataFrame.apply
,因此结果应该相同。
如果您想突出显示整个表中的最大值,该怎么办?使用.apply(function,axis = None)
来表示您的函数需要整个表,而不是一次一列或一行。让我们试试下。
我们将改写highlight-max
来处理Series(从.apply(axis = 0或1)
)或DataFrames(从.apply无)
)。我们还将允许颜色可调,以演示.apply
和.applymap
传递关键字参数。
def highlight_max(data, color='yellow'):
'''
highlight the maximum in a Series or DataFrame
'''
attr = 'background-color: {}'.format(color)
if data.ndim == 1: # Series from .apply(axis=0) or axis=1
is_max = data == data.max()
return [attr if v else '' for v in is_max]
else: # from .apply(axis=None)
is_max = data == data.max().max()
return pd.DataFrame(np.where(is_max, attr, ''),
index=data.index, columns=data.columns)
当使用Styler.apply(func,axis = None)
时,函数必须返回具有相同索引和列标签的DataFrame。
df.style.apply(highlight_max, color='darkorange', axis=None)
Building Styles Summary¶
样式函数应返回带有一个或多个CSS 属性的字符串:value
以分号分隔。使用
Styler.applymap(func)
用于元素样式Styler.apply(func,axis = 0)
用于列方式Styler.apply(func,axis = 1)
用于行式样式Styler.apply(func,axis = None)
用于表格样式
至关重要的是,func
的输入和输出形状必须匹配。如果x
是输入,则func(x).shape == x.shape
。
Finer Control: Slicing¶
Styler.apply
和Styler.applymap
都接受子集
关键字。这样,您就可以将样式应用于特定的行或列,而无需将该逻辑编写到样式
函数中。
传递给子集
的值与切分DataFrame类似。
- 标量被视为列标签
- 列表(或系列或numpy数组)
- 元组被视为
(row_indexer,column_indexer)
考虑使用pd.IndexSlice
来构造最后一个的元组。
df.style.apply(highlight_max, subset=['B', 'C', 'D'])
对于行和列切片,.loc
的任何有效索引器将工作。
df.style.applymap(color_negative_red,
subset=pd.IndexSlice[2:5, ['B', 'D']])
现在只支持基于标签的切片,而不是位置。
如果您的样式函数使用子集
或轴
关键字参数,请考虑在functools.partial
中包装函数,以分离出该关键字。
my_func2 = functools.partial(my_func, subset=42)
df.style.format("{:.2%}")
使用字典格式化特定列。
df.style.format({'B': "{:0<4.0f}", 'D': '{:+.2f}'})
或者传递一个callable(或callables的字典)以更灵活的处理。
df.style.format({"B": lambda x: "±{:.2f}".format(abs(x))})
Builtin Styles¶
最后,我们期望某些样式函数是公用的,因为我们已经在Styler
中包含了一些“内置”,所以你不必自己编写它们。
df.style.highlight_null(null_color='red')
您可以使用background_gradient
方法创建“热图”。这些需要matplotlib,我们将使用Seaborn来获得一个很好的色彩映射。
import seaborn as sns
cm = sns.light_palette("green", as_cmap=True)
s = df.style.background_gradient(cmap=cm)
s
Styler.background_gradient
使用关键字参数低
和高
。粗略地说,这些扩展了低
和高 t>百分比的数据范围,以便在我们转换颜色时,不使用色彩映射的整个范围。
这是有用的,这样你可以读取文本仍然。
# Uses the full color range
df.loc[:4].style.background_gradient(cmap='viridis')
# Compreess the color range
(df.loc[:4]
.style
.background_gradient(cmap='viridis', low=.5, high=0)
.highlight_null('red'))
您可以在DataFrame中包括“条形图”。
df.style.bar(subset=['A', 'B'], color='#d65f5f')
还有.highlight_min
和.highlight_max
。
df.style.highlight_max(axis=0)
df.style.highlight_min(axis=0)
当样式实际上不依赖于值时,请使用Styler.set_properties
。
df.style.set_properties(**{'background-color': 'black',
'color': 'lawngreen',
'border-color': 'white'})
Sharing Styles¶
假设你有一个可爱的样式为DataFrame构建,现在你想要应用相同的样式到第二个DataFrame。使用df1.style.export
导出样式,并使用df1.style.set
在第二个DataFrame上导入样式
df2 = -df
style1 = df.style.applymap(color_negative_red)
style1
style2 = df2.style
style2.use(style1.export())
style2
请注意,您可以共享样式,即使它们是数据感知。在他们使用
的新DataFrame上重新评估样式。
Other options¶
你已经看到了几种数据驱动的样式方法。Styler
还为不依赖于数据的样式提供了一些其他选项。
- 精确
- 字幕
- 表格样式
这些都可以通过两种方式指定:
pandas.core.Styler
的关键字参数- 调用
.set _
方法之一,例如.set_caption
使用的最佳方法取决于上下文。在构建许多应该都具有相同属性的样式化DataFrame时,使用Styler
构造函数。对于交互式使用,.set _
方法更方便。
Precision¶
您可以使用pandas的常规display.precision
选项来控制浮点数的精度。
with pd.option_context('display.precision', 2):
html = (df.style
.applymap(color_negative_red)
.apply(highlight_max))
html
或者通过set_precision
方法。
df.style\
.applymap(color_negative_red)\
.apply(highlight_max)\
.set_precision(2)
设置精度只影响打印数量;全精度值总是传递给您的样式函数。如果您想从头开始,可以使用df.round(2).style
。
Captions¶
可以通过几种方式添加常规表格标题。
df.style.set_caption('Colormaps, with a caption.')\
.background_gradient(cmap=cm)
Table Styles¶
下一个选项是“表样式”。这些是应用于整个表的样式,但不要查看数据。某些交换,包括像:hover
这样的伪选择器只能以这种方式使用。
from IPython.display import HTML
def hover(hover_color="#ffff99"):
return dict(selector="tr:hover",
props=[("background-color", "%s" % hover_color)])
styles = [
hover(),
dict(selector="th", props=[("font-size", "150%"),
("text-align", "center")]),
dict(selector="caption", props=[("caption-side", "bottom")])
]
html = (df.style.set_table_styles(styles)
.set_caption("Hover to highlight."))
html
table_styles
应该是字典列表。每个字典应该有选择符
和道具
键。selector
的值应为有效的CSS选择器。请记住,所有样式都已附加到id
,每个样式器
都是唯一的。此选择器除了id
。props
的值应该是('attribute','value')
的元组列表。
table_styles
非常灵活,但没有手动输入的乐趣。我们希望在大熊猫中收集一些有用的内容,或者在这里建立的工具的新包中优先选择。
CSS Classes¶
某些CSS类附加到单元格。
- Index and Column names include
index_name
andlevel<k>
wherek
is its level in a MultiIndex - 索引标签单元格包括
row_heading
row&lt; n&gt;
其中n
是行的数字位置level&lt; k&gt;
其中k
是MultiIndex中的级别
- 柱标记细胞包括
col_heading
col&lt; n&gt;
其中n
是列的数字位置level&lt; k&gt;
其中k
是MultiIndex中的级别
- 空白单元格包括
blank
- 数据单元包括
数据
Limitations¶
- DataFrame仅
(使用Series.to_frame()。style)
- 索引和列必须是唯一的
- 没有大的repr,和性能不是很大;这是为了概要DataFrames
- 您只能设置值的样式,而不是索引或列
- 您只能应用样式,不能插入新的HTML实体
其中一些将在未来得到解决。
Terms¶
- 样式函数:传递到
Styler.apply
或Styler.applymap
并返回像'css attribute:value'
- 内置样式函数:样式函数是
Styler
上的方法 - 表格样式:具有两个键
选择器
和道具
的字典。selector t>是
props
将应用于的CSS选择器。props
是(属性,值)
元组的列表。传递到Styler
中的表格样式列表。
from IPython.html import widgets
@widgets.interact
def f(h_neg=(0, 359, 1), h_pos=(0, 359), s=(0., 99.9), l=(0., 99.9)):
return df.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)
df = pd.DataFrame(np.random.randn(20, 25)).cumsum()
df.style.background_gradient(cmap, axis=1)\
.set_properties(**{'max-width': '80px', 'font-size': '1pt'})\
.set_caption("Hover to magify")\
.set_precision(2)\
.set_table_styles(magnify())
Extensibility¶
熊猫的核心是,并将继续是其“高性能,易于使用的数据结构”。考虑到这一点,我们希望DataFrame.style
实现两个目标
- 提供一个令人愉快的交互式API,并且对于许多任务都“足够好”
- 为专用图书馆提供基础
如果您在此之上建立了一个优秀的图书馆,请告诉我们,我们将链接到它。
Subclassing¶
本节包含有关Styler
实施的信息。由于功能是如此新,所有这一切都可能改变,甚至比最终使用的API。
当用户应用样式时(通过.apply
,.applymap
或其中一个内置函数),我们实际上不计算任何内容。相反,我们将函数和参数附加到列表self._todo
。当询问(通常在.render
中),我们将遍历列表并执行每个函数(这在self._compute()
中。这些函数更新内部defaultdict(list)
,self.ctx
,它将DataFrame行/列位置映射到CSS属性值对。
我们通过self._todo
采取额外的步骤,以便我们可以导出样式并将其设置在其他Styler
上。
呈现使用Jinja模板。.translate
方法采用self.ctx
并构建另一个字典,准备传递到Styler.template.render
,Jinja模板。
Alternate templates¶
我们使用Jinja模板来构建HTML。模板存储为类变量Styler.template。
。子类可以覆盖它。
class CustomStyle(Styler):
template = Template("""...""")