はじめに
この実験では、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:描画する Axesedges:各ビンの左辺と最後のビンの右辺を与える長さ 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:アーティストを追加する Axesstacked_data:(M, N) の形状の配列。最初の次元を反復処理して、行方向にヒストグラムを計算します。sty_cycle:各セットに適用するスタイルを表す Cycler または辞書の操作可能なオブジェクトbottoms:配列、既定値:0、下端の初期位置hist_func:コール可能オブジェクト(オプション)。署名bin_vals, bin_edges = f(data)を持たなければなりません。bin_edgesはbin_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_data、color_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_data、color_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 つのハッチ付きのヒストグラムを作成しました。