akiyoko blog

akiyoko の IT技術系ブログです

Pythonで散布図

今回は、Pythonで散布図を描こうと思います。
内容的には「Pythonでヒストグラム」の続編のような形になっていますのでご注意を。

相関係数を求める

シチュエーションとしては、ExcelのセルF10から下にデータ(テストの点数)がずらっと並んでいて、それと対になるデータ(エラー度合いの合計)が隣り合ったセルG10から下に同じく並んでいるものとします。


まずは、xlrd を使ってExcelから対象のデータを抜き出し、相関係数を求めてみます。
相関係数は、Numpy の corrcoef() を使って求めます。

test_corrcoef.py

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import numpy
import xlrd


def get_data(sheet, rowx, colx):
    data = []
    for row in range(rowx, sheet.nrows):
        value = sheet.cell(row, colx).value
        if value != '':
            data.append(value)
    data = numpy.array(data)
    return data


if __name__ == '__main__':
    book = xlrd.open_workbook('/Users/akiyoko/Documents/temp/2nd_test.xls')
    sheet = book.sheet_by_name('Statistics (total score)')
    data1 = get_data(sheet, 9, 5)  # データの起点はF10
    data2 = get_data(sheet, 9, 6)  # データの起点はG10
    print 'data1=%s' % data1
    print 'size of data 1=%d' % len(data1)
    print 'mean value of data 1=%.1f' % numpy.mean(data1)
    print 'data2=%s' % data2
    print 'size of data 2=%d' % len(data2)
    print 'mean value of data 2=%.0f' % numpy.mean(data2)
    # 相関係数
    print 'coefficient of correlation=%.2f' % numpy.corrcoef(data1, data2)[0, 1]

numpy.corrcoef(x, y)[0, 1] は、numpy.corrcoef(x, y)[0][1] と書いてもいいようです(Pythonネイティブの配列だとエラーになります)。

実行結果

$ python test_corrcoef.py 
data1=[ 42.  42.  40.  38.  38.  38.  38.  35.  35.  35.  35.  34.  34.  34.  34.
  33.  33.  33.  33.  32.  32.  32.  30.  30.  30.  30.  30.  30.  30.  30.
  29.  29.  29.  28.  28.  28.  28.  28.  27.  27.  27.  27.  27.  27.  26.
  26.  26.  26.  26.  26.  25.  24.  24.  24.  24.  23.  23.  23.  23.  23.
  23.  23.  23.  23.  22.  22.  22.  22.  22.  22.  22.  22.  21.  21.  20.
  20.  20.  20.  20.  19.  19.  19.  19.  19.  18.  18.  17.  17.  16.  16.
  16.  16.  15.  15.  15.  15.  14.  13.  13.  13.  13.  12.  12.  11.   9.]
size of data 1=105
mean value of data 1=24.9
data2=[  576.   668.   682.   795.   814.  1046.  1414.  1084.  1107.  1196.
  1533.  1205.  1327.  1427.  1434.  1575.  1578.  1683.  1772.  1522.
  1585.  1665.  1542.  1642.  1677.  1795.  1813.  1824.  1865.  1980.
  1483.  1830.  2257.  1763.  2120.  2207.  2712.  2828.  1793.  1846.
  1959.  2049.  2124.  2677.  2223.  2236.  2511.  2910.  3028.  3371.
  2528.  2196.  2274.  2469.  2539.  2155.  2158.  2474.  2601.  2733.
  2793.  2863.  3398.  3585.  1984.  2413.  2574.  2752.  2892.  3212.
  3318.  3794.  2839.  3406.  2435.  2571.  3011.  3133.  3265.  2506.
  2931.  2983.  3455.  3609.  2895.  3586.  3722.  3846.  2883.  2937.
  3213.  3456.  3161.  3623.  3888.  4035.  4400.  4051.  4364.  4533.
  4825.  4063.  4289.  3762.  4453.]
size of data 2=105
mean value of data 2=2510
coefficient of correlation=-0.92

相関係数は「-0.92」と求めることができました。

散布図描画

散布図を描画するには Axesクラスの scatter() を使うのですが、これまたパラメータが煩雑。実際に使いながら理解していくしかありません。
自分なりにまとめると、こんな感じです。

scatter(x, y, s=20, c='b', marker='o', cmap=None, norm=None,
        vmin=None, vmax=None, alpha=None, linewidths=None,
        verts=None, **kwargs)

x: データ1
y: データ2
s: プロットのサイズ
c: プロットの色(注1)
marker: マーカーの形(注2)

注1. cで色を指定するだけだと枠線がデフォルト色で描画されてしまう。
colorだと、内側の塗りと枠線を一緒に指定できる。
なお、内側の塗りと枠線の色を分けたい場合は、cやcolorを使わず、facecolor='r', edgecolor='r' などとすればよい。
注2. マーカーの形は以下が使い勝手がよさそうだった。
'o' circle
'x' x
'D' diamond
'+' plus
'v' triangle_down
'<' triangle_left
'>' triangle_right
'^' triangle_up
http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.scatter より)

サンプル

実際に作ったサンプルコードです。

test_scatter.py

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import numpy
import xlrd
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt


def get_data(sheet, rowx, colx):
    data = []
    for row in range(rowx, sheet.nrows):
        value = sheet.cell(row, colx).value
        if value != '':
            data.append(value)
    data = numpy.array(data)
    return data


if __name__ == '__main__':
    book = xlrd.open_workbook('/Users/akiyoko/Documents/temp/2nd_test.xls')
    sheet = book.sheet_by_name('Statistics (total score)')
    data1 = get_data(sheet, 9, 5)  # データの起点はF10
    data2 = get_data(sheet, 9, 6)  # データの起点はG10
    print 'data1=%s' % data1
    print 'size of data 1=%d' % len(data1)
    print 'mean value of data 1=%.1f' % numpy.mean(data1)
    print 'data2=%s' % data2
    print 'size of data 2=%d' % len(data2)
    print 'mean value of data 2=%.0f' % numpy.mean(data2)
    # 相関係数
    r = numpy.corrcoef(data1, data2)[0, 1]
    print 'coefficient of correlation=%.2f' % r

    # 共通初期設定
    plt.rc('font', **{'family': 'serif'})
    # キャンバス
    fig = plt.figure()
    # プロット領域(1x1分割の1番目に領域を配置せよという意味)
    ax = fig.add_subplot(111)
    # 散布図
    sc = ax.scatter(data1, data2, s=25, marker='x', color='b')
    # X, Y方向の表示範囲
    ax.set_xlim(0, 50)
    ax.set_ylim(0, 6000)
    # タイトル
    ax.set_title('Scatter Graph: r=%.2f' % r, size=16)
    ax.set_xlabel('Score', size=14)
    ax.set_ylabel('Error Severity', size=14)
    # 凡例
    ax.legend((sc,), ('2nd Test',), scatterpoints=1, loc='upper left', fontsize=10)
    # グリッド表示
    ax.grid(True)
    plt.show()

実行結果

$ python test_scatter.py 
data1=[ 42.  42.  40.  38.  38.  38.  38.  35.  35.  35.  35.  34.  34.  34.  34.
  33.  33.  33.  33.  32.  32.  32.  30.  30.  30.  30.  30.  30.  30.  30.
  29.  29.  29.  28.  28.  28.  28.  28.  27.  27.  27.  27.  27.  27.  26.
  26.  26.  26.  26.  26.  25.  24.  24.  24.  24.  23.  23.  23.  23.  23.
  23.  23.  23.  23.  22.  22.  22.  22.  22.  22.  22.  22.  21.  21.  20.
  20.  20.  20.  20.  19.  19.  19.  19.  19.  18.  18.  17.  17.  16.  16.
  16.  16.  15.  15.  15.  15.  14.  13.  13.  13.  13.  12.  12.  11.   9.]
size of data 1=105
mean value of data 1=24.9
data2=[  576.   668.   682.   795.   814.  1046.  1414.  1084.  1107.  1196.
  1533.  1205.  1327.  1427.  1434.  1575.  1578.  1683.  1772.  1522.
  1585.  1665.  1542.  1642.  1677.  1795.  1813.  1824.  1865.  1980.
  1483.  1830.  2257.  1763.  2120.  2207.  2712.  2828.  1793.  1846.
  1959.  2049.  2124.  2677.  2223.  2236.  2511.  2910.  3028.  3371.
  2528.  2196.  2274.  2469.  2539.  2155.  2158.  2474.  2601.  2733.
  2793.  2863.  3398.  3585.  1984.  2413.  2574.  2752.  2892.  3212.
  3318.  3794.  2839.  3406.  2435.  2571.  3011.  3133.  3265.  2506.
  2931.  2983.  3455.  3609.  2895.  3586.  3722.  3846.  2883.  2937.
  3213.  3456.  3161.  3623.  3888.  4035.  4400.  4051.  4364.  4533.
  4825.  4063.  4289.  3762.  4453.]
size of data 2=105
mean value of data 2=2510
coefficient of correlation=-0.92

こんな感じでグラフが出力されます。
f:id:akiyoko:20130610022543p:plain