散布図は、2つの変量の関係と分布を一度に把握できる効果的な可視化手法です。本記事では、Matplotlib を使って、基本的な散布図の作成から始め、データの第3、第4の変数に基づいて点の色やサイズを変更する方法、さらにこれらの情報を分かりやすく伝えるカラーバーやサイズ凡例の追加方法までを解説します。この記事を通して、色やサイズを活用して複数の情報を盛り込んだ、より豊かで表現力のある散布図を作成するスキルを習得できます。
この記事で学べること
scatter()
関数を使った基本的な散布図の作成方法c
およびs
引数を使ったデータに基づいた点の色分けとサイズ変更colorbar()
を使ったカラーバーの追加とカスタマイズ- サイズによる情報表現のための凡例の追加方法
- 複数の情報を盛り込んだ散布図を効果的に見せるための工夫(重なり対策、注釈、グリッド)
- データに適したカラーマップの選び方と色覚多様性への配慮
- Plotly を使ったインタラクティブな散布図の作成
▶️ Matplotlib 散布図の公式ドキュメントも参考にしてください:
matplotlib.axes.Axes.scatter
1. 基本的な散布図を作成する
この図は、X 軸と Y 軸の 2 つの変数間の関係とデータ点の分布を示す基本的な散布図です。
ax.scatter(x, y)
が散布図の基本です。ここから点の色、サイズ、透明度などにデータを割り当てて、より多くの情報を表現できるようになります。
import pandas as pd
import matplotlib.pyplot as plt
!pip install japanize-matplotlib > /dev/null # Google Colab の機能を使って日本語フォントをインストール・インポート
import japanize_matplotlib
# データをDataFrameにする
data = {'X': [1, 2, 3, 4, 5, 6], 'Y': [1.2, 2.4, 1.8, 3.6, 2.2, 3.9]}
df = pd.DataFrame(data)
fig, ax = plt.subplots()
ax.scatter(df['X'], df['Y'])
ax.set_title("基本の散布図")
ax.set_xlabel("X")
ax.set_ylabel("Y")
plt.show()
<Figure size 640x480 with 1 Axes>
図から読み取れる考察: この基本的な散布図では、Xの値が増加するとYの値も概ね増加する傾向が見られます。これは、2つの変数間に正の相関がある可能性を示唆しています。しかし、データ点が少ないため、明確な結論を出すにはさらなるデータが必要です。
2. 連続値で点を色分けする(c, cmap, colorbar)
3 つ目の変数(連続値)を、点の色で表現します。c
引数に色の基準となる連続値の配列を渡し、cmap
引数で色分けに使うカラーマップを指定します。さらに colorbar()
を追加することで、色の濃淡がどの値に対応するかを明確に示します。
import pandas as pd
import matplotlib.pyplot as plt
!pip install japanize-matplotlib > /dev/null # Google Colab の機能を使って日本語フォントをインストール・インポート
import japanize_matplotlib
from matplotlib import cm, colors
# 7都市データ
data_cities = {
"都市名": ["千葉市", "福岡市", "神戸市", "名古屋市", "さいたま市", "札幌市", "仙台市"],
"緯度": [35.60, 33.59, 34.69, 35.18, 35.91, 43.06, 38.27],
"経度": [140.12, 130.40, 135.18, 136.90, 139.66, 141.35, 140.87],
"人口密度 (人/km²)": [3613, 4668, 2719, 7128, 6072, 1750, 1389],
"人口総数 (人)": [972861, 1579450, 1526639, 2283289, 1226656, 1955115, 1088669]
}
# DataFrameを作成
df_cities = pd.DataFrame(data_cities )
display(df_cities)
# 散布図の描画
fig, ax = plt.subplots(figsize=(10, 8))
# 人口密度に応じて色分けするための設定
norm = colors.Normalize(vmin=df_cities['人口密度 (人/km²)'].min(), vmax=df_cities['人口密度 (人/km²)'].max())
# より分かりやすいカラーマップとして'viridis'に変更
cmap = plt.get_cmap('viridis')
sc=ax.scatter(df_cities['経度'], df_cities['緯度'],
c=df_cities['人口密度 (人/km²)'], cmap=cmap, norm=norm)
# カラーバーの追加
cbar = plt.colorbar(sc, ax=ax)
cbar.set_label("人口密度 (人/km²)") # カラーバーのラベルを明確に
# グラフの装飾
ax.set_title("日本の都市の位置と人口密度の関係")
ax.set_xlabel("経度")
ax.set_ylabel("緯度")
# 各点に都市名を表示
for i, row in df_cities.iterrows():
ax.annotate(row['都市名'], (row['経度'], row['緯度']), textcoords="offset points", xytext=(0,10), ha='center')
plt.show()
都市名 | 緯度 | 経度 | 人口密度 (人/km²) | 人口総数 (人) | |
---|---|---|---|---|---|
0 | 千葉市 | 35.60 | 140.12 | 3613 | 972861 |
1 | 福岡市 | 33.59 | 130.40 | 4668 | 1579450 |
2 | 神戸市 | 34.69 | 135.18 | 2719 | 1526639 |
3 | 名古屋市 | 35.18 | 136.90 | 7128 | 2283289 |
4 | さいたま市 | 35.91 | 139.66 | 6072 | 1226656 |
5 | 札幌市 | 43.06 | 141.35 | 1750 | 1955115 |
6 | 仙台市 | 38.27 | 140.87 | 1389 | 1088669 |
<Figure size 1000x800 with 2 Axes>
図から読み取れる考察: この散布図は、都市の位置(経度、緯度)と人口密度の関係を色の濃淡で示しています。カラーバーを見ると、黄色に近いほど人口密度が高く、紫に近いほど低いことがわかります。「名古屋市」が最も人口密度が高く黄色で表示されており、「札幌市」や「仙台市」は人口密度が低く紫で表示されています。地理的な位置と人口密度の関連性を視覚的に把握するのに役立ちます。
3. 連続値で点のサイズを変える(s)
点のサイズにも意味を持たせ、4つ目の変数(連続値)を表現することができます。s
引数にサイズの基準となる連続値の配列を渡します。サイズの基準となる値の範囲に応じて適切にスケーリングすることで、点のサイズの変化を視覚的に分かりやすく表現できます。
import pandas as pd
import matplotlib.pyplot as plt
!pip install japanize-matplotlib > /dev/null # Google Colab の機能を使って日本語フォントをインストール・インポート
import japanize_matplotlib
# 7都市データ
data_cities = {
"都市名": ["千葉市", "福岡市", "神戸市", "名古屋市", "さいたま市", "札幌市", "仙台市"],
"緯度": [35.60, 33.59, 34.69, 35.18, 35.91, 43.06, 38.27],
"経度": [140.12, 130.40, 135.18, 136.90, 139.66, 141.35, 140.87],
"人口密度 (人/km²)": [3613, 4668, 2719, 7128, 6072, 1750, 1389],
"人口総数 (人)": [972861, 1579450, 1526639, 2283289, 1226656, 1955115, 1088669]
}
# DataFrameを作成
df_cities = pd.DataFrame(data_cities)
display(df_cities)
# 散布図の描画
fig, ax = plt.subplots(figsize=(10, 8))
ax.scatter(df_cities['経度'], df_cities['緯度'],s=df_cities['人口総数 (人)']/10000)
# グラフの装飾
ax.set_title("日本の都市の位置と人口総数の関係")
ax.set_xlabel("経度")
ax.set_ylabel("緯度")
# 各点に都市名を表示
for i, row in df_cities.iterrows():ax.annotate(row['都市名'], (row['経度'], row['緯度']), textcoords="offset points", xytext=(0,10), ha='center')
plt.show()
都市名 | 緯度 | 経度 | 人口密度 (人/km²) | 人口総数 (人) | |
---|---|---|---|---|---|
0 | 千葉市 | 35.60 | 140.12 | 3613 | 972861 |
1 | 福岡市 | 33.59 | 130.40 | 4668 | 1579450 |
2 | 神戸市 | 34.69 | 135.18 | 2719 | 1526639 |
3 | 名古屋市 | 35.18 | 136.90 | 7128 | 2283289 |
4 | さいたま市 | 35.91 | 139.66 | 6072 | 1226656 |
5 | 札幌市 | 43.06 | 141.35 | 1750 | 1955115 |
6 | 仙台市 | 38.27 | 140.87 | 1389 | 1088669 |
<Figure size 1000x800 with 1 Axes>
図から読み取れる考察: この散布図では、都市の位置(経度、緯度)と人口総数の関係を点の大きさで示しています。「名古屋市」が最も点が大きく表示されており、人口総数が最も多いことがわかります。一方で、「仙台市」や「千葉市」は点が小さく、人口総数が比較的少ないことが視覚的に把握できます。点の大きさの相対的な違いから、各都市の人口規模の違いを直感的に比較できます。
注意点: scatter()
関数の s
引数は、マーカーの面積に相当する値を指定します。そのため、表現したいデータの値(例えば人口総数)をそのまま s
に渡すと、値の小さな違いが分かりにくくなることがあります。データの値そのものではなく、その値の平方根や対数などを s
に渡す、あるいは適切な係数でスケーリングするなど、視覚的に意図した通りのサイズ感になるように調整することが重要です
4. 色とサイズを組み合わせて使う
点の色とサイズを同時に使うことで、X軸・Y軸で表現される2つの変数に加え、さらに2つの変数(計4つの変数)を同時に1つの2D散布図で表現できます。これにより、より多角的な視点からデータを分析することが可能になります。
import pandas as pd
import matplotlib.pyplot as plt
!pip install japanize-matplotlib > /dev/null # Google Colab の機能を使って日本語フォントをインストール・インポート
import japanize_matplotlib
from matplotlib import cm, colors
# 7都市データ
data_cities = {
"都市名": ["千葉市", "福岡市", "神戸市", "名古屋市", "さいたま市", "札幌市", "仙台市"],
"緯度": [35.60, 33.59, 34.69, 35.18, 35.91, 43.06, 38.27],
"経度": [140.12, 130.40, 135.18, 136.90, 139.66, 141.35, 140.87],
"人口密度 (人/km²)": [3613, 4668, 2719, 7128, 6072, 1750, 1389],
"人口総数 (人)": [972861, 1579450, 1526639, 2283289, 1226656, 1955115, 1088669]
}
# DataFrameを作成
df_cities = pd.DataFrame(data_cities )
display(df_cities)
# 散布図の描画
fig, ax = plt.subplots(figsize=(10, 8))
# 人口密度に応じて色分けするための設定
norm = colors.Normalize(vmin=df_cities['人口密度 (人/km²)'].min(), vmax=df_cities['人口密度 (人/km²)'].max())
# より分かりやすいカラーマップとして'viridis'に変更
cmap = plt.get_cmap('viridis')
sc=ax.scatter(df_cities['経度'], df_cities['緯度'],
c=df_cities['人口密度 (人/km²)'], cmap=cmap, norm=norm,s=df_cities['人口総数 (人)']/10000)
# カラーバーの追加
cbar = plt.colorbar(sc, ax=ax)
cbar.set_label("人口密度 (人/km²)") # カラーバーのラベルを明確に
# グラフの装飾
ax.set_title("日本の都市の位置と人口密度と人口総数の関係")
ax.set_xlabel("経度")
ax.set_ylabel("緯度")
# 各点に都市名を表示
for i, row in df_cities.iterrows():
ax.annotate(row['都市名'], (row['経度'], row['緯度']), textcoords="offset points", xytext=(0,10), ha='center')
plt.show()
都市名 | 緯度 | 経度 | 人口密度 (人/km²) | 人口総数 (人) | |
---|---|---|---|---|---|
0 | 千葉市 | 35.60 | 140.12 | 3613 | 972861 |
1 | 福岡市 | 33.59 | 130.40 | 4668 | 1579450 |
2 | 神戸市 | 34.69 | 135.18 | 2719 | 1526639 |
3 | 名古屋市 | 35.18 | 136.90 | 7128 | 2283289 |
4 | さいたま市 | 35.91 | 139.66 | 6072 | 1226656 |
5 | 札幌市 | 43.06 | 141.35 | 1750 | 1955115 |
6 | 仙台市 | 38.27 | 140.87 | 1389 | 1088669 |
<Figure size 1000x800 with 2 Axes>
この図は、点の色とサイズを同時に使って複数の情報を表現した散布図の例です。点の色は人口密度を、点のサイズは人口総数をそれぞれ表しています。これにより、都市の位置(経度、緯度)に加え、人口密度と人口総数という二つの異なる追加情報を一つの散布図で同時に視覚化しています。図の右側にあるカラーバーは、色の変化が示す人口密度の値のスケールを表しています。
図から読み取れる考察: この多変量散布図から、以下の点が読み取れます。
- 「名古屋市」は人口密度(色)も人口総数(サイズ)も最も大きいことが一目でわかります。
- 「福岡市」は人口密度は比較的高いですが、人口総数は名古屋市ほど大きくありません。
- 「札幌市」や「仙台市」は、人口密度も人口総数も比較的小さい傾向があります。
このように、色とサイズを組み合わせることで、複数の変数間の複雑な関係性を一つの図で効率的に理解することができます。
5. 散布図をより見やすくするための工夫
作成した散布図をより効果的に、そして誤解なく情報を伝えるためには、いくつかの「見やすくするための工夫」を取り入れることが重要です。ここでは、データ点が多い場合の重なり対策、図の重要なポイントを示す注釈、データ点の位置を正確に読み取るためのグリッドについて解説します。
- 重なり対策:データ点が多いと点が重なってしまい、全体の傾向が分かりにくくなることがあります。これを改善するには、いくつか方法があります。
* 点の透明度を調整する (alpha): 点を半透明にすることで、重なっている部分の密度が分かりやすくなります。
* 点のサイズを小さくする: 点の大きさを調整して、重なりを減らします。
* 点の枠線を消す (edgecolor=”none”): 点の輪郭線をなくすことで、見た目をすっきりさせます。
データが非常に密集している場合は、散布図の代わりに以下のプロットも検討できます。
* 等高線プロット (matplotlib.pyplot.contour または contourf): データの密度の高い領域を等高線で表示します。
* Hexbin プロット (matplotlib.pyplot.hexbin): 領域内のデータ点の数を集計して表示します。
これらの方法で、データセット全体の密度分布やパターンを把握しやすくなります。
- 注釈: グラフ中の特定のデータ点や重要な情報に注釈(テキストラベルや矢印)を追加することで、図のどの部分に注目すべきかを明確に示すことができます。Matplotlib の
ax.annotate()
関数を使用すると、テキストの配置や矢印のスタイル(色、太さ、形状など)を細かくカスタマイズして、強調したいポイントへの誘導をより分かりやすくデザインすることが可能です。 - グリッド: グラフにグリッド線 (
ax.grid(True, linestyle=":", alpha=0.6)
) を追加することで、各データ点のX軸・Y軸上の位置を正確に読み取りやすくなり、異なるデータ点間の比較が容易になります。特定の値を読み取ったり、複数のデータ点を比較したりする場合に特に役立ちます。
これらの工夫を適切に施すことで、散布図が持つ情報をより正確に、かつ効率的に伝えることができます。
import pandas as pd
import matplotlib.pyplot as plt
!pip install japanize-matplotlib > /dev/null # Google Colab の機能を使って日本語フォントをインストール・インポート
import japanize_matplotlib
from matplotlib import cm, colors
# 7都市データ
data_cities = {
"都市名": ["千葉市", "福岡市", "神戸市", "名古屋市", "さいたま市", "札幌市", "仙台市"],
"緯度": [35.60, 33.59, 34.69, 35.18, 35.91, 43.06, 38.27],
"経度": [140.12, 130.40, 135.18, 136.90, 139.66, 141.35, 140.87],
"人口密度 (人/km²)": [3613, 4668, 2719, 7128, 6072, 1750, 1389],
"人口総数 (人)": [972861, 1579450, 1526639, 2283289, 1226656, 1955115, 1088669]
}
# DataFrameを作成
df_cities = pd.DataFrame(data_cities )
display(df_cities)
# 散布図の描画
fig, ax = plt.subplots(figsize=(10, 8))
# 人口密度に応じて色分けするための設定
norm = colors.Normalize(vmin=df_cities['人口密度 (人/km²)'].min(), vmax=df_cities['人口密度 (人/km²)'].max())
# より分かりやすいカラーマップとして'viridis'に変更
cmap = plt.get_cmap('viridis')
sc=ax.scatter(df_cities['経度'], df_cities['緯度'],
c=df_cities['人口密度 (人/km²)'], cmap=cmap, norm=norm,s=df_cities['人口総数 (人)']/10000,edgecolor="black", linewidth=1.0)
# カラーバーの追加
cbar = plt.colorbar(sc, ax=ax)
cbar.set_label("人口密度 (人/km²)") # カラーバーのラベルを明確に
# グラフの装飾
ax.set_title("日本の都市の位置と人口密度と人口総数の関係")
ax.set_xlabel("経度")
ax.set_ylabel("緯度")
# 各点に都市名を表示
for i, row in df_cities.iterrows():
ax.annotate(row['都市名'], (row['経度'], row['緯度']), textcoords="offset points", xytext=(0,10), ha='center')
# グリッド線の追加
ax.grid(True, linestyle=":", alpha=0.6)
# 注釈の追加
ax.annotate("人口密度が最も高い。人口総数が最も多い", xy=(136.50,35.18), xytext=(132,36),
arrowprops=dict(facecolor="black", arrowstyle="->"))
ax.annotate("緯度が最も高い", xy=(141.00,43.06), xytext=(138,42),
arrowprops=dict(facecolor="black", arrowstyle="->"))
plt.show()
都市名 | 緯度 | 経度 | 人口密度 (人/km²) | 人口総数 (人) | |
---|---|---|---|---|---|
0 | 千葉市 | 35.60 | 140.12 | 3613 | 972861 |
1 | 福岡市 | 33.59 | 130.40 | 4668 | 1579450 |
2 | 神戸市 | 34.69 | 135.18 | 2719 | 1526639 |
3 | 名古屋市 | 35.18 | 136.90 | 7128 | 2283289 |
4 | さいたま市 | 35.91 | 139.66 | 6072 | 1226656 |
5 | 札幌市 | 43.06 | 141.35 | 1750 | 1955115 |
6 | 仙台市 | 38.27 | 140.87 | 1389 | 1088669 |
<Figure size 1000x800 with 2 Axes>
6. カラーマップの選び方と注意点
散布図の色分けには、適切なカラーマップを選ぶことが大切です。データの種類(連続値かカテゴリ値か)や、データの特性に合わせて選びましょう。
連続値を表現する場合:
- 順序のある連続値: 値の大小を分かりやすく示すには、色の濃淡や色相が滑らかに変わるカラーマップが適しています。
- おすすめ: viridis, plasma, inferno, magma, cividis (色覚多様性にも配慮されています)
- 中心からの差分を表現したい場合: 中央を基準に色が対称的に変わるカラーマップが有効です。
- おすすめ: coolwarm, bwr, seismic
カテゴリ値を表現する場合:
- カテゴリごとに明確に区別できる離散的な色を持つカラーマップを選びます。
- おすすめ: tab10, tab20 (カテゴリ数が多い場合は色の識別が難しくなるため、マーカーの形状なども組み合わせましょう)
▶️ Matplotlib のカラーマップ一覧も参考にしてください:
Choosing Colormaps in Matplotlib
カラーマップ選びの重要な注意点:
表現したいデータの種類と、使うカラーマップの種類を必ず一致させましょう。間違った組み合わせは、図を見た人に誤解を与える可能性があります。データの性質を正しく理解し、それに合ったカラーマップを選ぶことが、正確な情報伝達のために重要です。
import matplotlib.pyplot as plt
import numpy as np
# データ(カラーバー用)
gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))
# 利用可能なカラーマップをカテゴリごとにまとめる
cmaps = {
'Perceptually Uniform Sequential': ['viridis', 'plasma', 'inferno', 'magma', 'cividis'],
'Sequential': ['Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds'],
'Sequential (2)': ['YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn'],
'Diverging': ['PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic'],
'Qualitative': ['Pastel1', 'Pastel2', 'Paired', 'Accent',
'Dark2', 'Set1', 'Set2', 'Set3',
'tab10', 'tab20', 'tab20b', 'tab20c'],
'Miscellaneous': ['flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix',
'brg', 'hsv', 'gist_rainbow', 'rainbow', 'jet',
'nipy_spectral', 'gist_ncar']
}
# 表示する関数
def plot_color_gradients(category, cmap_list):
nrows = len(cmap_list)
fig, axes = plt.subplots(nrows=nrows, figsize=(8, nrows * 0.4))
fig.subplots_adjust(top=1, bottom=0, left=0, right=1,hspace=1)
for ax, name in zip(axes, cmap_list):
ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name))
ax.set_axis_off()
ax.set_title(name, fontsize=12, loc='left')
plt.show()
# 例:Sequential系カラーマップを表示
plot_color_gradients('Sequential', cmaps['Sequential'])
<Figure size 800x240 with 6 Axes>
上の図は、Matplotlib で利用可能な様々なカラーマップの例を、カテゴリ別に示しています。データの特性に合わせて適切なカラーマップを選択する際の参考にしてください。
図から読み取れる考察: この図は、各カラーマップがどのように色のグラデーションを表現しているかを示しています。「Sequential」カテゴリのカラーマップは、単一の色相または明度の連続的な変化でデータの大小を表現するのに適しています。「Diverging」カテゴリのカラーマップは、中央値を基準としたデータの正負の偏りを示すのに有効です。「Qualitative」カテゴリのカラーマップは、カテゴリデータの識別に使われます。データの性質に合ったカラーマップを選ぶことで、より正確で分かりやすい可視化が可能になります。
7. サイズの凡例を追加する方法
散布図で点のサイズに意味を持たせた場合、そのサイズがどのような値を表しているのかを示す凡例があると、図の解釈が容易になります。Matplotlibのscatter
関数は、色を示すカラーバーのようにサイズを示す凡例を自動で生成する直接的な機能はありませんが、いくつかの手法でサイズ凡例を表現することができます。
一般的なアプローチの一つは、凡例として表示したい代表的なサイズを持つ「ダミー」の散布図要素を作成し、それらをax.legend()
で表示する方法です。この際、実際のデータには含まれないが、凡例として表示したいサイズに対応する点を作成します。
具体的には、以下のような手順で実装します。
1. 凡例に表示したいサイズの値(例: 最小サイズ、中央サイズ、最大サイズなど)を決定します。
2. それぞれのサイズに対応するダミーの散布図要素を作成します。これらの要素は、表示領域の外など、実際のデータと重ならない場所に配置します。
3. ax.legend()
を呼び出し、作成したダミーの散布図要素とそれに対応するラベル(例: “Small Value”, “Medium Value”, “Large Value”など)を渡します。
4. 必要に応じて、handler_map
を使用して凡例マーカーの表示をカスタマイズし、散布図の点と同じように表示されるように調整します(詳細は省略)。
次のセクションで、この手法を用いた具体的なコード例を示します。
# サイズの凡例を追加するコード例
import pandas as pd
import matplotlib.pyplot as plt
!pip install japanize-matplotlib > /dev/null # Google Colab の機能を使って日本語フォントをインストール・インポート
import japanize_matplotlib
from matplotlib import cm, colors
import numpy as np
# 7都市データ
data_cities = {
"都市名": ["千葉市", "福岡市", "神戸市", "名古屋市", "さいたま市", "札幌市", "仙台市"],
"緯度": [35.60, 33.59, 34.69, 35.18, 35.91, 43.06, 38.27],
"経度": [140.12, 130.40, 135.18, 136.90, 139.66, 141.35, 140.87],
"人口密度 (人/km²)": [3613, 4668, 2719, 7128, 6072, 1750, 1389],
"人口総数 (人)": [972861, 1579450, 1526639, 2283289, 1226656, 1955115, 1088669]
}
# DataFrameを作成
df_cities = pd.DataFrame(data_cities )
display(df_cities)
# 散布図の描画
fig, ax = plt.subplots(figsize=(10, 8))
# 人口密度に応じて色分けするための設定
norm = colors.Normalize(vmin=df_cities['人口密度 (人/km²)'].min(), vmax=df_cities['人口密度 (人/km²)'].max())
# より分かりやすいカラーマップとして'viridis'に変更
cmap = plt.get_cmap('viridis')
sc = ax.scatter(df_cities['経度'], df_cities['緯度'],
c=df_cities['人口密度 (人/km²)'], cmap=cmap, norm=norm, s=df_cities['人口総数 (人)']/10000, alpha=0.7, edgecolors="black", linewidth=1.0)
# カラーバーの追加
cbar = plt.colorbar(sc, ax=ax)
cbar.set_label("人口密度 (人/km²)") # カラーバーのラベルを明確に
# グラフの装飾
ax.set_title("日本の都市の位置と人口密度と人口総数の関係")
ax.set_xlabel("経度")
ax.set_ylabel("緯度")
# 各点に都市名を表示
for i, row in df_cities.iterrows():
ax.annotate(row['都市名'], (row['経度'], row['緯度']), textcoords="offset points", xytext=(0,10), ha='center')
# グリッド線の追加
ax.grid(True, linestyle=":", alpha=0.6)
# 注釈の追加
ax.annotate("人口密度が最も高い。人口総数が最も多い", xy=(136.50,35.18), xytext=(132,36),
arrowprops=dict(facecolor="black", arrowstyle="->"))
ax.annotate("緯度が最も高い", xy=(141.00,43.06), xytext=(138,42),
arrowprops=dict(facecolor="black", arrowstyle="->"))
# サイズの凡例のためのダミーデータとラベルを作成
# 例: 最小値、中央値、最大値
legend_population = np.array([df_cities['人口総数 (人)'].min(), df_cities['人口総数 (人)'].median(), df_cities['人口総数 (人)'].max()])
# これらの人口総数値に対応するサイズを計算
legend_sizes = legend_population/ 10000
# 凡例のラベル
legend_labels = [f'人口総数: {int(p):,d}人' for p in legend_population]
# ダミーの散布図要素を生成
# 表示領域の外に配置するため、x, y座標は適当な値(例: 軸の範囲外)を設定
legend_elements = [
ax.scatter([], [], s=s, label=label, color='gray', alpha=0.7, edgecolors='black', linewidth=1.0)
for s, label in zip(legend_sizes, legend_labels)
]
# 凡例を追加
# scatterpointsを増やすことで凡例の点が複数表示されるようにする
ax.legend(handles=legend_elements, title="人口総数によるサイズ(最大値、中央値、最小値)", scatterpoints=1)
plt.show()
都市名 | 緯度 | 経度 | 人口密度 (人/km²) | 人口総数 (人) | |
---|---|---|---|---|---|
0 | 千葉市 | 35.60 | 140.12 | 3613 | 972861 |
1 | 福岡市 | 33.59 | 130.40 | 4668 | 1579450 |
2 | 神戸市 | 34.69 | 135.18 | 2719 | 1526639 |
3 | 名古屋市 | 35.18 | 136.90 | 7128 | 2283289 |
4 | さいたま市 | 35.91 | 139.66 | 6072 | 1226656 |
5 | 札幌市 | 43.06 | 141.35 | 1750 | 1955115 |
6 | 仙台市 | 38.27 | 140.87 | 1389 | 1088669 |
<Figure size 1000x800 with 2 Axes>
8. カテゴリ別に色分けする
これまでは連続値に基づいた色分けを見てきましたが、散布図ではカテゴリ値に基づいて点を色分けすることも非常に一般的です。これにより、異なるカテゴリに属するデータ点の分布や集まり具合を視覚的に比較できます。
カテゴリ別の色分けを行うには、scatter
関数のc
パラメータにカテゴリを示す配列を渡し、cmap
にはカテゴリ数の分だけ明確に異なる色を持つカラーマップを指定します。Matplotlib には、tab10
, tab20
, tab20b
, tab20c
といったカテゴリカルカラーマップが用意されています。
カテゴリ別の色分けでは、カラーバーの代わりに凡例を使用して、各色がどのカテゴリに対応するかを示すのが一般的です。ax.legend()
を使って凡例を追加できます。
次のセクションでは、カテゴリ別の色分けのコード例を示します。
# カテゴリ別の色分けコード例(アヤメデータセット)
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
import pandas as pd # pandasをインポート
!pip install japanize-matplotlib > /dev/null # Google Colab の機能を使って日本語フォントをインストール・インポート
import japanize_matplotlib
# データ読み込み
iris = load_iris(as_frame=True) # as_frame=TrueでDataFrameとして読み込む
df = iris.frame # DataFrameを取得
display(df.head())
df["species"] = df["target"].map(dict(enumerate(iris.target_names))) # 品種名カラムを追加
# 散布図(がく片の長さ vs 幅)
fig, ax = plt.subplots(figsize=(6, 6))
# 'species'カラムでデータをグループ化し、グループごとに異なる色でプロット
for species_name, group in df.groupby('species'):
ax.scatter(
group["sepal length (cm)"], # がく片の長さ
group["sepal width (cm)"], # がく片の幅
label=species_name # 凡例に表示するラベルとして品種名を使用
)
# グラフの装飾
ax.set_xlabel("がく片の長さ (cm)")
ax.set_ylabel("がく片の幅 (cm)")
ax.set_title("アヤメ(Iris)データセット ― がく片の特徴")
# 凡例を追加(titleで凡例のタイトルを設定)
ax.legend(title="種別")
plt.show()
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | target | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | 0 |
1 | 4.9 | 3.0 | 1.4 | 0.2 | 0 |
2 | 4.7 | 3.2 | 1.3 | 0.2 | 0 |
3 | 4.6 | 3.1 | 1.5 | 0.2 | 0 |
4 | 5.0 | 3.6 | 1.4 | 0.2 | 0 |
<Figure size 600x600 with 1 Axes>
図から読み取れる考察: このアヤメデータセットの散布図は、「がく片の長さ」と「がく片の幅」の関係を、アヤメの品種(setosa, versicolor, virginica)ごとに色分けして示しています。図を見ると、「setosa」(青い点)は他2つの品種と比較して、がく片の長さが短く幅が広い傾向にあることが明確に分かります。一方、「versicolor」(オレンジ色の点)と「virginica」(緑色の点)は一部重なりがありますが、virginicaの方ががく片の長さ・幅ともに大きい傾向が見られます。このようにカテゴリ別に色分けすることで、異なるグループ間のデータ分布の違いや特徴を視覚的に比較・分析することができます。
9. インタラクティブな散布図を試す (Plotly)
これまでに見てきた Matplotlib を使った散布図は静的な画像ですが、Plotly のようなライブラリを使用すると、マウス操作に反応するインタラクティブな散布図を作成できます。インタラクティブな散布図には、以下のような利点があります。
- ツールチップ表示: マウスカーソルをデータ点に重ねると、その点の詳細情報(X, Y の値や、色、サイズに対応する値など)を表示できます。これにより、個別のデータ点の正確な値を確認する際に便利です。例えば、特定の外れ値や興味深いデータ点の詳細を素早く把握したい場合に役立ちます。
- ズームとパン: グラフ上で自由に拡大・縮小、移動ができるため、データのごく一部を詳細に確認したり、全体像を素早く把握したりできます。これにより、データの特定の領域に注目したり、全体のパターンを俯瞰したりすることが容易になります。例えば、特定のクラスターの詳細を確認したり、データ全体の傾向を把握したりするのに便利です。
- 選択とハイライト: 特定のデータ点や範囲を選択してハイライト表示したり、関連するデータを別のグラフと連動させたりすることが可能です。これは、特定のグループや条件に合致するデータを探索・分析する際に役立ちます。例えば、特定の条件を満たすデータ点だけを抽出し、さらに分析を進めたい場合に有効です。
Plotly は Matplotlib とは異なる描画ライブラリですが、Python から手軽にインタラクティブなグラフを作成できます。特に Web アプリケーションでのデータの共有や探索において強力なツールとなります。より高度なデータ探索やプレゼンテーションには、インタラクティブな可視化も検討する価値があります。
# Plotly によるインタラクティブな散布図作成
import plotly.express as px
from sklearn.datasets import load_iris
import pandas as pd
!pip install japanize-matplotlib > /dev/null # Google Colab の機能を使って日本語フォントをインストール・インポート
import japanize_matplotlib
# データ読み込み
iris = load_iris(as_frame=True) # as_frame=TrueでDataFrameとして読み込む
df = iris.frame # DataFrameを取得
display(df.head())
df["species"] = df["target"].map(dict(enumerate(iris.target_names))) # 0/1/2 -> 品種名
# 散布図(がく片の長さ×幅)をインタラクティブに作成
fig = px.scatter(
df,
x="sepal length (cm)",
y="sepal width (cm)",
color="species", # カテゴリ色分け
symbol="species", # カテゴリに基づいてマーカーの形状を変更(任意
hover_name="species", # ホバー時に表示される名前
hover_data={
"sepal length (cm)": ":.2f", # ホバー時に表示するデータとそのフォーマット
"sepal width (cm)": ":.2f",
"petal length (cm)": ":.2f",
"petal width (cm)": ":.2f",
},
title="アヤメ(Iris)データセット:がく片の特徴(インタラクティブ散布図)" # タイトルをより具体的に
)
fig.update_layout(legend_title_text="種別") # 凡例のタイトル設定
fig.show() # グラフ表示
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | target | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | 0 |
1 | 4.9 | 3.0 | 1.4 | 0.2 | 0 |
2 | 4.7 | 3.2 | 1.3 | 0.2 | 0 |
3 | 4.6 | 3.1 | 1.5 | 0.2 | 0 |
4 | 5.0 | 3.6 | 1.4 | 0.2 | 0 |
10. 次の記事
続いて「ヒストグラム&箱ひげ:bin 設計・外れ値」で、分布の把握と外れ値の見極めを学びます。
まとめ
この記事では、Matplotlib を使って効果的な散布図を作成するための様々な方法を学びました。
- 色による情報付加: c パラメータでデータの値に基づいた色分けを行い、cmap で適切なカラーマップを選択し、colorbar で色のスケールを明示することで、第三の変数を分かりやすく表現できます。Normalize を活用することで、色分けの再現性を高めることができます。
- サイズによる情報付加: s パラメータにデータの値を渡すことで、点のサイズに意味を持たせることができます。s はポイントの二乗で指定されるため、視覚的に分かりやすいように適切にスケーリングすることが重要です。
- 視覚的な工夫: 点の重なりを軽減するためには alpha で透明度を調整したり、edgecolors=”none”で枠線を消したりする工夫が有効です。これらのテクニックを組み合わせることで、色とサイズを同時に使用した多次元的な散布図も分かりやすく提示できます。
- カラーマップの選択: データの種類(連続値・カテゴリ値)や特性に応じて、適切なカラーマップ(シーケンシャル、ダイバージング、カテゴリカルなど)を選ぶことの重要性を学びました。特に、色覚多様性への配慮も考慮したカラーマップ選びが推奨されます。
- インタラクティブな散布図: Plotly を使うことで、ツールチップ表示やズーム・パンが可能なインタラクティブな散布図を作成でき、より詳細なデータ探索や共有に役立つことを紹介しました。
これらのテクニックを活用することで、より多くの情報を盛り込み、データの傾向や関係性を直感的に伝えられる散布図を作成できるようになります。

コメント