Matplotlib ヒストグラム:Pythonにおけるplt.hist()の完全ガイド
Updated on
何千もの数値データ(年齢、テストの点数、応答時間、センサーの読み取り値など)を含むデータセットがあり、それらの値がどのように分布しているかを理解する必要があるとします。中心点の周りに集中していますか?片方の端に偏っていますか?正規分布に従っていますか?散布図では役に立ちません。棒グラフはカテゴリデータ向けであり、連続データ向けではありません。必要なのはヒストグラムであり、Pythonではmatplotlib.pyplot.hist()がそれを作成する標準的な方法です。
問題は、plt.hist()には12以上のパラメータがあり、デフォルトの出力はしばしば地味で誤解を招くように見えることです。ビンの数を誤って選択すると、データの重要なパターンが隠れてしまう可能性があります。1つのチャートで複数の分布を比較するには、正しいオプションの組み合わせを知る必要があります。このガイドでは、重要なすべてのパラメータを、ノートブックやスクリプトに直接コピーできる実用的なコード例とともに解説します。
ヒストグラムとは何か、いつ使うべきか?
ヒストグラムは、数値の範囲をビンと呼ばれる等幅の区間に分割し、各ビンに含まれるデータポイントの数をカウントします。x軸は値の範囲を示し、y軸は各ビンの頻度(カウント)または密度を示します。カテゴリデータを表示する棒グラフとは異なり、ヒストグラムは連続的な数値データの分布を表します。
ヒストグラムは以下の場合に使用します:
- 分布の形状を確認する(正規、歪み、二峰性、一様)
- データの外れ値やギャップを特定する
- グループ間の値の広がりを比較する
- モデリング前のデータ変換を決定する
基本的なplt.hist()の構文
最もシンプルなヒストグラムは、データ配列という1つの引数のみを必要とします。
import matplotlib.pyplot as plt
import numpy as np
# Generate 1000 normally distributed values
np.random.seed(42)
data = np.random.normal(loc=50, scale=15, size=1000)
plt.hist(data)
plt.title('Basic Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()デフォルトでは、matplotlibはデータを10個のビンに分割します。この関数は3つのオブジェクトを返します:ビンのカウント、ビンの境界、パッチオブジェクト(描画された長方形)です。これらの返り値については後で詳しく説明します。
完全なシグネチャ
plt.hist(x, bins=None, range=None, density=False, weights=None,
cumulative=False, bottom=None, histtype='bar', align='mid',
orientation='vertical', rwidth=None, log=False, color=None,
label=None, stacked=False, edgecolor=None, alpha=None)ビンの制御
binsパラメータはヒストグラムにおいて最も重要な設定です。ビンが少なすぎるとパターンが隠れます。ビンが多すぎるとノイズが生じます。
固定数のビンを設定する
fig, axes = plt.subplots(1, 3, figsize=(14, 4))
axes[0].hist(data, bins=5, edgecolor='black')
axes[0].set_title('5 Bins')
axes[1].hist(data, bins=30, edgecolor='black')
axes[1].set_title('30 Bins')
axes[2].hist(data, bins=100, edgecolor='black')
axes[2].set_title('100 Bins')
plt.tight_layout()
plt.show()5ビンでは大まかな形しか見えません。100ビンでは、ビンあたりのサンプルサイズが小さくなり、視覚的なノイズが生じます。この1,000ポイントのデータセットでは、30ビンが正規分布の明確な像を生成します。
カスタムビン境界
binsにシーケンスを渡して正確な境界を定義します:
custom_edges = [0, 20, 35, 50, 65, 80, 100]
plt.hist(data, bins=custom_edges, edgecolor='black', color='steelblue')
plt.title('Histogram with Custom Bin Edges')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()これは、データに意味のあるしきい値がある場合に便利です -- 文字による成績、年齢層、パフォーマンス段階など。
自動ビンアルゴリズム
Matplotlibは、データの特性に基づいて最適なビン数を計算するいくつかのアルゴリズムをサポートしています:
| アルゴリズム | bins=の値 | 方法 | 最適な用途 |
|---|---|---|---|
| Sturges | 'sturges' | 1 + log2(n) | 小さく、おおよそ正規分布のデータセット |
| Scott | 'scott' | 標準偏差とnに基づく | 正規または正規に近いデータ |
| Freedman-Diaconis | 'fd' | IQRとnに基づく | 外れ値に対してロバスト |
| 平方根 | 'sqrt' | sqrt(n) | 素早い大まかな推定 |
| Auto | 'auto' | SturgesとFDの最大値 | 汎用的なデフォルト |
fig, axes = plt.subplots(1, 3, figsize=(14, 4))
for ax, method in zip(axes, ['sturges', 'scott', 'fd']):
ax.hist(data, bins=method, edgecolor='black', color='#4C72B0')
ax.set_title(f'bins="{method}"')
plt.tight_layout()
plt.show()ほとんどの場合、bins='auto'が確実な出発点です。データに外れ値が含まれている場合は'fd'に切り替えてください。これは標準偏差の代わりに四分位範囲を使用するためです。
正規化ヒストグラムと密度ヒストグラム
デフォルトでは、y軸は生のカウントを表示します。density=Trueを設定すると、バーの下の総面積が1になるようにヒストグラムが正規化されます。これにより、y軸が頻度から確率密度に変換されます。
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].hist(data, bins=30, edgecolor='black', color='#55A868')
axes[0].set_title('Frequency (default)')
axes[0].set_ylabel('Count')
axes[1].hist(data, bins=30, edgecolor='black', color='#C44E52', density=True)
axes[1].set_title('Density (density=True)')
axes[1].set_ylabel('Probability Density')
plt.tight_layout()
plt.show()密度正規化は、理論的な分布曲線を重ね合わせたり、サイズの異なるデータセットを比較したりする場合に不可欠です:
from scipy import stats
plt.hist(data, bins=30, density=True, edgecolor='black', color='#55A868', alpha=0.7)
# Overlay the theoretical normal curve
x_range = np.linspace(data.min(), data.max(), 200)
plt.plot(x_range, stats.norm.pdf(x_range, loc=50, scale=15), 'r-', linewidth=2, label='Normal PDF')
plt.legend()
plt.title('Density Histogram with Normal Curve Overlay')
plt.show()外観のカスタマイズ
色、エッジの色、透明度
plt.hist(data, bins=30, color='#4C72B0', edgecolor='white', alpha=0.85)
plt.title('Styled Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()ヒストグラムの種類
histtypeパラメータはビジュアルスタイルを変更します:
histtypeの値 | 説明 |
|---|---|
'bar' | 従来の塗りつぶしバー(デフォルト) |
'barstacked' | 複数データセット用の積み上げバー |
'step' | 塗りつぶしなしの線のアウトライン |
'stepfilled' | ステップアウトライン付きの塗りつぶし領域 |
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
types = ['bar', 'barstacked', 'step', 'stepfilled']
for ax, ht in zip(axes.flat, types):
ax.hist(data, bins=30, histtype=ht, edgecolor='black', color='#4C72B0')
ax.set_title(f'histtype="{ht}"')
plt.tight_layout()
plt.show()'step'タイプは、複数の分布を重ね合わせる際に特に便利です。塗りつぶしのないアウトラインは互いを隠しません。
1つのプロットに複数のヒストグラム
重ね合わせヒストグラム
alpha(透明度)を使用して、2つ以上の分布を重ね合わせます:
np.random.seed(42)
group_a = np.random.normal(loc=50, scale=10, size=800)
group_b = np.random.normal(loc=65, scale=12, size=800)
plt.hist(group_a, bins=30, alpha=0.6, color='#4C72B0', edgecolor='black', label='Group A')
plt.hist(group_b, bins=30, alpha=0.6, color='#C44E52', edgecolor='black', label='Group B')
plt.legend()
plt.title('Overlapping Histograms')
plt.xlabel('Score')
plt.ylabel('Frequency')
plt.show()並列ヒストグラム
配列のリストを渡して、グループ化されたバーで表示します:
plt.hist([group_a, group_b], bins=20, color=['#4C72B0', '#C44E52'],
edgecolor='black', label=['Group A', 'Group B'])
plt.legend()
plt.title('Side-by-Side Histograms')
plt.xlabel('Score')
plt.ylabel('Frequency')
plt.show()配列のリストを渡すと、matplotlibは各ビン内で各データセットのバーを隣り合わせに配置します。
積み上げヒストグラム
stacked=Trueを設定して、あるデータセットを別のデータセットの上に積み上げます。これにより、個々の分布とそれらの合計の両方が表示されます。
np.random.seed(42)
freshmen = np.random.normal(loc=68, scale=8, size=500)
sophomores = np.random.normal(loc=72, scale=7, size=400)
juniors = np.random.normal(loc=75, scale=6, size=300)
plt.hist([freshmen, sophomores, juniors], bins=25, stacked=True,
color=['#4C72B0', '#55A868', '#C44E52'], edgecolor='black',
label=['Freshmen', 'Sophomores', 'Juniors'])
plt.legend()
plt.title('Stacked Histogram: Exam Scores by Class Year')
plt.xlabel('Score')
plt.ylabel('Frequency')
plt.show()積み上げヒストグラムは、サブグループが全体の分布にどのように寄与しているかを示したい場合にうまく機能します。ただし、3つまたは4つ以上のグループでは読みにくくなります。
累積ヒストグラム
cumulative=Trueを設定して、値が左から右にどのように蓄積されるかを示します。最後のバーは合計カウント(density=Trueの場合は1.0)に達します。
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].hist(data, bins=30, cumulative=True, edgecolor='black', color='#DD8452')
axes[0].set_title('Cumulative Histogram (Count)')
axes[0].set_ylabel('Cumulative Count')
axes[1].hist(data, bins=30, cumulative=True, density=True, edgecolor='black', color='#8172B3')
axes[1].set_title('Cumulative Histogram (Density)')
axes[1].set_ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()累積ヒストグラムは、「値の何パーセントが60未満か?」のような質問に、y軸から直接読み取って答えるのに便利です。
水平ヒストグラム
orientation='horizontal'を設定して軸を反転します。これは、値のラベルが長い場合や、ヒストグラムを別の垂直チャートの横に配置したい場合に便利です。
plt.hist(data, bins=30, orientation='horizontal', color='#64B5CD', edgecolor='black')
plt.title('Horizontal Histogram')
plt.xlabel('Frequency')
plt.ylabel('Value')
plt.show()plt.hist()の返り値
plt.hist()は、ヒストグラムデータへのプログラム的なアクセスを提供する3つの値を返します:
n, bin_edges, patches = plt.hist(data, bins=20, edgecolor='black', color='#4C72B0')
plt.show()
print(f"Bin counts (n): shape = {n.shape}, first 5 = {n[:5]}")
print(f"Bin edges: shape = {bin_edges.shape}, first 5 = {bin_edges[:5]}")
print(f"Patches: {len(patches)} Rectangle objects")| 返り値 | 型 | 説明 |
|---|---|---|
n | ndarray | 各ビンのカウント(または密度) |
bin_edges | ndarray | 各ビンの境界値(長さ = len(n) + 1) |
patches | Rectangleのリスト | 各バーのmatplotlibパッチオブジェクト |
patchesを使用して、高さや位置に基づいて個々のバーに色を付けることができます:
n, bin_edges, patches = plt.hist(data, bins=30, edgecolor='black')
# Color bars based on height
for count, patch in zip(n, patches):
if count > 50:
patch.set_facecolor('#C44E52')
else:
patch.set_facecolor('#4C72B0')
plt.title('Conditional Bar Coloring')
plt.show()plt.hist()の一般的なパラメータリファレンス
| パラメータ | 型 | 説明 | デフォルト |
|---|---|---|---|
x | array-like | 入力データ | 必須 |
bins | int、シーケンス、またはstr | ビンの数、ビンの境界、またはアルゴリズム名 | 10 |
range | tuple | ビンの下限と上限の範囲 | (x.min(), x.max()) |
density | bool | 面積が1になるように正規化 | False |
weights | array-like | 各データポイントの重み | None |
cumulative | bool | 累積ヒストグラムを計算 | False |
histtype | str | 'bar'、'barstacked'、'step'、'stepfilled' | 'bar' |
orientation | str | 'vertical'または'horizontal' | 'vertical' |
color | colorまたはリスト | バーの色 | None |
edgecolor | color | バーのエッジの色 | None |
alpha | float | 透明度(0から1) | None |
label | str | 凡例のラベル | None |
stacked | bool | 複数データセットを積み上げ | False |
log | bool | 対数y軸 | False |
rwidth | float | バーの相対幅(0から1) | None |
bottom | array-likeまたはスカラー | 各バーのベースライン | 0 |
plt.hist() vs sns.histplot():どちらを使うべきか
matplotlibと一緒にseabornを使用している場合、どちらのヒストグラム関数を使うべきか迷うかもしれません。直接比較を示します:
| 機能 | plt.hist() | sns.histplot() |
|---|---|---|
| ライブラリ | matplotlib | seaborn |
| 入力タイプ | Array、リスト、Series | Array、Series、DataFrameの列 |
| KDEオーバーレイ | 手動(scipy必要) | 組み込み(kde=True) |
| デフォルトスタイル | 最小限 | 出版品質 |
| 複数グループ | 配列のリストを渡す | hueパラメータ |
| 統計オプション | カウント、密度 | カウント、密度、頻度、確率、パーセント |
| ビンアルゴリズム | sturges、scott、fd、sqrt、auto | auto、fd、doane、scott、stone、rice、sturges、sqrt |
| 対数スケール | log=True | log_scale=True |
| カテゴリ軸 | 非対応 | hue経由で対応 |
| パフォーマンス(大量データ) | より高速 | やや低速 |
| カスタマイズの深さ | 完全なmatplotlib API | seaborn + matplotlib API |
plt.hist()を使用する場合:すべてのビジュアル要素を完全に制御する必要がある場合、サブプロットで作業する場合、またはseabornが利用できない場合。sns.histplot()を使用する場合:KDEオーバーレイ、よりクリーンなデフォルトスタイルが必要な場合、または最小限のコードでカテゴリ変数によるデータ分割が必要な場合。
PyGWalkerでインタラクティブなヒストグラムを作成
静的なヒストグラムはレポートやスクリプトには最適ですが、探索的データ分析では、ビンの変更、サブセットのフィルタリング、チャートタイプ間の素早い切り替えが頻繁に必要になります。PyGWalker (opens in a new tab)は、pandasまたはpolarsのDataFrameをJupyter Notebook内で直接、インタラクティブなドラッグ&ドロップ可視化インターフェースに変換するオープンソースのPythonライブラリです -- フロントエンドのコードは不要です。
pip install pygwalkerimport pandas as pd
import pygwalker as pyg
# Load your dataset into a DataFrame
df = pd.DataFrame({
'score': np.random.normal(70, 12, 2000),
'group': np.random.choice(['A', 'B', 'C'], 2000)
})
# Launch the interactive UI
walker = pyg.walk(df)インターフェースが開いたら、scoreをx軸にドラッグすると、PyGWalkerが自動的にヒストグラムを生成します。ビンサイズの調整、カラーエンコーディングによるgroupでの分割、密度モードへの切り替え、結果のチャートのエクスポートが可能です -- すべて追加のコードを書く必要はありません。これは、レポート用の最終的なmatplotlibコードを書く前に、複数の変数を素早く探索する必要がある場合に特に便利です。
よくある質問
matplotlibヒストグラムに適切なビン数を選ぶにはどうすればよいですか?
bins='auto'から始めてください。これはSturgesとFreedman-Diaconis法の最大値を使用します。外れ値のあるデータにはbins='fd'を使用してください。小さなデータセット(200ポイント未満)にはbins='sturges'がうまく機能します。整数を渡して目視で調整することもできます:分布が過度に滑らかに見える場合は数を増やし、バーがノイジーに見える場合は減らしてください。
plt.hist()のdensity=Trueとcumulative=Trueの違いは何ですか?
density=Trueはヒストグラムを正規化して、すべてのバーの下の総面積が1になるようにし、y軸を確率密度に変換します。cumulative=Trueは各バーが前のすべてのバーの合計に自身を加えたものを表すようにします。両方を組み合わせることができます:density=True, cumulative=Trueは最後のバーが1.0に達する累積分布関数を生成します。
matplotlibで2つのヒストグラムを重ね合わせるにはどうすればよいですか?
同じbins値でplt.hist()を2回呼び出し、alphaを1未満の値(例:0.5または0.6)に設定して、両方の分布が見えるようにします。各呼び出しにlabelを追加し、plt.legend()で仕上げます。代替手段としてhisttype='step'を使用すると、アウトラインのみを描画するため、透明度の必要性が完全になくなります。
plt.hist()はpandas SeriesやDataFrameの列を直接処理できますか?
はい。plt.hist()はpandas Seriesを含む、あらゆるarray-likeの入力を受け付けます。df['column_name']を直接渡すことができます。pandasの組み込みメソッドを使用してDataFrameからプロットするには、df['column_name'].plot.hist(bins=30)を使用します。これは内部的にmatplotlibを使用しています。
matplotlibヒストグラムを画像ファイルとして保存するにはどうすればよいですか?
plt.hist()を呼び出した後、plt.show()の前にplt.savefig('histogram.png', dpi=150, bbox_inches='tight')を使用します。bbox_inches='tight'パラメータにより、ラベルが切り取られるのを防ぎます。サポートされるフォーマットにはPNG、PDF、SVG、EPSがあります。