PyLearnST02: 散布図によるデータ分析

散布図を描く方法を学びます。分析をしているときかなりの頻度で使うので、ぜひマスターして欲しいです。 以下、おまじないとして書いてください。乱数を生成するコードです。

In [1]:
import numpy as np

# 右上に集中させた乱数
mu = [1.0, 1.0]
sigma = [[0.1, 0], [0, 0.1]]
Dat01 = np.random.multivariate_normal(mu, sigma, 100)

# 右下に集中させた乱数
mu = [1.0, -1.0]
sigma = [[0.1, 0], [0, 0.1]]
Dat02 = np.random.multivariate_normal(mu, sigma, 100)

# 左上に集中させた乱数
mu = [-1.0, 1.0]
sigma = [[0.1, 0], [0, 0.1]]
Dat03 = np.random.multivariate_normal(mu, sigma, 100)

# 左下に集中させた乱数
mu = [-1.0, -1.0]
sigma = [[0.1, 0], [0, 0.1]]
Dat04 = np.random.multivariate_normal(mu, sigma, 100)

散布図の作り方

以下がテンプレートです。コメントを見ればわかると思います。変えながらどう変化するかチェックして見てください。

In [2]:
import matplotlib.pyplot as plt

x = Dat01[:,0]
y = Dat01[:,1]

plt.figure(figsize=(5, 4)) # figureの縦横の大きさ

plt.scatter(x, y,           # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='orange',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='s',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

plt.title("Title")  # タイトル
plt.xlabel("x axis") # 軸名
plt.ylabel("y axis") #軸名
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(-2.5, 2.5)  # 横軸最小最大
plt.ylim(-2.5, 2.5)  # 縦軸最小最大

plt.show() # グラフの表示
<Figure size 500x400 with 1 Axes>

マーカの形について

  • . 点
  • o 円
  • v 下向き三角形
  • ^ 上向き三角形
  • 8 八角形
  • s 四角形
  • p 五角形
  • x ×印

マーカの色について

いっぱいあります。 https://matplotlib.org/examples/color/named_colors.html

複数の散布図を1つのグラフに重ねる方法

基本的には、並べればokです。重ねる回数が多くなる場合には、for文の利用も検討してください。複数重ねる場合は、どのデータかわかりやすくするために、色やマーカの形を変更させます。公表する論文誌がグレースケールでわかるようにせよ、とか指示が出てくるので、マーカの形を変えたほうが無難です。また、どのプロットがなんなのかわかりやすくするために、凡例をつけます。以下のlegendを参照してください。

In [3]:
# リストに、生成したデータを入れます。
x = [Dat01[:,0], Dat02[:,0], Dat03[:,0], Dat04[:,0]]
y = [Dat01[:,1], Dat02[:,1], Dat03[:,1], Dat04[:,1]]

plt.figure(figsize=(5, 4)) # figureの縦横の大きさ

# 1つ目
plt.scatter(x[0], y[0],      # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='orange',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='s',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

# 2つ目
plt.scatter(x[1], y[1],      # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='red',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='*',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

# 3つ目
plt.scatter(x[2], y[2],      # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='blue',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='^',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

# 4つ目
plt.scatter(x[3], y[3],      # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='black',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='o',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

plt.title("Title")  # タイトル
plt.xlabel("x axis") # 軸名
plt.ylabel("y axis") #軸名
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(-2.5, 2.5)  # 横軸最小最大
plt.ylim(-2.5, 2.5)  # 縦軸最小最大

plt.legend(["orange", "red", "blue", "black"], loc="upper right") # 凡例

plt.show() # グラフの表示

凡例の位置調整について:

  • ‘best’
  • ‘upper right’
  • ‘upper left’
  • ‘lower left’
  • ‘lower right’
  • ‘right’
  • ‘center left’
  • ‘center right’
  • ‘lower center’
  • ‘upper center’
  • ‘center’

凡例をグラフの外に出したい:

legendの中に、以下の引数を追加してください。はじめの数字が、右への移動量、2つ目の数字が上への移動量となります。

bbox_to_anchor=(1.5, 1)

個別の散布図を作り並べる方法

散布図を個別に作り、並べるためにはsubplotを用います。コードの中に、suplot(1,2,x)という記述があります。これは、1行2列の描画スペースを用意し、そのx番目にグラフを描きます、という意味を持ちます。1行2列なので、合計2枚のグラフをかくことが出来ますね。

In [4]:
# リストに、生成したデータを入れます。
x = [Dat01[:,0], Dat02[:,0], Dat03[:,0], Dat04[:,0]]
y = [Dat01[:,1], Dat02[:,1], Dat03[:,1], Dat04[:,1]]

# 描画領域の生成(2枚横に並べたいので、横の長さを広めにとる)
fig = plt.figure(figsize=(12, 4))

# 複数画像を保持するリストを生成

# *** Dat01 ***
plt.subplot(1,2,1)
plt.scatter(x[0], y[0],   # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='orange',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='s',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

plt.title("Dat01")  # タイトル
plt.xlabel("x axis") # 軸名
plt.ylabel("y axis") #軸名
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(-2.5, 2.5)  # 横軸最小最大
plt.ylim(-2.5, 2.5)  # 縦軸最小最大

# *** Dat02 ***
plt.subplot(1,2,2)
plt.scatter(x[1], y[1],   # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='red',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='*',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

plt.title("Dat02")  # タイトル
plt.xlabel("x axis") # 軸名
plt.ylabel("y axis") #軸名
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(-2.5, 2.5)  # 横軸最小最大
plt.ylim(-2.5, 2.5)  # 縦軸最小最大

plt.show()

4枚を並べたい場合には、以下のように描きます。2行2列で4枚を並べたいので、subplot(2, 2, x)とします。 別のグラフ同士が重なってしまう場合が多いので、
fig.subplots_adjust(wspace=0.3, hspace=0.4) # 横のグラフ同士の幅、縦のグラフ同士の幅
で調整します。

In [5]:
# リストに、生成したデータを入れます。
x = [Dat01[:,0], Dat02[:,0], Dat03[:,0], Dat04[:,0]]
y = [Dat01[:,1], Dat02[:,1], Dat03[:,1], Dat04[:,1]]

# 描画領域の生成
fig = plt.figure(figsize=(7, 7))
fig.subplots_adjust(wspace=0.3, hspace=0.4) # 横のグラフ同士の幅、縦のグラフ同士の幅

# *** Dat01 ***
plt.subplot(2,2,1)
plt.scatter(x[0], y[0],   # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='orange',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='s',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

plt.title("Dat01")  # タイトル
plt.xlabel("x axis") # 軸名
plt.ylabel("y axis") #軸名
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(-2.5, 2.5)  # 横軸最小最大
plt.ylim(-2.5, 2.5)  # 縦軸最小最大

# *** Dat02 ***
plt.subplot(2,2,2)
plt.scatter(x[1], y[1],   # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='red',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='*',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

plt.title("Dat02")  # タイトル
plt.xlabel("x axis") # 軸名
plt.ylabel("y axis") #軸名
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(-2.5, 2.5)  # 横軸最小最大
plt.ylim(-2.5, 2.5)  # 縦軸最小最大

# *** Dat03 ***
plt.subplot(2,2,3)
plt.scatter(x[2], y[2],   # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='blue',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='^',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

plt.title("Dat03")  # タイトル
plt.xlabel("x axis") # 軸名
plt.ylabel("y axis") #軸名
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(-2.5, 2.5)  # 横軸最小最大
plt.ylim(-2.5, 2.5)  # 縦軸最小最大

# *** Dat04 ***
plt.subplot(2,2,4)
plt.scatter(x[3], y[3],   # 横軸、縦軸の値
        s=30,               # マーカサイズ
        c='black',         # マーカの色(red, blue, pink, yellow, orange, blackなど)
        marker='o',         #マーカの形(.osなど:下記参照)
        alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
        linewidths=0.5,     # マーカの線の太さ
        edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

plt.title("Dat04")  # タイトル
plt.xlabel("x axis") # 軸名
plt.ylabel("y axis") #軸名
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(-2.5, 2.5)  # 横軸最小最大
plt.ylim(-2.5, 2.5)  # 縦軸最小最大

plt.show()

上のグラフは、以下のようにfor文を利用することで、まとめて描くことが出来ます。よくわからない場合は、for文とリストを復習してから解読してみてください。

In [6]:
# リストに、生成したデータを入れます。
x = [Dat01[:,0], Dat02[:,0], Dat03[:,0], Dat04[:,0]]
y = [Dat01[:,1], Dat02[:,1], Dat03[:,1], Dat04[:,1]]

col=["orange", "red", "blue", "black"]
figtitle=["Dat01", "Dat02", "Dat03", "Dat04"]

# 描画領域の生成
fig = plt.figure(figsize=(7, 7))
fig.subplots_adjust(wspace=0.3, hspace=0.4) # 横のグラフ同士の幅、縦のグラフ同士の幅

# for文を利用して、4つのグラフを描画
for i in range(0, len(x)):
    plt.subplot(2,2,i+1)
    plt.scatter(x[i], y[i],   # 横軸、縦軸の値
            s=30,               # マーカサイズ
            c=col[i],         # マーカの色(red, blue, pink, yellow, orange, blackなど)
            marker='s',         #マーカの形(.osなど:下記参照)
            alpha=0.8,          # 塗りつぶしの透明度(0:完全に透明〜1:透明にしない)
            linewidths=0.5,     # マーカの線の太さ
            edgecolors='black') # マーカの線の太さ(マーカの色と同じ。不要の場合は''なしでNone)

    plt.title(figtitle[i])  # タイトル
    plt.xlabel("x axis") # 軸名
    plt.ylabel("y axis") #軸名
    plt.grid(True)      #グリッド線(True:引く、False:引かない)
    plt.xlim(-2.5, 2.5)  # 横軸最小最大
    plt.ylim(-2.5, 2.5)  # 縦軸最小最大

plt.show()

条件によってマーカの色を変える

分析しているとき、条件によってマーカの色を変えたいときがかなりよくあります。このテクニックについて説明します。まず、以下のデータを見てください。

In [7]:
plt.figure(figsize=(5, 4)) # figureの縦横の大きさ

plt.scatter(Dat01[:,0], Dat01[:,1], s=30, c='orange', marker='s')
plt.grid(True)      #グリッド線(True:引く、False:引かない)
plt.xlim(0, 2)  # 横軸最小最大
plt.ylim(0, 2)  # 縦軸最小最大

plt.show() # グラフの表示

横軸が1.00以上のものだけ青にしてみたいとします。以下のコードを実行して見てください。Dat01の0列目のデータの中から、1を超える部分だけ、Trueが返されます。

In [8]:
print(Dat01[:,0] > 1)
[ True  True False False False False False  True  True  True False  True
  True False False  True False False  True False False  True  True  True
  True  True  True  True  True  True  True  True False  True  True False
 False  True  True  True False  True False False  True  True  True False
 False False False  True False  True False  True False  True  True  True
 False  True False  True  True False  True  True  True  True False False
 False  True False  True  True  True False  True False  True  True False
 False  True  True  True  True  True  True False  True  True  True False
  True  True  True False]

なので、Dat01[Dat01[:,0] > 1, 0]と指定すると、Dat01の0列目の、1以上のデータのみが対象となります。

In [9]:
print(Dat01[Dat01[:,0]>1,0])
[1.16071305 1.44933287 1.27451983 1.37599438 1.16948611 1.03482209
 1.40806995 1.27962605 1.00422634 1.3292284  1.20890273 1.52485659
 1.24947684 1.01303349 1.01305396 1.69508303 1.51712685 1.4233182
 1.30794037 1.31439776 1.44261479 1.15152967 1.36395452 1.41742337
 1.25845531 1.33657421 1.05836988 1.48692924 1.62882788 1.37882444
 1.18839716 1.06891964 1.20390748 1.14875014 1.37553995 1.53061079
 1.08133315 1.01482592 1.16096801 1.10040483 1.1620017  1.51449695
 1.03890041 1.09481577 1.35717873 1.21208018 1.61593038 1.15377488
 1.39588222 1.21295274 1.3774472  1.61716691 1.13847197 1.31820832
 1.04290102 1.00860748 1.10475244 1.0353907  1.57063962 1.04630418
 1.02821804]

このように、Dat01の0列目の、1以上のデータだけが抜き出されています。これを応用すると、以下のように条件を満たしているときだけ色を変える、ということも容易に行えます。ちょっと複雑ですが、気合いを出して解読して見ましょう!

In [10]:
plt.figure(figsize=(5, 4)) # figureの縦横の大きさ

# 横軸の値(0列目)が0未満のデータのみを散布図として描画
plt.scatter(Dat01[Dat01[:,0] < 1, 0], Dat01[Dat01[:,0] < 1, 1], s=30, c='orange', marker='s')
plt.grid(True)
plt.xlim(0, 2)
plt.ylim(0, 2)

# 横軸の値(0列目)が0以上のデータのみを散布図として描画
plt.scatter(Dat01[Dat01[:,0] >= 1, 0], Dat01[Dat01[:,0] >= 1, 1], s=30, c='blue', marker='s')
plt.grid(True)
plt.xlim(0, 2)
plt.ylim(0, 2)

plt.show() # グラフの表示

散布図を利用したデータ分析の演習

Aさんは、どういう人が試験で高い点数を取れるのか調べてみることにしました。仮説として、授業に出た回数と、試験前の勉強時間が重要だと考え、以下のデータを集めました。(架空のデータです)

  • X1:授業の出席回数
  • X2:試験前の勉強時間
  • Y:試験得点
In [11]:
# リストで定義(本当はcsvでデータを保存しておき、それをロードしますが。。。)
X1 = [3,  1,  2,  5,  3,  9,  8,  7,  4,  3,  9,  6,  9,  8,  9,  1,  1,  2,  6,  6,  8]
X2 = [7,  1,  1,  3,  4,  1,  2,  9,  2,  1,  3,  5,  9,  5,  1,  7,  8,  5,  7,  1,  1]
Y  = [68, 23, 29, 58, 56, 52, 54, 89, 21, 32, 63, 73, 98, 73, 43, 45, 62, 63, 49, 35, 65]

# ndarray型にキャスト
import numpy as np
X1 = np.array(X1)
X2 = np.array(X2)
Y  = np.array(Y)

データだけを見ても、よくわかりません。傾向を掴むために、60点未満を赤、60点以上を青にして、散布図を描いて見ます。

In [12]:
plt.figure(figsize=(5, 4))

# 60点未満のプロット
plt.scatter(X1[Y<60], X2[Y<60], c="red", marker="o")
plt.grid(True)
plt.xlim(0, 10)
plt.ylim(0, 10)
plt.xlabel("Number of Attending Lectute")
plt.ylabel("Studing Time")

# 60点未満のプロット
plt.scatter(X1[Y>=60], X2[Y>=60], c="blue", marker="s")
plt.grid(True)
plt.xlim(0, 10)
plt.ylim(0, 10)
plt.xlabel("Number of Attending Lectute")
plt.ylabel("Studing Time")

plt.legend(["less than 60", "over 60"], loc="upper right", bbox_to_anchor=(1.5, 1))
plt.show()

データの羅列を見るだけではよくわかりませんでしたが、7回以上授業に出席した方が良い、5時間以上は勉強した方が良いなどのことがわかりました。このように、散布図はシンプルな分析手法ですが、割と強力なので良く使います。

別の方法として、scatterの引数のcにYを入れ、cmapに色合いを指定する方法があります。濃度が試験得点を表すわけです。先ほどとは異なり少し見にくいですが、情報量は多くなります。

In [13]:
plt.figure(figsize=(5, 4))

plt.scatter(X1, X2, c=Y, cmap="Blues", marker="o")
plt.grid(True)
plt.xlim(0, 10)
plt.ylim(0, 10)
plt.xlabel("Number of Attending Lectute")
plt.ylabel("Studing Time")
plt.colorbar()
Out[13]:
<matplotlib.colorbar.Colorbar at 0x119f3a630>

作成した画像を保存するには、plt.show()を使わずに、以下のように書けばokです。

In [14]:
plt.savefig('figure.png')
plt.savefig('figure.pdf')
<Figure size 432x288 with 0 Axes>

3次元散布図

別の表し方として、以下のように書くことで、3次元の散布図を描くことができます。ただし、傾向を読み取るのが難しくなるので、(立体的でかっこいいので、なんか分析してるっぽい感じはしますが)あまりおすすめしません。

In [15]:
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(7, 5))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X1, X2, Y)
ax.set_xlabel("Number of Attending Lectute")
ax.set_ylabel("Studing Time")
ax.set_zlabel("Academic Score")
ax.set_title("3D plot")
plt.show()