Matplotlib を使ってハッチ付きのヒストグラムを作成する

PythonPythonBeginner
今すぐ練習

This tutorial is from open-source community. Access the source code

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

この実験では、Matplotlibを使ってハッチで塗りつぶされたヒストグラムを作成する方法を学びます。ヒストグラムは、棒グラフを使って数値データの頻度を示すデータのグラフ表示です。ハッチで塗りつぶされたヒストグラムは、棒が線、点、またはその他の記号のパターンで塗りつぶされたヒストグラムです。

VMのヒント

VMの起動が完了したら、左上隅をクリックしてノートブックタブに切り替えて、Jupyter Notebookを使って練習します。

時々、Jupyter Notebookが読み込み終わるまで数秒待つ必要があります。Jupyter Notebookの制限により、操作の検証を自動化できません。

学習中に問題に遭遇した場合は、Labbyにお問い合わせください。セッション後にフィードバックを提供してください。すぐに問題を解決いたします。

必要なライブラリをインポートする

この実験で必要なライブラリをインポートします。必要なライブラリは以下の通りです。

  • 乱数データを生成するためのnumpy
  • グラフを作成するためのmatplotlib.pyplot
  • 軸の目盛りの位置を設定するためのmatplotlib.ticker
  • スタイルサイクルを作成するためのcycler
  • 部分関数を作成するためのfunctools.partial
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cycler import cycler
from functools import partial

ヒストグラム関数を定義する

ヒストグラムを段階的なパッチとして描画する関数を定義します。この関数は以下のパラメータを取ります。

  • ax:描画するAxes
  • edges:各ビンの左辺と最後のビンの右辺を与える長さn + 1の配列
  • values:ビンのカウントまたは値の長さnの配列
  • bottoms:floatまたは配列(オプション)、棒の下端の長さnの配列。Noneの場合、0が使用されます。
  • orientation:文字列(オプション)、ヒストグラムの方向。'v'(既定値)は、棒が正のy方向に増加するようになっています。
def filled_hist(ax, edges, values, bottoms=None, orientation='v', **kwargs):
    """
    Draw a histogram as a stepped patch.

    Parameters
    ----------
    ax : Axes
        The axes to plot to

    edges : array
        A length n+1 array giving the left edges of each bin and the
        right edge of the last bin.

    values : array
        A length n array of bin counts or values

    bottoms : float or array, optional
        A length n array of the bottom of the bars.  If None, zero is used.

    orientation : {'v', 'h'}
       Orientation of the histogram.  'v' (default) has
       the bars increasing in the positive y-direction.

    **kwargs
        Extra keyword arguments are passed through to `.fill_between`.

    Returns
    -------
    ret : PolyCollection
        Artist added to the Axes
    """
    if orientation not in 'hv':
        raise ValueError(f"orientation must be in {{'h', 'v'}} not {orientation}")

    kwargs.setdefault('step', 'post')
    kwargs.setdefault('alpha', 0.7)
    edges = np.asarray(edges)
    values = np.asarray(values)
    if len(edges) - 1!= len(values):
        raise ValueError(f'Must provide one more bin edge than value not: {len(edges)=} {len(values)=}')

    if bottoms is None:
        bottoms = 0
    bottoms = np.broadcast_to(bottoms, values.shape)

    values = np.append(values, values[-1])
    bottoms = np.append(bottoms, bottoms[-1])
    if orientation == 'h':
        return ax.fill_betweenx(edges, values, bottoms, **kwargs)
    elif orientation == 'v':
        return ax.fill_between(edges, values, bottoms, **kwargs)
    else:
        raise AssertionError("you should never be here")

積み重ねヒストグラム関数を定義する

積み重ねヒストグラムを作成する関数を定義します。この関数は以下のパラメータを取ります。

  • ax:アーティストを追加するAxes
  • stacked_data:(M, N) の形状の配列。最初の次元を反復処理して、行方向にヒストグラムを計算します。
  • sty_cycle:各セットに適用するスタイルを表すCyclerまたは辞書の操作可能なオブジェクト
  • bottoms:配列、既定値: 0、下端の初期位置
  • hist_func:コール可能オブジェクト(オプション)。署名 bin_vals, bin_edges = f(data) を持たなければなりません。bin_edgesbin_vals よりも1つ長いことが期待されます。
  • labels:文字列のリスト(オプション)、各セットのラベル。指定されない場合、stacked_dataが配列の場合は 'default set {n}' が既定値となります。stacked_dataがマッピングで、labelsがNoneの場合はキーが既定値となります。stacked_dataがマッピングで、labelsが指定された場合は、列挙された列のみが描画されます。
  • plot_func:コール可能オブジェクト(オプション)、ヒストグラムを描画するために呼び出す関数。署名 ret = plot_func(ax, edges, top, bottoms=bottoms, label=label, **kwargs) を持たなければなりません。
  • plot_kwargs:辞書(オプション)、描画関数に渡す任意の追加のキーワード引数。これは、描画関数のすべての呼び出しに対して同じで、sty_cycle内の値を上書きします。
def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, hist_func=None, labels=None, plot_func=None, plot_kwargs=None):
    """
    Parameters
    ----------
    ax : axes.Axes
        The axes to add artists too

    stacked_data : array or Mapping
        A (M, N) shaped array.  The first dimension will be iterated over to
        compute histograms row-wise

    sty_cycle : Cycler or operable of dict
        Style to apply to each set

    bottoms : array, default: 0
        The initial positions of the bottoms.

    hist_func : callable, optional
        Must have signature `bin_vals, bin_edges = f(data)`.
        `bin_edges` expected to be one longer than `bin_vals`

    labels : list of str, optional
        The label for each set.

        If not given and stacked data is an array defaults to 'default set {n}'

        If *stacked_data* is a mapping, and *labels* is None, default to the
        keys.

        If *stacked_data* is a mapping and *labels* is given then only the
        columns listed will be plotted.

    plot_func : callable, optional
        Function to call to draw the histogram must have signature:

          ret = plot_func(ax, edges, top, bottoms=bottoms,
                          label=label, **kwargs)

    plot_kwargs : dict, optional
        Any extra keyword arguments to pass through to the plotting function.
        This will be the same for all calls to the plotting function and will
        override the values in *sty_cycle*.

    Returns
    -------
    arts : dict
        Dictionary of artists keyed on their labels
    """
    ## 既定のビニング関数を処理する
    if hist_func is None:
        hist_func = np.histogram

    ## 既定の描画関数を処理する
    if plot_func is None:
        plot_func = filled_hist

    ## 既定を処理する
    if plot_kwargs is None:
        plot_kwargs = {}

    try:
        l_keys = stacked_data.keys()
        label_data = True
        if labels is None:
            labels = l_keys

    except AttributeError:
        label_data = False
        if labels is None:
            labels = itertools.repeat(None)

    if label_data:
        loop_iter = enumerate((stacked_data[lab], lab, s) for lab, s in zip(labels, sty_cycle))
    else:
        loop_iter = enumerate(zip(stacked_data, labels, sty_cycle))

    arts = {}
    for j, (data, label, sty) in loop_iter:
        if label is None:
            label = f'dflt set {j}'
        label = sty.pop('label', label)
        vals, edges = hist_func(data)
        if bottoms is None:
            bottoms = np.zeros_like(vals)
        top = bottoms + vals
        sty.update(plot_kwargs)
        ret = plot_func(ax, edges, top, bottoms=bottoms, label=label, **sty)
        bottoms = top
        arts[label] = ret
    ax.legend(fontsize=10)
    return arts

固定ビンでヒストグラム関数を設定する

numpy.histogram を使って固定ビンでヒストグラム関数を設定します。-3から3までの範囲に20個のビンを作成します。

edges = np.linspace(-3, 3, 20, endpoint=True)
hist_func = partial(np.histogram, bins=edges)

スタイルサイクルを設定する

cycler を使ってヒストグラム用のスタイルサイクルを設定します。3つのスタイルサイクルを作成します。1つは面の色用、1つはラベル用、1つはハッチパターン用です。

color_cycle = cycler(facecolor=plt.rcParams['axes.prop_cycle'][:4])
label_cycle = cycler(label=[f'set {n}' for n in range(4)])
hatch_cycle = cycler(hatch=['/', '*', '+', '|'])

乱数データを生成する

numpy.random.randn を使って乱数データを生成します。12250個のポイントからなる4セットのデータを生成します。

np.random.seed(19680801)
stack_data = np.random.randn(4, 12250)

ハッチ付きのヒストグラムを作成する

先ほど定義した stack_hist 関数を使って、ハッチ付きのヒストグラムを作成します。先ほど定義した stack_datacolor_cycle、および hist_func を使用します。また、plot_kwargs には端の色と方向を含めるように設定します。

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True)
arts = stack_hist(ax1, stack_data, color_cycle + label_cycle + hatch_cycle, hist_func=hist_func)

arts = stack_hist(ax2, stack_data, color_cycle, hist_func=hist_func, plot_kwargs=dict(edgecolor='w', orientation='h'))
ax1.set_ylabel('counts')
ax1.set_xlabel('x')
ax2.set_xlabel('counts')
ax2.set_ylabel('x')

ラベル付きのハッチ付きヒストグラムを作成する

先ほど定義した stack_hist 関数を使って、ラベル付きのハッチ付きヒストグラムを作成します。先ほど定義した dict_datacolor_cycle、および hist_func を使用します。また、最初と最後のセットのみをプロットするために、labels['set 0','set 3'] に設定します。

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True, sharey=True)
dict_data = dict(zip((c['label'] for c in label_cycle), stack_data))
arts = stack_hist(ax1, dict_data, color_cycle + hatch_cycle, hist_func=hist_func)

arts = stack_hist(ax2, dict_data, color_cycle + hatch_cycle, hist_func=hist_func, labels=['set 0','set 3'])
ax1.xaxis.set_major_locator(mticker.MaxNLocator(5))
ax1.set_xlabel('counts')
ax1.set_ylabel('x')
ax2.set_ylabel('x')

まとめ

この実験では、Matplotlibを使ってハッチ付きのヒストグラムを作成する方法を学びました。2つの関数を定義しました。1つは階段状のパッチとしてヒストグラムを描画する filled_hist 関数と、積み重ねたヒストグラムを作成する stack_hist 関数です。また、numpy.histogram を使って固定ビンでヒストグラム関数を設定し、cycler を使ってヒストグラム用の3つのスタイルサイクルを定義しました。最後に、乱数データを生成し、stack_hist 関数を使って2つのハッチ付きのヒストグラムを作成しました。