MEP28: Axes.boxplot から複雑さを取り除く#

ステータス番号

討論

ブランチとプルリクエスト#

以下は、この MEP に関連するオープンな PR またはブランチの一覧です。

  1. 冗長な統計 kwargs を廃止Axes.boxplot: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations

  2. 冗長なスタイル オプションを廃止Axes.boxplot: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations

  3. 2D NumPy 配列を入力として渡すことを廃止: なし

  4. 前処理および後処理オプションを追加cbook.boxplot_stats: https://github.com/phobson/matplotlib/tree/boxplot-stat-transforms

  5. kwargscbook.boxplot_statsによる公開Axes.boxplot: なし

  6. 冗長な統計クワーグを削除Axes.boxplot: なし

  7. の冗長なスタイル オプションを削除Axes.boxplot: なし

  8. その他の協議事項:なし

アブストラクト#

過去数回のリリースで、Axes.boxplotメソッドは複雑になり、完全にカスタマイズ可能なアーティストのスタイリングと統計計算をサポートするようになりました。これAxes.boxplotにより、複数の部分に分割されます。ボックスプロットを描画するために必要な統計は で計算され ますcbook.boxplot_statsが、実際のアーティストは で描画されAxes.bxpます。元のメソッドは、ユーザー提供のデータを に渡し、結果を に送り、ボックスプロット プロットの各ファセットのスタイル情報を前処理Axes.boxplotする最も一般的な API として残っています。cbook.boxplot_statsAxes.bxp

この MEP は、合理的な下位互換性を維持しながら、追加された複雑さをロールバックし、API を簡素化するためのパスの概要を示します。

詳細な説明#

現在、Axes.boxplotメソッドは、ユーザーがプロットに描画される各ボックスの中央値と信頼区間を指定できるようにするパラメーターを受け入れます。これらは、上級ユーザーが matplotlib によって提供される単純な方法とは異なる方法で計算された統計を提供できるように提供されました。ただし、この入力を処理するには、データ構造のフォームが描画する必要があるものと一致することを確認するための複雑なロジックが必要です。現時点では、そのロジックには for ループで最大 5 レベルの深さでネストされた 9 つの個別の if/else ステートメントが含まれており、最大 2 つのエラーが発生する可能性があります。これらのパラメーターはAxes.bxp、関連する統計を含む辞書のリストから箱ひげ図を描画するメソッドの作成前に追加されました。Matplotlib は、これらの統計を計算する関数も提供します。cbook.boxplot_stats. 上級ユーザーは、a) で必要な統計を計算する独自の関数を作成するか、 Axes.bxpb) で返される出力を変更してcbook.boxplots_stats 、プロットのアーティストの位置を完全にカスタマイズできるようになりました。この柔軟性により、中央値とその信頼区間のみを手動で指定するパラメーターは、下位​​互換性のために残されています。

の 2 つの役割が計算用と描画 用Axes.boxplotに分割され たのとほぼ同時期に、両方とも、箱ひげ図のすべてのコンポーネントの描画を個別に切り替えるパラメーターと、それらのアーティストのスタイルを個別に構成するパラメーターを受け入れるように記述されました。ただし、下位互換性を維持するために、パラメーター (以前はチラシのシンボルを指定するために使用されていた) は保持されました。このパラメータ自体は、 で指定されたデフォルト スタイルでパラメータを新しいパラメータと調整するために、かなり複雑なロジックを必要とします。cbook.boxplot_statsAxes.bxpAxes.boxplotAxes.bxpsymsymflierpropsmatplotlibrc

この MEP は、初心者から上級者まで、箱ひげ図の作成を劇的に簡素化することを目的としています。重要なことに、ここで提案されている変更は、seaborn のようなダウンストリーム パッケージでも利用できます。seaborn では、ユーザーが seaborn API を介してパラメーターの任意の辞書を基礎となる matplotlib 関数にスマートに渡すことができるためです。

これは、次の方法で実現されます。

  1. cbook.boxplot_stats計算前および計算後の変換関数を渡すことができるように変更されます (たとえば、np.log 対数np.exp正規分布データの場合) 。

  2. Axes.boxplotまた、それらを受け入れて単純に渡すように変更されますcbook.boxplots_stats(Alt: stat 関数とそのオプションのパラメーターの dict を渡します)。

  3. の古いパラメータは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()

(ソースコードpng )

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

実装#

変換関数をcbook.boxplots_stats#に渡す

この MEP は、2 つのパラメーター (たとえば、boxplot 関数の統計を計算するクックブック関数に追加することを提案しています。これらはオプションのキーワードのみの引数であり、ユーザーが省略した場合はノーオペレーションとして簡単に設定できtransform_inます 。 The function will be applied to the data as the function loops through each subnet of the data passed to it. 統計辞書のリストが計算された後、 関数は辞書の各値に適用されます。transform_outlambda x: xtransform_inboxplot_statstransform_out

Axes.boxplotこれらの変換は、そのメソッドの複雑さにほとんど影響を与えずに、 の呼び出しシグネチャに追加できます 。これは、 に直接渡すことができるためcbook.boxplot_statsです。あるいは、Axes.boxplotオプションの統計関数 kwarg とそれに直接渡されるパラメーターのディクショナリを受け入れるように変更することもできます。

実装のこの時点で、ユーザーと seaborn などの外部ライブラリは、Axes.boxplotメソッドを介して完全に制御できます。さらに重要なことは、少なくとも、ユーザーがこれらの新しいオプションを利用できるようにするために、seaborn はその API を変更する必要がないということです。

Axes.boxplotAPI およびその他の機能の簡素化#

boxplot メソッドを単純化するには、主に、冗長なパラメーターを非推奨にしてから削除する必要があります。Axes.boxplot オプションで、次のステップには、との間のマイナーな用語の不一致の修正が含まれAxes.bxpます。

廃止および削除されるパラメータには、次のものがあります。

  1. usermedians- 10 SLOC、3ifブロック、forループで処理

  2. conf_intervals- 15 SLOC、6ifブロック、forループで処理

  3. sym- 12 SLOC、4ifブロックで処理

オプションを削除するとsym、残りのスタイリング パラメータを処理するすべてのコードを に移動できますAxes.bxp。これによって複雑さが解消されるわけではありませんがAxes.bxp、 、cbook.boxplot_stats、および間の単一責任の原則が強化されAxes.boxplotます。

さらに、notchパラメータの名前shownotches を と一致するように変更できますAxes.bxp。この種のクリーンアップをさらに一歩進めて、whis, bootstrap,autorangeを新しいパラメーターに渡される kwargs にまとめることができstatfxnます。

下位互換性#

この MEP の実装は、最終的に下位互換性のない非推奨となり、キーワード パラメータ usermediansconf_intervals、およびが削除されますsym。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 配列を入力として使用します。2D 配列に関するセマンティクスは、一般的にわかりにくいものです。

    2. usermediansconf_intervalssymパラメータ

  3. v2.2.0

    1. usermediansconf_intervalssymパラメータを削除

    2. 他のパラメーターとの一貫性を維持notchすることを優先して非推奨にし、shownotchesAxes.bxp

  4. v2.3.0
    1. notchパラメータを削除

    2. すべてのスタイルとアーティストのトグル ロジックをAxes.bxpそのようなものに移動することは、とAxes.boxplot の間のブローカーに過ぎません。Axes.bxpcbook.boxplots_stats

ユーザーへの予想される影響#

上記のように非推奨usermediansであり、conf_intervals ほとんどのユーザーに影響を与える可能性があります。影響を受けるのは、ほぼ確実に、変化に適応できる上級ユーザーです。

このオプションを廃止すると、symより多くのユーザーがインポートされる可能性があり、これに関するコミュニティのフィードバックを収集するために努力する必要があります。

ダウンストリーム ライブラリへの予想される影響#

ソース コード (2016 年 10 月 17 日現在の GitHub マスター) は、seaborn と python-ggplot について検査され、これらの変更がそれらの使用に影響を与えるかどうかが確認されました。この MEP で削除対象として指定されたパラメーターは、seaborn によって使用されていません。matplotlib の boxplot 関数を使用する seaborn API により、ユーザーは任意のパス**kwargsを matplotlib の API に渡すことができます。したがって、最新の matplotlib インストールを使用する海生まれのユーザーは、この MEP の結果として追加された新機能を最大限に活用できます。

Python-ggplot には、boxplot を描画する独自の関数が実装されています。したがって、この MEP の実装による影響はありません。

代替案#

テーマのバリエーション#

この MEP は、いくつかの疎結合コンポーネントに分割できます。

  1. 計算前および計算後の変換機能を許可するcbook.boxplot_stats

  2. Axes.boxplotAPIでその変換を公開する

  3. の冗長な統計オプションを削除するAxes.boxplot

  4. すべてのスタイリング パラメータ処理を からAxes.boxplotにシフトしますAxes.bxp

このアプローチでは、#2 は #1 に依存し、#4 は #3 に依存します。

#2には2つの可能なアプローチがあります。最初の最も直接的な方法は、 in の新しいパラメータとパラメータをミラーリングしてtransform_in直接transform_out渡す ことです。cbook.boxplot_statsAxes.boxplot

2 番目のアプローチは、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)

しかし、オプション 2 では、ユーザーは完全にカスタムの 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)

そして、オプション2でより簡潔になります

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

上記の例から、オプション 2 にはわずかな利点しかないように見えますが、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 全体を現在の 3 つの関数に分割する意図です。ただし、実際には、seaborn のようなダウンストリーム ライブラリは、分割のかなり前にさかのぼる matplotlib のバージョンをサポートしています。したがって、にもう少し柔軟性を追加すると Axes.boxplot、ダウンストリーム ライブラリのメンテナーからの介入なしに、最新の matplotlib インストールを使用してダウンストリーム ライブラリのユーザーにすべての機能を公開できます。

少ないこと#

もう 1 つの明白な代替案は、 および で追加された計算前および計算後の変換機能を省略し、cbook.boxplot_stats上記 Axes.boxplotのように冗長な統計およびスタイル パラメータを単純に削除することです。

何もしない#

人生の多くのことと同様に、ここでは何もしないという選択肢があります。これは、ユーザーと下流のライブラリが と の間の分割を利用して、cbook.boxplot_statsそのAxes.bxpインターフェイスを提供する方法を決定できるようにすることを単純に提唱することを意味します。