Pythonでグラフを描こう ― 棒グラフ/ヒストグラム/散布図/ヒートマップ数学×Pythonプログラミング入門(2/3 ページ)

» 2021年10月11日 05時00分 公開
[羽山博]

[4]散布図の作成

 散布図は回帰分析を行うにあたって変数同士の関係を可視化したり、データがどのようなグループに分かれているのかアタリを付けたりするのに役立ちます。散布図を描くにはscatter関数を使います。最低限必要なデータはxのリストとyのリストだけです。以下(リスト28、図17)の例は英語の成績と数学の成績を散布図にしたものです。

import matplotlib.pyplot as plt

english = [98, 77, 64, 45, 58, 62, 89, 30]
mathmatics = [88, 54, 72, 85, 70, 81, 63, 27]

plt.scatter(english, mathmatics, marker='*')
plt.xlabel('English')
plt.ylabel('Math')

リスト28 散布図を描画するためのコード
引数marker'*'を指定すると、散布図のマーカーとして☆が表示される。他にもいろいろなマーカーが指定できる。こちらの公式APIドキュメントを参照するとよい。scatter関数では、最初の引数にx(横軸)の値を、2番目の引数にy(縦軸)の値を指定するだけで散布図が描けるが、それだけだとグラフを表示したときに英語と数学がどちらの軸なのか分からなくなるので、xlabel関数とylabel関数を使って軸ラベルを指定してある。

英語と数学の成績の散布図 図17 英語と数学の成績を散布図にしたもの
データを見る限りでは英語の成績と数学の成績には負の相関があるように思える。が、第3回で作成した相関係数を求めるプログラムで計算すると、0.453という正の相関が得られる。また、後の練習問題で取り組む回帰分析のプログラムを使って計算しても、回帰式の係数が0.404という正の値になる。これは、極端に成績の高い人と極端に成績の低い人がいて、それらの値に引きずられたため(ちなみに、それらの値を除外すると相関係数は-0.800という負の値になり、回帰式の係数も-0.596という負の値になる)。1、2件の外れ値だけでは何とも言えないが、全体を込みにして考えるのではなく、幾つかのクラスタがあるかもしれないなどと考えるヒントにもなる。

[5]ヒートマップを作成する

 ここまでは、収集したデータの特徴を見付けたり、分析のための手かがりを得たりするためにグラフを作成してきましたが、ここでは、分析した結果を基にその特徴を可視化する例を見てみます。架空の例ですが、表1は、クラスタリングにより、顧客データを5つのクラスタに分けたときに、それぞれのクラスタに各年齢の人が何人いるかを集計したものです。

クラスタ\年齢 10〜19 20〜29 30〜39 40〜49 50〜
0 12 25 54 12 8
1 14 36 44 8 9
2 0 1 12 48 65
3 4 4 0 65 23
4 1 5 8 14 74
表1 クラスタリングの結果(クラスタごとの年齢分布)
数字だけ見ると、クラスタ0とクラスタ1には30代より下の層が多く、クラスタ2〜4に40代以降が多いように見える。この特徴を可視化しよう。

 この表にまとめられた値をヒートマップで表してみます。ヒートマップとは、値を色で分けたもので、一般に値が大きくなるほど色が濃くなります。天気予報や地理情報などで、気温が高い地域ほど赤く、低い地域ほど青く表示される地図がありますが、そういったものを想像してもらうといいでしょう。先に実行例を見ておきます(図18)。

ヒートマップ 図18 ヒートマップの作成例
ここでは、赤っぽい色のカラーマップで表示してみた。最大値の74が最も濃い色になり、最小値の0が最も薄い色になる。どのクラスタでどの年齢層の割合が大きいか(クラスタによる偏りがどのようになっているか)が一目で分かる。

 ヒートマップは、seabornモジュールのheatmap関数を使えば簡単に作成できるのですが(これについては後で補足します)、ここでは、画像の作成や操作のための基礎ともなるので、あえて自分で色を塗り分ける方法でやってみましょう。そういうわけで、ちょっと回り道になりますが、ヒートマップそのものの作成の前に、画像を自分で作成する方法から見ていきます。既に画像の作り方について理解しているという方は、独力でヒートマップを作るか、コードの作成例のところまで読み飛ばしてもらってもけっこうです。ここから、ステップを分けて進めます。まずは、モノクロ画像(グレースケール画像)の作成からです。

ステップ1: モノクロ(グレースケール)画像を作る方法を知る

 リスト29のように二次元のリストを用意しておき、各点の色の濃さを指定します。あとはmatplotlib.pyplotモジュールのimshow関数で表示するだけです(図19)。簡単ですね。

import matplotlib.pyplot as plt

data=[[1.0, 0.0, 1.0],
      [0.5, 1.0, 0.2]]

plt.imshow(data, cmap='gray')

リスト29 モノクロ画像を表示するコード
画像を表す二次元のリストdataには、1.0を白、0.0を黒とした値を指定している。cmapはカラーマップの指定。これを'gray'に設定しておくとグレースケールで表示される(何も指定しないと1.0が黄色っぽい色、0.0が紫っぽい色で表示される)。

モノクロ画像の表示例 図19 モノクロ画像の表示例
この画像には「点」が6個しかないが、もっと点の数を増やせばどんな画像でも描ける。クラスタリングの結果を可視化するために使うヒートマップの作成にもそのまま利用できる。

 では、解説していきましょう。画像を表すデータdataは二次元のリストになっています。ここでは、2行3列の画像を表しています。以下の図20でインデックスとその位置の対応を見ると、書き方と意味がよく分かりますね。

モノクロ画像の図解 図20 モノクロ画像を表示するためのリスト
二次元のリストを作り、最初のインデックスを行、次のインデックスを列と考えて色の濃さを指定すればよい。

 確認のために穴埋め問題を出しておきましょう。以下のオレンジ色の部分はどのような値かを考えてみてください。その部分をクリックすると答えが表示されます。

  • (1) data[0][1]の値は 0.0 である。
  • (2) data[ 1 ][ 0 ]の値は0.5である。

 リスト名のdataは行全体を表すので、リストの長さを求めるlen関数を使ってlen(data)の値を求めると2が返されます。また、data[0]は0行目全体(0行目の列全体)なのでlen(data[0])の値は3となります。

 さて、ここで重要な注意点です。画像を表示するために使うimshow関数でモノクロ(グレースケール)画像を表示する際には、0.0が黒、1.0が白というわけではなく、最小値が黒、最大値が白となるということです。例えば、左上(0行0列目)の1.010.0に書き換えて実行すると、左上だけが白で、残りは黒や黒に近い色になります。

 なお、色の濃さは上のように浮動小数点数で指定することもできますし、整数で指定することもできます。整数で指定する場合は0〜255という値を使うのが一般的です。以下のコードを実行すると、図19と同様の画像が表示されます。

import matplotlib.pyplot as plt

data=[[255, 0, 255],
      [127, 255, 51]]

plt.imshow(data, cmap='gray')

リスト30 モノクロ画像の色の濃さを整数で指定する
整数で色の濃さを指定した場合でも、最大値が白、最小値が黒になる。実行結果は図19と同じ。

ステップ2: カラー画像を作る方法についても知っておく(ちょっと脇道)

 続いて、カラー画像です。実は、ヒートマップはカラー画像を使わなくても作成できるのですが、カラー画像の作成もモノクロ画像の延長でできるので、ここで併せて説明しておくことにします。先を急ぐ方はこのステップを読み飛ばしてもらって結構です。

 カラー画像を作成するには、モノクロ画像の各点の値の代わりに、[r, g, b]という形式のリストを指定するだけです(図21)。rは赤の度合い、gは緑の度合い、bは青の度合いです。不透明度aを含めた[r, g, b, a] という形式も使えます。いずれの値にも、0.01.0までの浮動小数点数や0255の整数が指定できます。

カラー画像のリスト 図21 カラー画像を表示するためのリスト
二次元のリストの各点の色を[r, g, b]のリストとすればよい。つまり三次元のリストになる。例えば、data[0][1]の値はモノクロ画像では0.0という数値だったが、カラー画像では[231, 50, 245]というリストになっている。

 カラー画像の場合、データは三次元のリストになりますが、最初のインデックスが行に、次のインデックスが列に、最後のインデックスがrgbそれぞれの色の度合いに当たると考えると分かりやすいですね。例えば、図21のdata[0][1]は0行1列目の色を表しますが、その0番目つまりdata[0][1][0]の値は231で、これが赤(r)の度合いを表します。同様に、data[0][1]の1番目つまりdata[0][1][1]の値は50で、これが緑(g)の度合いを表します。data[0][1] の2番目つまりdata[0][1][2] の値は245で、これが青(b)の度合いを表します。

 カラー画像を表すリストを作る方法は、モノクロのリストから順を追って考えると分かりやすかったと思います。では、図21に示したようなカラー画像のデータを作って、表示してみましょう。ここでは、目盛りや軸を非表示にしてみます。

import matplotlib.pyplot as plt

data=[[[233, 55, 36], [231, 50, 245], [168, 245, 253]],
      [[104, 223, 181], [247, 236, 80], [247, 203, 248]]]

plt.imshow(data)
plt.axis('off') # 目盛りや軸を非表示にする設定

リスト31 カラー画像を表示するコードの例
各点の色を[r, g, b]のリストで指定した。plt.axis('off')と記述しておくと目盛りや軸が非表示になる。

 実行例は図22の通りです。

カラー画像 図22 カラー画像の表示例
いわゆるドット絵と同じ考え方。点や色を増やしていけばどんな画像でも描ける。

 表示する色の種類がある程度限られるのであれば、色を表すリストに変数名を付けておくと便利です。例えば、[0, 0, 0] blackという名前を付けておき、[76, 178, 153] greenblueという名前を付けるなら、以下のように書けます。

black = [0, 0, 0] # 黒
greenblue = [76, 178, 153] # 緑青っぽい色

 さらに、画像の配色に何らかのルールがあるなら、そのルールに従ってコードを書けばさまざまな模様が描けますね。上の2つの色を使って、図23のような市松模様を表示する例を紹介しておきます。

市松模様 図23 市松模様の表示例
ルールが決まっている模様であれば、点の色を一つ一つ指定しなくても描画できる。

 市松模様がどのようなルールで表現できるかちょっと考えてみてください。作成された画像を眺めていると、0行0列、0行2列、1行1列、1行3列……が黒で、0行1列、0行3列、1行0列、1行2列……が緑青になっていることに気づくと思います。ということは、列位置+行位置の値が偶数であれば黒、そうでなければ緑青といった表し方ができますね。偶数であるかどうかは、2で割った余りが0であるかを調べれば分かります。もちろん、他にもやり方はありますが、以下のように書けます。

import matplotlib.pyplot as plt

black = [0, 0, 0] # 黒
greenblue = [76, 178, 153] # 緑青っぽい色

data = [[black if (x + y)% 2 == 0 else greenblue for x in range(10)] for y in range(10)]
plt.imshow(data)

リスト32 市松模様を表示するコードの例
リスト内包表現を二重に使い、列(x)を10列分、行(y)を10行分とした二次元のリストを作る。リストの値は、列位置と行位置の和が偶数であれば黒、そうでなければ緑青としている。

 リスト内包表記に慣れていないと、二重のリスト内包表記が理解しづらいかもしれません。また、条件式三項演算子)と呼ばれることもあります)の、

  式1 if 条件 else 式2

という書き方で、「条件を満たしたときには式1の値を返し、そうでないときは式2の値を返す」というのも、初めてだと面食らうかもしれません。if文を使った条件分岐すらこの連載ではまだ触れていないので、本来なら詳しく説明しないといけないところですが、このステップは「脇道」のお話なので、形式を示すにとどめ、詳しい説明はまたの機会とします。

 ただ、リスト内包表記については前回も紹介したので、二重のリスト内包表記については簡単に説明を加えておきます。これについては、内側から細部を「つぶして」大まかに考えていくと理解しやすいと思います。まず、条件式の部分を、ざっくりと「何かの値を返す」と考えます。次に内側のリスト内包表記を「10列のリストにする」と考え、最後に一番外のリスト内包表記を「10行のリストにする」と考えればスッキリと理解できます。

二重のリスト内包表記を理解する 図24 二重のリスト内包表記を解きほぐす
慣れればどうということはないが、最初のうちは、細部を大まかに捉えながら、全体像を考えていくとよい。

 画像の取り扱いについては、PNGファイルやJPEGファイルの画像を読み込んで、拡大/縮小する、回転するといった基本的な処理や、さまざまな現象の画像によるシミュレーションなど、興味深い例が数多くあります。それらについても、いずれ回を改めて紹介できればと思います。では、元のお話に戻りましょう。

ステップ3: ヒートマップの画像を作って表示する

 実は、ステップ1の知識があれば、ヒートマップは作成できます。実行例を見ると、赤っぽい色で表示されているので、自分でカラー画像を作る必要があるかのように思われますが、あらかじめ決められた配色(カラーマップ)に従って、値に対応する色が表示されているだけなので、モノクロ画像が作成できればヒートマップが表示できます。データは二次元のリストにすればいいですね。

import matplotlib.pyplot as plt

cluster=[[12, 25, 54, 128],
         [14, 36, 4489],
         [ 01, 12, 48, 65],
         [ 440, 65, 23],
         [ 158, 14, 74]]

plt.imshow(cluster, cmap='Reds') # 画像の作成(ヒートマップの作成)
plt.title('heatmap') # タイトルの指定
plt.xlabel('age') # X軸のラベル
plt.ylabel('cluster') # Y軸のラベル
plt.xticks(range(5), ['10-19', '20-29', '30-39', '40-49', '50-']) # 目盛りを置き換える
plt.show()

リスト33 ヒートマップを表示するためのコードの例
タイトルや軸ラベルの設定は見ての通り。xticks関数はX軸の目盛りの設定。最初の引数で指定した数値が次の引数に置き換えられる。従って、0〜5'10-19','20-29', ... , '50-'に置き換えられて表示される。imshow関数の引数にcmap='Reds'を指定すれば、赤っぽい配色の画像になる。

 統計データの可視化に使われるseabornモジュールのheatmap関数を利用するのであれば、以下の3つの文を書くだけでヒートマップが作成できます。

  • import seaborn as snsを書く
  • リストclusterを作成する ← これは上で見た通り
  • sns.heatmap(cluster)と書く

 これだけです。他のコードは必要ありません。ただし、ここでの実行例と同様にX軸の目盛りの設定やカラーマップの指定をするなら、sns.heatmap(cluster, xticklabels=['10-19', '20-29', '30-39', '40-49', '50-'], cmap="Reds")と書きます。ヒートマップを作成するためのデータ(cluster)として、ここではリストを使いましたが、pandasのデータフレームやnumpyndarrayも使えます。

[6]複数のグラフを並べて表示する

 ここまでは、単一のグラフを描く例しか見てきませんでしたが、複数のグラフを並べて描き、それぞれの傾向や特徴を比較したいこともあるはずです。そのような場合には、まず、subplot関数を使って幾つかの描画領域(Axesオブジェクト)を作成します。それらの描画領域にグラフを描画するというわけです。Axesは「軸」や「座標軸」を意味するAxisの複数形ですが、取りあえずは描画領域を表すものだと考えればいいでしょう。

 簡単な例で見てみましょう。y=x2y=2xのグラフを比較するために、同じ描画領域に2つのグラフを描いた例と、複数の描画領域に並べて描いた例を見てみます(リスト34、図25)。数のグラフの並べて描画する方法については、図解で説明した動画も用意してあるので、ぜひ視聴してみてください。リスト34で使われている関数makedataは、前回作成したものと同じです。

動画3 複数のグラフを並べて描く方法


import numpy as np
import matplotlib.pyplot as plt

def quadratic(x): # xの2乗
  return x**2

def exponential(x): # 2のx乗
  return 2**x

# 指定した関数funcを基に、xminからxmaxまで、step刻みでデータを作る(前回作成したもの)
def makedata(func, xmin, xmax, step, **args):
  x = np.arange(xmin, xmax, step)
  y = func(x, **args)
  return x, y

# データを作る
qx, qy = makedata(quadratic, 0, 10, 0.1) # x=0から10まで0.1刻みで
ex, ey = makedata(exponential, 0, 10, 0.1)

# 単純に2つのグラフを描く
plt.figure(figsize=[7, 4])
plt.plot(qx, qy, label='x^2')
plt.plot(ex, ey, label='2^x')
plt.legend(loc=2)
plt.show()  # ここまでをいったん表示

# 複数のグラフを並べて描く
plt.figure(figsize=[7, 4]) # デフォルトのサイズだと少し重なるのでやや横長に

# xの2乗のグラフ
ax1 = plt.subplot(121)  # 1行2列の1つ目のグラフ
ax1.axis(ymax=1000# y軸の最大値を1000とする
ax1.plot(qx, qy, color='C0', label='x^2')
ax1.legend(loc=2)

# 2のx乗のグラフ
ax2 = plt.subplot(122)  # 1行2列の2つ目のグラフ
ax2.axis(ymax=1000)
ax2.plot(ex, ey, color='C1', label='2^x')
ax2.legend(loc=2)
plt.show()  # 2つ目を表示(この行がなくても自動的に表示される)

リスト34 複数のグラフをまとめて描くコードと並べて描くコード
subplot関数の引数には、何行何列の何番目のグラフであるかを指定する。上のように121と数字を並べてもいいし1,2,1と指定してもよい。234なら、2行3列の4番目(2行目の1列目に当たる位置)に表示される。ここでは描画領域を2つ作っている(ax1ax2という名前で参照できる)。いずれも、スケールを統一するためにymax=1000を指定し、y軸の最大値を1000にしている。plotメソッドを使って描画するときには、線の色が同じだと区別が付かないので、引数colorに異なる色を指定している。'C0'は0番の色(通常はオレンジ)、'C1'は1番の色(通常はブルー)となる。色名(例:'magenta')や16進数のカラーコード(例:'#FF00FF')も指定できる。

複数のグラフを描く(1)
複数のグラフを描く(2) 図25 複数のグラフをまとめて描いた例と並べて描いた例
x2よりも2xの方が急激に値が大きくなることが分かる。このように、グラフを並べると、関数やデータの特徴を比較できる。この例はアルゴリズムによる計算量の違いを比較するのによく使われる。計算量が入力に対して二次関数的(quadratic)/指数関数的(exponential)なオーダー(成長率)で増える場合、x2の二次関数的なオーダーなら実用上ほぼ問題がないが、2xの指数関数的なオーダーだとxが少し大きくなるだけで実用的にならないことが、視覚的にも読み取れる。

 以上、幾つかのグラフや設定を見てきましたが、紹介した内容はごく一部です。これら以外にも「こんなグラフが作りたい」「こんな設定にしたい」というさまざまな要求があるかと思います。が、基本的なグラフが描けるようになれば、あとはmatplotlib.pyplotの公式APIドキュメントなどを参照しながら自力で解決できると思います(サンプルも数多く提供されています)。

 というわけで、今回も練習問題に取り組んでみましょう。

Copyright© Digital Advantage Corp. All Rights Reserved.

アイティメディアからのお知らせ

スポンサーからのお知らせPR

注目のテーマ

Microsoft & Windows最前線2026
人に頼れない今こそ、本音で語るセキュリティ「モダナイズ」
4AI by @IT - AIを作り、動かし、守り、生かす
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。