MEP28:从 Axes.boxplot 中移除复杂性#
状态#
讨论
分支和拉取请求#
以下列出了与此 MEP 相关的任何开放 PR 或分支机构:
弃用冗余统计 kwargs
Axes.boxplot
:https ://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations弃用冗余样式选项
Axes.boxplot
:https ://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations不推荐将 2D NumPy 数组作为输入传递:无
将预处理和后处理选项添加到
cbook.boxplot_stats
:https ://github.com/phobson/matplotlib/tree/boxplot-stat-transformscbook.boxplot_stats
通过Axes.boxplot
kwargs暴露:无删除多余的统计 kwargs
Axes.boxplot
:无删除多余的样式选项
Axes.boxplot
:无通过讨论产生的剩余项目:无
摘要#
在过去的几个版本中,该Axes.boxplot
方法变得越来越复杂,以支持完全可定制的艺术家样式和统计计算。这导致Axes.boxplot
被分成多个部分。绘制箱线图所需的统计数据是在 中计算的
cbook.boxplot_stats
,而实际的艺术家是在 中绘制的Axes.bxp
。原始方法Axes.boxplot
仍然是最公开的 API,用于处理将用户提供的数据传递给cbook.boxplot_stats
、将结果提供给Axes.bxp
以及预处理箱线图每个方面的样式信息。
该 MEP 将概述一条前进的道路,以回滚增加的复杂性并简化 API,同时保持合理的向后兼容性。
详细说明#
目前,该Axes.boxplot
方法接受允许用户为将在图中绘制的每个框指定中值和置信区间的参数。提供这些是为了让高级用户可以提供以与 matplotlib 提供的简单方法不同的方式计算的统计数据。然而,处理这个输入需要复杂的逻辑来确保数据结构的形式与需要绘制的内容相匹配。目前,该逻辑包含 9 个单独的 if/else 语句,嵌套最多 5 层,带有一个 for 循环,最多可能引发 2 个错误。这些参数是在创建方法之前添加的,该Axes.bxp
方法从包含相关统计数据的字典列表中绘制箱线图。Matplotlib 还提供了一个计算这些统计数据的函数cbook.boxplot_stats
. 请注意,高级用户现在可以 a) 编写自己的函数来计算 所需的统计数据
Axes.bxp
,或 b) 修改返回的输出cbook.boxplots_stats
以完全自定义绘图艺术家的位置。凭借这种灵活性,仅手动指定中位数及其置信区间的参数保持向后兼容性。
大约在同一时间,这两个角色Axes.boxplot
被分为
cbook.boxplot_stats
计算和Axes.bxp
绘图,两者都
Axes.boxplot
被Axes.bxp
编写为接受单独切换箱线图所有组件的绘图的参数,以及单独配置这些艺术家风格的参数。但是,为了保持向后兼容性,保留了该sym
参数(以前用于指定传单的符号)。此参数本身需要相当复杂的逻辑来将sym
参数与新flierprops
参数以matplotlibrc
.
该 MEP 旨在为新手和高级用户等显着简化箱线图的创建。重要的是,这里提出的更改也将适用于 seaborn 等下游包,因为 seaborn 巧妙地允许用户通过 seaborn API 将任意参数字典传递给底层的 matplotlib 函数。
这将通过以下方式实现:
cbook.boxplot_stats
将被修改以允许传入计算前和计算后的转换函数(例如,np.log
对于np.exp
对数正态分布的数据)
Axes.boxplot
将被修改为也接受并天真地将它们传递给cbook.boxplots_stats
(Alt:传递 stat 函数及其可选参数的字典)。过时的参数 from
Axes.boxplot
将被弃用,稍后将被删除。
重要性#
由于晶须的限制是用算术计算的,因此在箱形和晶须图中存在一个隐含的正态性假设。这主要影响哪些数据点被归类为异常值。
如果已知数据不符合正态分布,则允许对数据和用于绘制箱线图的结果进行转换将允许用户选择退出该假设。
下面是一个示例,说明如何Axes.boxplot
根据这些类型的转换对对数正态数据的异常值进行不同的分类。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cbook
np.random.seed(0)
fig, ax = plt.subplots(figsize=(4, 6))
ax.set_yscale('log')
data = np.random.lognormal(-1.75, 2.75, size=37)
stats = cbook.boxplot_stats(data, labels=['arithmetic'])
logstats = cbook.boxplot_stats(np.log(data), labels=['log-transformed'])
for lsdict in logstats:
for key, value in lsdict.items():
if key != 'label':
lsdict[key] = np.exp(value)
stats.extend(logstats)
ax.bxp(stats)
fig.show()
实施#
将变换函数传递给cbook.boxplots_stats
#
该 MEP 建议将两个参数(例如,transform_in
添加
transform_out
到计算 boxplot 函数统计数据的食谱函数中。这些将是可选的仅关键字参数,并且可以在用户省略时轻松设置为无操作。当函数循环传递给它的每个数据子集时,
该函数将应用于数据。在计算统计字典列表后,该函数将应用于字典中的每个值。lambda x: x
transform_in
boxplot_stats
transform_out
然后可以将这些转换添加到调用签名中
Axes.boxplot
,而对该方法的复杂性几乎没有影响。这是因为它们可以直接传递给cbook.boxplot_stats
. 或者,Axes.boxplot
可以修改为接受可选的统计函数 kwarg 和要直接传递给它的参数字典。
此时在实现中,用户和 seaborn 等外部库将通过该Axes.boxplot
方法完全控制。更重要的是,至少,seaborn 不需要更改其 API 以允许用户利用这些新选项。
Axes.boxplot
API 和其他功能的简化#
简化箱线图方法主要包括弃用然后删除冗余参数。或者,下一步将包括纠正 和 之间的细微术语Axes.boxplot
不一致Axes.bxp
。
要弃用和删除的参数包括:
usermedians
- 由 10 个 SLOC、3if
个块、一个for
循环处理
conf_intervals
- 由 15 个 SLOC、6if
个块、一个for
循环处理
sym
- 由 12 个 SLOC 处理,4if
个块
删除该sym
选项允许将处理剩余样式参数的所有代码移至Axes.bxp
. 这并没有消除任何复杂性,但确实加强了 、 和 之间的单一Axes.bxp
责任cbook.boxplot_stats
原则Axes.boxplot
。
此外,notch
参数可以重命名shownotches
为与Axes.bxp
. 这种清理可以更进一步,whis
, bootstrap
,autorange
可以滚动到传递给新statfxn
参数的 kwargs 中。
向后兼容性#
此 MEP 的实施最终将导致向后不兼容的弃用,然后删除关键字参数
usermedians
、conf_intervals
和sym
。在 GitHub 上的粗略搜索表明usermedians
,conf_intervals
被少数用户使用,他们似乎都对 matplotlib 有非常深入的了解。一个强大的弃用周期应该为这些用户提供足够的时间来迁移到新的 API。
然而,弃用sym
可能对 matplotlib 用户群有更广泛的影响。
时间表#
加速时间线可能如下所示:
v2.0.1 添加转换
cbook.boxplots_stats
,暴露在Axes.boxplot
v2.1.0 Initial Deprecations ,并使用 2D NumPy 数组作为输入
使用 2D NumPy 数组作为输入。二维数组的语义通常令人困惑。
usermedians
,conf_intervals
,sym
参数
v2.2.0
删除
usermedians
,conf_intervals
,sym
参数弃用
notch
赞成shownotches
与其他参数和Axes.bxp
- v2.3.0
移除
notch
参数将所有风格和艺术家切换逻辑都转移到
Axes.bxp
这样Axes.boxplot
的地方,只不过是和之间的Axes.bxp
经纪人cbook.boxplots_stats
对用户的预期影响#
如上所述,弃用usermedians
并且conf_intervals
可能会影响少数用户。那些将受到影响的人几乎可以肯定是能够适应变化的高级用户。
弃用该sym
选项可能会引入更多用户,因此应努力收集社区对此的反馈。
对下游图书馆的预期影响#
检查了源代码(截至 2016 年 10 月 17 日的 GitHub 主文件)的 seaborn 和 python-ggplot 以查看这些更改是否会影响它们的使用。seaborn 没有使用本 MEP 中提名删除的参数。使用 matplotlib 的 boxplot 函数的 seaborn API 允许用户任意**kwargs
传递到 matplotlib 的 API。因此,安装了现代 matplotlib 的 seaborn 用户将能够充分利用由于此 MEP 添加的任何新功能。
Python-ggplot 已经实现了自己的功能来绘制箱线图。因此,实施本 MEP 不会对其产生任何影响。
替代方案#
主题变化#
这个 MEP 可以分为几个松散耦合的组件:
允许在计算前和计算后转换函数
cbook.boxplot_stats
Axes.boxplot
在API中公开该转换删除多余的统计选项
Axes.boxplot
将所有样式参数处理从 转移
Axes.boxplot
到Axes.bxp
.
使用这种方法,#2 依赖于#1,#4 依赖于#3。
#2 有两种可能的方法。第一个也是最直接的方法是镜像in的新参数transform_in
和transform_out
参数
并直接传递它们。cbook.boxplot_stats
Axes.boxplot
第二种方法是将statfxn
和statfxn_args
参数添加到Axes.boxplot
. 在此实现下,默认值为,但用户statfxn
可以cbook.boxplot_stats
传递自己的函数。然后transform_in
,transform_out
然后将作为statfxn_args
参数的元素传递。
def boxplot_stats(data, ..., transform_in=None, transform_out=None):
if transform_in is None:
transform_in = lambda x: x
if transform_out is None:
transform_out = lambda x: x
output = []
for _d in data:
d = transform_in(_d)
stat_dict = do_stats(d)
for key, value in stat_dict.item():
if key != 'label':
stat_dict[key] = transform_out(value)
output.append(d)
return output
class Axes(...):
def boxplot_option1(data, ..., transform_in=None, transform_out=None):
stats = cbook.boxplot_stats(data, ...,
transform_in=transform_in,
transform_out=transform_out)
return self.bxp(stats, ...)
def boxplot_option2(data, ..., statfxn=None, **statopts):
if statfxn is None:
statfxn = boxplot_stats
stats = statfxn(data, **statopts)
return self.bxp(stats, ...)
这两种情况都允许用户执行以下操作:
fig, ax1 = plt.subplots()
artists1 = ax1.boxplot_optionX(data, transform_in=np.log,
transform_out=np.exp)
但是选项二允许用户编写一个完全自定义的统计函数(例如my_box_stats
,
这在当前 API 下可用:
fig, ax1 = plt.subplots()
my_stats = my_box_stats(data, bootstrap_method='BCA',
whisker_method='dynamic')
ax1.bxp(my_stats)
并且使用选项二会更简洁
fig, ax = plt.subplots()
statopts = dict(transform_in=np.log, transform_out=np.exp)
ax.boxplot(data, ..., **statopts)
用户还可以传递他们自己的函数来计算统计数据:
fig, ax1 = plt.subplots()
ax1.boxplot(data, statfxn=my_box_stats, bootstrap_method='BCA',
whisker_method='dynamic')
从上面的例子来看,选项二似乎只有边际收益,但在像 seaborn 这样的下游库的上下文中,它的优势更加明显,因为在没有任何 seaborn 补丁的情况下,以下是可能的:
import seaborn
tips = seaborn.load_data('tips')
g = seaborn.factorplot(x="day", y="total_bill", hue="sex", data=tips,
kind='box', palette="PRGn", shownotches=True,
statfxn=my_box_stats, bootstrap_method='BCA',
whisker_method='dynamic')
这种类型的灵活性是将整个 boxplot API 拆分为当前三个函数的意图。然而,在实践中,像 seaborn 这样的下游库支持的 matplotlib 版本可以追溯到分裂之前。因此,只需增加一点灵活性,就
Axes.boxplot
可以通过现代 matplotlib 安装向下游库的用户公开所有功能,而无需下游库维护者的干预。
少做事#
另一个明显的替代方案是省略 和 中添加的计算前和计算后转换功能,cbook.boxplot_stats
并
Axes.boxplot
简单地删除如上所述的冗余统计和样式参数。
什么都不做#
与生活中的许多事情一样,在这里什么都不做是一种选择。这意味着我们只是提倡用户和下游库利用它们之间的分离cbook.boxplot_stats
,Axes.bxp
并让他们决定如何提供接口。