決まったら考えるよ。

思いついたことそのまま書く。髭剃り、読書、仕事、考えたこと、調べたことを備忘録代わりに。慶應通信で大学生もやっています。

慶應通信の統計学レポートを作るときに必死こいて調べたmatplotlibを使ったグラフ作成の覚書

f:id:siuso:20200322112249p:plain

なんでわざわざmatplotlibでグラフ描くの?と、問われれば「なんかカッコイイと思ったから」以上の答えが用意できていない。そしてさらに、僕はPythonをマスターしたエンジニアでもなければなんでもない。だから内容はおそらくPythonエンジニアの方々からしたら鼻くそみたいなレベルのものなのであろう。それにまだまだレポートが全部終わった訳ではないので、この記事の内容で全部グラフ描けるぜ!という次元には至らない。でもこのまま統計学で作成するレポートでmatplotlibを使った軌跡は自身の備忘を目的としてここに残しておこうかと思う。大学のレポートとかをPythonが得意な訳でもなんでもないけどmatplotlibで描いてみようとか考えたすげぇ特殊なケースの人が居るかもしれな…居ないか。でも、でもね!階級幅が違うヒストグラムを描くのであれば絶対matplotlibのが楽だから!絶対!w



スポンサーリンク


普通のヒストグラムの書き方

 matplotlibでヒストグラムを描くのはそう難しくない。あるデータの集合A_histがリストで存在するとすると、

# 必要なものをインポート
import matplotlib.pyplot as plt

# グラフ描画のおまじない
fig = plt.figure()
ax = fig.add_subplot(1,1,1)

# A_histのリスト内容をヒストグラムで描画、線は黒
ax.hist(A_hist,ec='black')

これだけ。でもこの描画方法だと、

  • 区間が自動で切られてしまうので、階級図が与えられている時に使えない
  • 実数でしか描けないので、正規化(%表示)するのにまた違うリスト作らなきゃいかんやん

といったあたりが課題として挙げられる。なので、まずは正規化する方法に関して。


正規化したヒストグラム

 正規化するのに別に違うデータを用意しなくても大丈夫。さっきのヒストグラムのコードにdensity=Trueって追記するだけ。


追記前 ~ before ~

ax.hist(A_hist,ec='black')


追記後 ~ after ~

ax.hist(A_hist,ec='black',density=True)

これだけで、勝手に割合計算して正規化されたヒストグラムが完成する。あら便利(Excelも簡単に正規化出来るけどさ)


軸をパーセント表記にしたいんだよ!

 前項までで正規化したヒストグラムは完成したが、実はひとつ足りない。縦軸の数値が小数点表記なのだよ。正規化したんだから、パーセント表記じゃなきゃダメでしょ!という訳でそちらの対応。対応方法は、ちょろっとコードを書き足すだけ。

# 必要なものを追加インポート
import matplotlib

# A_histのリスト内容をヒストグラムで描画、線は黒
ax.hist(A_hist,ec='black',density=True)

# Y軸をパーセント表記に
ax.yaxis.set_major_formatter(
  matplotlib.ticker.PercentFormatter(1.0)
  )

追加でimport matplotlibを行ったのち、yaxis.set_major_formatter()を使ってY軸をパーセント表記に変えている。多分このyaxisxaxisにしたらX軸が変えれるのかな?変えたことないからわからんけどw


階級幅が違うヒストグラム

 ただし、このヒストグラムでは階級幅が異なる階級表からヒストグラムを作ることがうまくできない。だってmatplotlibでヒストグラム作ると適当(いい意味でも悪い意味でも)な階級幅でいい感じに作ってくれちゃうから。なので、階級幅を自分で定義したヒストグラムを作るにはどうすれば良いのか。それは…


binsを指定してやれば良い


これだけ。だけどここに行き着くのに散々ネットの海を彷徨ったよ…。実際のコードはこちら。

# 階級幅をリストに格納する。
# 下のリストは、50-53,53-61,…,72-80という階級幅を表す
edges = [50,53,61,64,65,69,72,80]

# ヒストグラム描画コードにbinsを渡す
ax.hist(A_hist,bins=edges,ec='black')

と、こうなる。普通のヒストグラム作成のときにもbinの数を指定する際にとか使ったよね。このパラメータ。


Excelのデータラベル的な文字を付ける

 Excelでグラフを描くと、右クリック一発で「データラベルの作成」が選べて、グラフの上端にその数値が出たりなどなど、素敵な処理が簡単に出来る。ではmatplotlibでそれは出来るのか。答えは…


出来る。けどちょっと面倒くさい


となる。まぁ結局やることはplt.textを使って直接文字を打ち込むだけなんだけど。ちなみにこのplt.textは覚えておくと便利なのでちょっとだけ解説。

plt.text(x座標,y座標,'表示させたいテキスト')

これだけでグラフの任意の場所にテキストを表示させられる。だから本項の目的であるデータラベル的な文字列表示も、位置さえ分かれば自由自在ですよ。位置さえ分かれば…。という訳で、位置の特定に進む。

位置の特定は、ax.hist()の戻り値を使うことで可能。ax.hist()は3つの戻り値を返すので先ほどのコードをちょっと変えてやる。

n, bins, _ = ax.hist(A_hist,bins=edges,ec='black')

これはax.hist()から貰える戻り値を3つの変数に格納している。最後の_は使わないから「使わないぜこの変数!」という硬い意思が見え隠れする変数名にしておいた。で、nは「n番目のbinはこの高さよ」というリストを返してくれて、binsはbinsそれぞれの左側のx座標と右側のx座標をリストで返してくれる。だから、先に使ったedgesのリストで説明すると…。

edges = [50,53,61,64,65,69,72,80]

というリストで描いたヒストグラムにおけるbinsの戻り値リストは、

  • 1番目の左側のx座標
  • 2番目の左側のx座標(=1番目の右側のx座標)
  • 3番目の左側のx座標(=2番目の右側のx座標)
  • n番目の左側のx座標(=n-1番目の右側のx座標)
  • n番目の右側のx座標

というものになる。で、データラベル的なものはbinsの上、真ん中あたりにテキストを表示させたい訳なので、

n番目のbinsのデータラベルとしてテキストをプロットするx座標は、binsのn-1番目とn番目の平均を取った数値

という結論に至る。んでy座標はそのままヒストグラムの高さのちょっと上なので、ヒストグラムのnのリストに格納されているY座標の数値にちょっと数値を足してやることにする。だから実際のコードは…

### グラフ描画しつつ、戻り値を取得
n, bins, _ = ax.hist(A_hist,bins=edges,ec='black',density=True)

### 前の行で取得したbinsを使って平均算出
xs = (bins[:-1] + bins[1:])/2

### 繰り返し処理を使ってテキストを表示していく
### shareのリストは全体の何パーセントかの数値が格納されている
### 表示テキストはパーセント
for x, y in zip(xs, n):
    plt.text(
    x, y + 0.005,
    '構成比\n {:.1%}'.format(share[z]),
    horizontalalignment='center'
    )
    z = z + 1


f:id:siuso:20200322112643p:plain


最終的に出来上がったヒストグラムはこんな感じ。(レポートで使ったグラフなので、数値はボカしてあります…)何か疑問が発生する度にググってみては失敗して、ググってみては失敗して…を繰り返したので、いっそ手書きでヒストグラム作った方が楽だったんじゃいないのかという説すら自身で否定できなかったw

いつまでこの熱が続くかなぁ…Excelに逃げちゃいそうだなぁ…。

シナジーがあるとは思えないけどシリーズになりそうな慶應通信 × Pythonシリーズはこちら。


www.siuso.online


Pythonによるデータ分析入門 第2版 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 第2版 ―NumPy、pandasを使ったデータ処理

  • 作者:Wes McKinney
  • 発売日: 2018/07/26
  • メディア: 単行本(ソフトカバー)