MEP28:从axes.boxplot中删除复杂性

状态

Discussion

分支和请求

以下列出了与此MEP相关的所有打开的PRS或分支:

  1. Axes.boxplothttps://github.com/phobson/matplotlib/tree/mep28-initial-deprecations
  2. 在中取消预测冗余样式选项 Axes.boxplothttps://github.com/phobson/matplotlib/tree/mep28-initial-deprecations
  3. 不推荐将passings 2D NumPy数组作为输入:无
  4. 将预处理和后处理选项添加到 cbook.boxplot_statshttps://github.com/phobson/matplotlib/tree/boxplot-stat-transforms
  5. 曝光 cbook.boxplot_stats 通过 Axes.boxplot 克沃斯:没有
  6. 删除多余的统计知识 Axes.boxplot 没有
  7. 删除中的冗余样式选项 Axes.boxplot 没有
  8. 通过讨论产生的剩余项目:无

摘要

在过去的几个版本中, Axes.boxplot 该方法的复杂性不断增加,以支持完全可定制的艺术家样式和统计计算。这导致 Axes.boxplot 分成多个部分。绘制箱线图所需的统计数据计算在 cbook.boxplot_stats 而真正的艺术家是由 Axes.bxp . 原来的方法, Axes.boxplot 仍然是处理将用户提供的数据传递给 cbook.boxplot_stats ,将结果提供给 Axes.bxp 以及对箱线图每个方面的样式信息进行预处理。

该MEP将概述一条向前的路径,以回滚增加的复杂性并简化API,同时保持合理的向后兼容性。

详细描述

目前, Axes.boxplot 方法接受允许用户为将在绘图中绘制的每个框指定中间值和置信区间的参数。提供这些数据是为了使avdanced用户能够以与matplotlib提供的简单方法不同的方式提供统计数据。然而,处理这个输入需要复杂的逻辑,以确保数据结构的形式与需要绘制的内容相匹配。目前,该逻辑包含9个单独的if/else语句,它们用for循环嵌套最多5层,并可能引发最多2个错误。这些参数是在创建 Axes.bxp 方法,该方法从包含相关统计信息的字典列表中绘制箱线图。matplotlib还提供了一个函数,该函数通过 cbook.boxplot_stats . 注意,高级用户现在可以a)编写自己的函数来计算 Axes.bxp ,或b)修改返回的输出 cbook.boxplots_stats 完全自定义绘图艺术家的位置。有了这种灵活性,手动指定媒体及其信任间隔的参数将保持向后兼容性。

大约在同一时间, Axes.boxplot 被分成 cbook.boxplot_stats 用于计算和 Axes.bxp 用于绘图,两者 Axes.boxplotAxes.bxp 这些参数分别用于切换箱线图所有组件的绘图,以及分别配置这些艺术家样式的参数。但是,为了保持向后兼容性,需要 sym 参数(以前用于指定传单的符号)被保留。这个参数本身需要相当复杂的逻辑来协调 sym 参数更新 flierprops 参数的默认样式由指定 matplotlibrc .

该MEP旨在显著简化为新手和高级用户创建箱线图的过程。重要的是,这里提出的更改也可用于下游包,如seaborn,因为seaborn智能地允许用户通过seaborn API将任意的参数字典传递给底层matplotlib函数。

这将通过以下方式实现:

  1. cbook.boxplot_stats 将被修改以允许传入计算前和计算后转换函数(例如, np.lognp.exp 对于对数正态分布数据)
  2. Axes.boxplot 将被修改为也接受并且不完全将其传递给 cbook.boxplots_stats (alt:传递stat函数及其可选参数的dict)。
  3. 来自的过期参数 Axes.boxplot 将被弃用,稍后删除。

重要性

由于晶须的极限是用算术方法计算出来的,因此盒和晶须图中存在一个隐式的正态性假设。这主要影响哪些数据点被归类为异常值。

如果已知数据不符合正态分布,允许对数据和绘制箱线图的结果进行转换将允许用户选择退出该假设。

下面是一个如何 Axes.boxplot 根据这些类型的转换,对lognormal数据的离群值进行不同的分类。

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()

(Source code _, pngpdf

../../_images/MEP28-1.png

实施

将转换函数传递到 cbook.boxplots_stats

本MEP提出两个参数(例如, transform_intransform_out 添加到计算boxplot函数统计信息的cookbook函数中。这些将是仅限关键字的可选参数,可以轻松设置为 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.boxplotAxes.bxp .

要弃用和删除的参数包括:

  1. usermedians -由10个sloc处理,3个 if 块,A for
  2. conf_intervals -由15个SLOC处理,6个 if 块,A for
  3. sym -由12个sloc处理,4个 if 阻碍

去掉 sym 选项允许将处理剩余样式参数的所有代码移到 Axes.bxp . 这并没有消除任何复杂性,但强化了 Axes.bxpcbook.boxplot_statsAxes.boxplot .

另外, notch 可以重命名参数 shownotches 与…一致 Axes.bxp . 这种清理工作可以进一步进行, whisbootstrapautorange 可以卷进新的禁运区 statfxn 参数。

向后兼容性

此MEP的实现最终将导致向后不兼容的拒绝,然后删除关键字参数。 usermediansconf_intervalssym . 对Github的粗略搜索表明 usermediansconf_intervals 很少有用户使用,他们似乎都对matplotlib有很强的了解。一个强大的反预测周期应该为这些用户提供足够的时间迁移到一个新的API。

贬低 sym 然而,在Matplotlib用户库中可能有更广泛的范围。

地铁列车时刻表

加速的时间表可能如下所示:

  1. V2.0.1将转换添加到 cbook.boxplots_stats 暴露在 Axes.boxplot

  2. v2.1.0初始不推荐使用,并使用2D NumPy数组作为输入

    1. 使用2D NumPy数组作为输入。二维数组的语义通常令人困惑。
    2. usermediansconf_intervalssym 参数
  3. v2.2.0

    1. 去除 usermediansconf_intervalssym 参数
    2. 贬低 notch 赞成 shownotches 与其他参数保持一致 Axes.bxp
  4. v2.3.0
    1. 去除 notch 参数
    2. 将所有样式和艺术家切换到逻辑 Axes.bxp 这样的 Axes.boxplot 只是一个中间人 Axes.bxpcbook.boxplots_stats

对用户的预期影响

如上所述,折旧 usermediansconf_intervals 可能对少数用户产生影响。受影响的用户几乎肯定是能够适应变化的高级用户。

贬低 sym 这个选项可能会导入更多的用户,应该努力收集社区对此的反馈。

对下游类库的预期影响

对源代码(截至2016-10-17的github master)进行了seaborn和python ggplot检查,以确定这些更改是否会影响它们的使用。Seaborn不使用本MEP中指定要删除的参数。使用matplotlib的boxplot函数的seaborn API允许用户任意传递 **kwargs 到Matplotlib的API。因此,具有现代Matplotlib安装的Seaborn用户将能够充分利用由于该MEP而添加的任何新功能。

python ggplot已经实现了自己的函数来绘制箱线图。因此,执行此MEP不会对其产生任何影响。

选择

主题变化

该MEP可分为几个松散耦合的组件:

  1. 允许计算前和计算后转换函数 cbook.boxplot_stats
  2. Axes.boxplot API
  3. 删除中的冗余统计选项 Axes.boxplot
  4. 将所有样式参数处理从 Axes.boxplotAxes.bxp .

通过这种方法,2依赖于1,而4依赖于3。

对于2有两种可能的方法。第一个也是最直接的方法是模仿新的 transform_intransform_out 参数 cbook.boxplot_stats 在里面 Axes.boxplot 直接通过。

第二种方法是增加 statfxnstatfxn_args 参数到 Axes.boxplot . 在这个实现中,默认值为 statfxn 将是 cbook.boxplot_stats ,但用户可以传递自己的函数。然后 transform_intransform_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)

但是选项二允许用户编写一个完全自定义的stat函数(例如, my_box_stats )根据数据的某些属性,BCA置信区间和胡须设置不同。

这在当前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_statsAxes.boxplot ,并简单地删除上面描述的冗余统计和样式参数。

无所事事

就像生活中的许多事情一样,在这里什么都不做是一种选择。这意味着我们只是提倡用户和下游库利用 cbook.boxplot_statsAxes.bxp 让他们决定如何提供一个接口。