matplotlib(読み方:マットプロットリブ)はプログラミング言語pythonで最も使われている可視化・グラフ作成用のライブラリです.既に登場以来10年以上もの間多くの人たちによって使われてきましたし,これからも使われ続けるでしょう.そのため一端使い方を理解すれば,これからpythonを使い続ける限り,陳腐化しない・役に立つスキルと言って差し支えないでしょう.
本記事ではmatplotlibで作成可能なグラフの種類について,具体例を交えながら解説していきます.
matplotlibとは
matplotlibは名前からも想像出来るとおり,市販の科学技術計算ソフト「MatLab」を強く意識した操作体系になっていますので,MatLab経験者には取っつきやすく,またpythonへの移行を実際に促しています.
matplotlibでは非常に詳細な描画設定が可能なため,慣れれば自分の思い通りにグラフを描くことができるようになります.
論文にも使えるようなpublication qualityの図が作成可能なことが特長になっていて,実際にmatplotlib公式ページのギャラリーにはたくさんのきれいなグラフが並んでいます.このギャラリーにはソースコードも併記されていますので,自分が作成したいグラフに近いものを見つけてソースコードを読んでみるのが上達の近道だと思います.
とはいえ,最初からソースコードを読んでいくのは初心者には難しいと思います.そこで「matplotlibの初心者が実際に論文クオリティの図を作成する」ために必要な知識の習得を目標に,これから数回に分けてmatplotlibの基本を紹介していきたいと思います.
必要なライブラリの準備
まずは必要なライブラリをimportしておきましょう.
import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt print(mpl.__version__) # 2.1.2
pyplotをplt,numpyをnpとimportするのがpythonデータサイエンス界隈での標準となっていますので,このブログでもそれに倣います.こうすることで他の人のコードを読む場合にも,とっかかりよく入り込むことができます.
スタイルの設定
matplotlibのグラフは論文にも使用可能な品質という触れ込みですが,初期設定のまま作成したグラフは正直やぼったいです.
細かい設定の方法などは次回以降に扱うことにして,手っ取り早く見た目を変える方法をここでは紹介します.実際に多くの人がmatplotlibのデフォルトの設定に不満を持っていますので,matplobllibをグラフ作成時の描画エンジンとしつつも見た目を変えるライブラリの開発が行われています.
すでにpythonの可視化ライブラリといえばものすごい数になっていますが,その中で最も有名なのがseabornというライブラリです.seabornは見やすい色使いに加えて特に統計分析に有用なグラフ作成機能を提供している優れたライブラリで,本連載でも後ほど扱う予定です.
また統計解析プログラミング言語Rでよく使われるggplot2に類似したpython用のライブラリも存在しますので,R使いの人がpythonへと移行した場合には使いやすいと思います.
もちろんこれらのライブラリをimportとして使うのも一つの方法になりますが,見た目だけを変えたい場合にはこういったライブラリが提供する描画スタイルだけを設定するのが一番楽です.
使用可能なスタイル例
使用可能なスタイルはstyle.availableで取得可能です.既にseabornがインストールされている場合には使用可能なスタイルが大幅に増えます.私の現在の環境では25種類のスタイルが利用可能なようです.
len(mpl.style.available) # 25
seabornのスタイルシート
seaborn系のスタイルシートは,
- 描画領域関係(グリッド)seaborn.set_style
- カラーパレット関係seaborn.set_color_codes
- グラフサイズ関係seaborn.set_context
の3つに大きく分かれています.
matplotlibではこれらが区別なくスタイルとして扱われていることに注意しながら,通常はこれらを組み合わせて用います.いずれseabornを取り上げる際にまた詳しく解説します.因みに論文用の想定と思われる‘seaborn-paper’は非常の文字が小さいので個人的にはおすすめしません.
スタイル種類 | スタイルシート |
---|---|
標準(darkgrid+deep) | ‘seaborn’ |
グリッド(style) | ‘seaborn-whitegrid’ |
‘seaborn-darkgrid’ | |
‘seaborn-white’ | |
‘seaborn-dark’ | |
‘seaborn-ticks’ | |
カラーパレット(color_codes) | ‘seaborn-deep’ |
‘seaborn-muted’ | |
‘seaborn-pastel’ | |
‘seaborn-bright’ | |
‘seaborn-dark-palette’ | |
‘seaborn-colorblind’ | |
グラフサイズ(context) | ‘seaborn-paper’ |
‘seaborn-notebook’ | |
‘seaborn-talk’ | |
‘seaborn-poster’ |
スタイルを使用した具体例
mpl.style.context(style_name)
ここでは利用可能なスタイルから15種類ほどを選んで,グラフを表示してみます.下の例ではグラフを4×4のグリッドに並べて見やすくしていますが,次々回辺りで詳しく扱う予定ですのでここでは気にしなくて構いません.
mpl.style.contextを利用することでwithブロック内でスタイルが変更されているということがなんとなくわかって頂ければよいです.
styles = ['ggplot', 'seaborn-muted', 'seaborn-whitegrid', 'Solarize_Light2', 'dark_background', 'seaborn-darkgrid', 'seaborn-pastel', 'seaborn-dark-palette', 'seaborn-deep', 'grayscale', 'classic', 'fast', 'bmh', 'seaborn', 'fivethirtyeight'] fig = plt.figure(figsize=(15,15)) for i, style in enumerate(styles, start=1): with mpl.style.context(style, after_reset=True): ax = fig.add_subplot(4,4,i) ax.plot(np.random.rand(10).cumsum()) ax.plot(np.random.randint(10,size=10)) ax.set_title('style: {}'.format(style))
使用するスタイルによって
- 背景の色
- グリッドの有無
- 線の太さ
- フォントの大きさ
- カラーパレット
など多岐にわたって変更が加えられているのが見て取れると思います.
また勘の良い方はわかったかもしれませんが,こういった要素を個別に設定していくことで自分だけのスタイルを設定していくことも可能です.この記事は初心者向けですのでここでは扱いませんが,興味を持った場合には公式ドキュメント「Customizing plots with style sheets」を参照してみてください.
以下の具体例ではseabornスタイルを使っていきますmpl.style.use(‘seaborn’).
また描画するデータセットをnumpyのランダムモジュールを使って乱数で作成していきます.
作成可能なグラフ
今回とりあげるグラフの種類は,
- 折れ線グラフ
- 散布図
- 棒グラフ
- ヒストグラム
- 円グラフ(パイチャート)
- 箱ひげ図
- バイオリンプロット
になります.
それでは,それぞれ詳細に見ていきましょう.
折れ線グラフ
最も基本的なグラフです.dataはリストに類似した形で渡します.リストが1つだけの場合には,リストの値はy軸の値に使用され,x軸は0,1,2…と自動で割り振られます.
plt.plot([1,3,7]) plt.plot([2,2,6]) plt.show()
リストを2つ渡した場合には,1つめのリストがx座標に,2つめのリストがy座標に使われます.
plt.plot([0,1,4], [2,8,4]) plt.plot([1,3,5], [4,4,0]) plt.show()
線の種類とマーカーの設定
折れ線グラフでは線の種類・太さを変えられる他,各点にマーカーを付けられます.
項目 | オプション |
---|---|
線の種類 | linestyle (ls) |
線の太さ | linewidth (lw) |
線の色 | color (c) |
線の種類は以下の4種類が選べます.
線の種類 | 記号 |
---|---|
実線 | ‘-‘ |
破線 | ‘- -‘ |
ブロック線 | ‘-.’ |
点線 | ‘:’ |
マーカーの種類はmarkerで指定できますが,次の散布図で詳しく説明します.また色・マーカー・線の種類を同時に指定可能なフォーマット形式が利用可能です.
この形式を用いると,青の実線‘b-‘や赤の点線に四角いマーカー‘rs:’などと一気に指定可能です.なお線の種類を指定せずにマーカーを指定することで,形式的に散布図を描くことも可能です.各プロットにlabelを設定することで凡例を表示することができます.
plt.plot(range(5), np.random.rand(5).cumsum(), 'o-', label='o-') plt.plot(range(5), np.random.rand(5).cumsum(), '--', label='--') plt.plot(range(5), np.random.rand(5).cumsum(), '-.', label='-.') plt.plot(range(5), np.random.rand(5).cumsum(), ':', label=':') plt.plot(range(5), np.random.rand(5).cumsum(), lw=3, label='lw=3') plt.plot(range(5), np.random.rand(5).cumsum(), 's', ms=5, label='scatter') plt.legend() plt.show()
散布図
散布図にはx座標のデータとy座標のデータをリスト形式で渡します.markerオプションで表示するマーカーの形を指定します.
使用できるマーカーの種類
散布図ではデフォルトで用いられるマーカーは丸‘o’になります.他にも色々種類がありますので,ここでは私がよく使うもの(=コードからすぐに形が想像できるもの)をまとめておきます.
これ以上の種類が必要なことは実用上ほとんどないと思われますが,詳細なリストについては公式ドキュメント「matplotlib.markers」を参照してください.
コード | マーカーの形 |
---|---|
‘o’ | 丸 |
‘v’ | 下三角形 |
‘^’ | 上三角形 |
‘<‘ | 左三角形 |
‘<‘ | 右三角形 |
‘s’ | 四角形 |
‘p’ | 五角形 |
‘h’ | 六角形 |
‘*’ | 星型 |
‘+’ | プラス |
‘x’ | バツ印 |
下のコードではデフォルトの丸に加えて,四角形に変えた上でマーカーの大きさも変更してプロットしています.
plt.scatter(np.random.randint(10,size=10), np.random.randint(10,size=10)) plt.scatter(np.random.randint(5, size=10), np.random.randint(7, size=10), s=100, marker='s') plt.show()
カラーバー:マーカーの色を用いた3次元データの可視化
散布図では各マーカーの色に幅を持たせる(=z軸のデータに対応)ことで,3次元のデータを可視化することが可能になります.表現方法が大幅に広がりますので是非ともマスターしたい方法です.
下の例では各点に0,1,2,3,,,19と値を与えることで,マーカーに白から黒へと徐々に濃淡をつけています.カラーバーはpyplot.colorbar()で表示可能です.
plt.scatter(np.random.rand(20), np.random.rand(20).cumsum(), c=range(20)) plt.colorbar() plt.show()
カラーバー:カラーマップと範囲の設定
先ほどの例では白=0,黒=19とした上で濃淡をつけてマーカーを表示しました.カラーマップcmapを設定することで,この色合いを変更することが可能になります.色々なパレットを使用可能ですので,公式ドキュメント「Choosing Colormaps」を眺めて是非とも自分のお気に入りを探してみてください.
下の例では‘Paired’というパレットを使っています.
カラーバーを用いた場合,通常は自動的に最小値から最大値へと徐々にグラディエーションがかけられます.
しかし,例えばテストの点数のように例えば0点と100点の人がいなかったとしても,これら最小・最大値から始めたカラーマップを作りたい場合が多々あると思います.そういった場合には下の2つめの例のように,正規化を行うことで実現可能です.
2つのグラフのカラーバーを比べてみると,同じzのデータにも関わらず色合いに違いがあることがわかると思います.
z = np.random.randint(1000, size=20) plt.scatter(np.random.rand(20), np.random.rand(20).cumsum(), c=z, cmap='Paired') plt.colorbar() plt.show()
plt.scatter(np.random.rand(20), np.random.rand(20).cumsum(), c=z, norm=mpl.colors.Normalize(0,1000), cmap='Paired') plt.colorbar() plt.show()
棒グラフ
棒グラフの描画には,棒を表示する位置とその高さを指定します.デフォルト設定では指定位置を中心align=’center’に横幅0.8width=0.8の棒が表示されます.
plt.bar(range(4), [x**2 for x in range(1,5)]) plt.show()
align=’edge’を設定すると指定した位置を始点として指定幅の棒を表示します.下の例では幅にマイナスの値を指定しているので,開始点の左側に棒が表示されています.
plt.bar(x=range(4), width=-0.5, height=[2*x for x in range(1,5)], align='edge') plt.show()
横並びの棒グラフ
先ほどの開始点と横幅の設定を応用することで,複数データの棒を並列したグラフを描くことが出来ます.
下の例ではまず始点の左側に棒を描き,続いて同じ始点の右側に棒を描くことで隣り合わせの棒グラフを実現しています.
3つ以上のデータを並べたい場合はもう少し複雑になりますので,事前に作りたい図のイメージを紙などに描いてからコードに落とし込む方がいいと思います.
plt.bar(x=range(4), width=-0.4, height=[x**2 for x in range(1,5)], align='edge') plt.bar(x=range(4), width=0.4, height=[x*3 for x in range(1,5)], align='edge') plt.show()
積み上げ型棒グラフ
横並びではなくデータを積み上げたい場合には少し工夫が必要です.AとBのデータを重ね合わせたい場合には,まず「A+B」の棒グラフを描きます.続いて同じ始点から同じ横幅で「A」の棒グラフを描きます.
重なり合った場合には後から描いた部分が表示されますから,形式上はAのデータの上にBのデータが積み重なったグラフが得られます.また今回は棒のラベルとしてtick_labelを設定しています.
plt.bar(x=range(4), width=0.4, height=[x**2+x3 for x in range(1,5)]) plt.bar(x=range(4), width=0.4, height=[x3 for x in range(1,5)], tick_label=['A', 'B', 'C', 'D']) plt.show()
横向きの棒グラフ
横向きのグラフを描く場合にはbarhというメソッドを使います.縦向きとの違いは棒の位置がyに変わり,棒の長さがwidthに,棒の幅がheightに変わる点です.コマンドの英語の意味を考えればすぐに理解できますね.
plt.barh(y=range(4), width=[x*2 for x in range(1,5)], height=0.6) plt.show()
ヒストグラム
ヒストグラムは階級ごとにデータをまとめ,その頻度を棒グラフにしたものになります.データをどのような範囲で分けるか,または何個の区分に分けるかを設定することで得られるグラフの見た目が変わってきます.
統計分析を行うにあたって非常によく使われるグラフになりますので,しっかりと理解しておきましょう.
複数のデータ
複数のデータセットを含むリストを与えると,横に並んだヒストグラムが得られます.
plt.hist([np.random.rand(100), np.random.rand(50)], bins=10) plt.show()
横に並べるのではなく,複数のデータを積み重ねたい場合にはstacked=Trueのオプションを使います.
plt.hist([np.random.randint(10, size=50), np.random.randint(10, size=100)], bins=10, stacked=True) plt.show()
相対度数
相対度数分布を使ってヒストグラムを描きたい場合はdensity=Trueのオプションを使います.以前はnormedのオプションを使っていましたが,現在では非推奨になっています.
なお統計学の教科書に記載の通りに,グラフの面積の合計が「1」になるように標準化をしていますので,横幅次第では縦軸は1以上になります.下の例では階級幅は0.2ですので,縦軸の合計は5になっています.
den = plt.hist([np.random.rand(100), np.random.rand(10000)], density=True, bins=5) plt.show()
([array([ 0.90004308, 1.4500694 , 0.80003829, 1.10005265, 0.7500359 ]), array([ 1.02204892, 0.98254703, 0.97954688, 1.02304896, 0.99304753])], array([ 4.69813524e-05, 2.00037409e-01, 4.00027837e-01, 6.00018265e-01, 8.00008692e-01, 9.99999120e-01])
累積度数
累積度数のヒストグラムを描きたい場合にはcumulative=Trueのオプションを使います.
plt.hist(np.random.rand(100), cumulative=True) plt.show()
横向きのヒストグラム
ヒストグラムの向きを水平にしたい場合はorientation=’horizontal’のオプションを使います.棒グラフの場合のbarhとは異なりhisthのような別のメソッドは用意されていませんので注意してください.
plt.hist(np.random.rand(20), orientation='horizontal', bins=5) plt.show()
円グラフ(パイチャート)
円グラフはデータを円弧に並べて表示する形式のグラフで,特にデータの割合を強調したい場合に使いやすい形式の表現方法です.matplotlibでは様々なオプションを用いて円グラフのカスタマイズが可能になっています.
オプション | 説明 |
---|---|
x | データ(必須) |
labels | データのラベル |
radius | 円グラフの半径.デフォルトは1 |
explode | データを円グラフから外にはみ出させる |
counterclock | データが表示される方向.デフォルトはFalse |
startangle | 最初のデータが表示される角度.デフォルトは0度(3時の方向) |
autopct | データの%割合の表示 |
pctdistance | %割合の表示位置 |
wedgeprops | 円弧の設定 |
textprops | %割合表示のテキスト設定 |
この表だけではよくわからないと思いますので,これから具体例を通して見ていきましょう.
楕円と円グラフ
表示するデータはABCDEとラベルのついた数字を使います.pyplot.pieで特に設定をせずに描画を行うと,楕円形のグラフが表示されることが多いです.またlabelsオプションを使うと弧の外側にラベルが表示されます.
data = [10, 15, 45, 30, 4] label = ['A', 'B', 'C', 'D', 'E'] plt.pie(data, labels=label) plt.show()
楕円を円グラフへと変更するにはx,yの両軸の比率を同じにする必要があります.そのためにはaxis(‘equal’)を使います.
plt.pie(data, counterclock=False, startangle=90) plt.axis('equal') plt.show()
データの表示形式
pyplot.pieに与えたデータは標準設定では3時の方向から反時計回りに表示されます.これを12時の方向から時計回りに変更してみましょう.
またデータの割合を%で表示してみます.pyplot.pieの内部では元データではなく個々のデータの割合に変換されています.下の例では変換された割合データをlambda関数を使うことで,5%以上のデータについて小数点1桁まで表示しています.
plt.pie(data, counterclock=False, startangle=90, autopct=lambda f: '{:.1f}%'.format(f) if f > 5 else '') plt.legend(label) plt.axis('equal') plt.show()
下の例ではデータの一部を円弧からはみ出させることによって強調しています.この設定はexplodeオプションにリストを与えることで指定できます.また弧の部分の設定にwedgepropsを,%割合の文字の設定にtextpropsを用いています.何れも辞書形式でプロパティを設定します.
explode = [0, 0.2, 0, 0, 0] plt.pie(data, explode=explode, autopct=lambda f: '{:.1f}%'.format(f) if f>5 else '', counterclock=False, startangle=90, wedgeprops={'linewidth': 2, 'edgecolor': 'white'}, textprops={'color': 'white', 'weight': 'bold'}) plt.axis('equal') plt.show()
次の例ではautopctで表示するテキストをより詳細に指定します.
先ほど述べたようにpyplot.pieにデータを渡すと,データそのものではなく各データの割合のみを保有してます.そこで関数pct_absを定義することで%割合を用いてもとの値を復元しています.返値をフォーマット化したstringオブジェクトにすることで円グラフ上で表示が可能になります.
def pct_abs(pct, raw_data): absolute = int(np.sum(raw_data)*(pct/100.)) return '{:d}\n({:.0f}%)'.format(absolute, pct) if pct > 5 else '' plt.pie(data, counterclock=False, startangle=90, autopct=lambda p: pct_abs(p, data), wedgeprops={'linewidth': 2, 'edgecolor': 'white'}, textprops={'color': 'white', 'weight': 'bold'}) plt.axis('equal') plt.legend(label) plt.show()
ドーナツ型円グラフ
円グラフの上から,白い円を重ね描きすることでドーナツ型の円グラフが作成可能です.
下の例では半径0.5の白い円を後から描いています.このままでは%割合が白い部分と重なってしまいますので,表示位置を中心から0.75の位置pctdistance=0.75にずらしています.
plt.pie(data, counterclock=False, startangle=90, autopct=lambda p: pct_abs(p, data), pctdistance=0.75, textprops={'color': 'white', 'weight':'bold'}) plt.pie([100], colors='white', radius=0.5) plt.axis('equal') plt.show()
箱ひげ図
箱ひげ図は,
- 最小値
- 第1四分位点
- 中央値
- 第3四分位点
- 最大値
を表すグラフで,第1四分位点から第3四分位点までの高さに箱を描き,中央値に仕切りを挿入します.データのばらつき具合を表現するのに有用なタイプのグラフになります.
下の例では一様分布と標準正規分布からそれぞれ50のサンプリングを行ったものを箱ひげ図で可視化しています.
plt.boxplot([np.random.rand(50), np.random.randn(50)], labels=['uniform', 'normal']) plt.show()
バイオリンプロット
バイオリンプロットは箱ひげ図に似ていますが,箱の両端を確率密度で表現している点が異なります.バイオリンプロットは箱ひげ図よりも多くの情報量を含んでいる優れた可視化方法ですが,知名度が低いためにバイオリンプロットを知らない人には読み方がわからないのが最大の欠点と言えます.
下の例では一様分布と標準正規分布からそれぞれ50のサンプリングを行ったものをバイオリンプロットで可視化しています.
plt.violinplot([np.random.rand(50), np.random.randn(50)], showmedians=True) plt.show()
終わりに
今回はmatplotlibのスタイルシートについて簡単に説明した後に,matplotlibで作成可能な様々な種類のグラフについて解説してきました.
よく使う機能は説明したつもりですが,機能の全てを網羅しているわけではありません.もし「こういった機能はないのかな?」と感じたら,是非ともmatplotlibの公式ドキュメントを眺めてみてください.ほとんどの場合,望みの機能は実装されていると思います.
次の記事では,グラフの軸・表示範囲・タイトル・マーカー・凡例といったプロットの細部をどのように設定していくかを見ていきたいと思います.こういった要素は実際にプレゼンをする際に細かく調節したくなる部分になります.これらの項目についての理解を深めることで,思い通りのグラフ作成にぐっと近づくと思います.頑張っていきましょう.
>>次の記事:「matplotlibで作成したプロットをきれいにカスタマイズ」
コメント
はじめまして、合成化学の研究室に所属している大学4年生です。
研究生活の中で、pythonを使って何かできないかと考え、
最近pythonを使った統計分析に興味をもちました。
matplotlibを使うのはほとんど初めてなのですが、質問させてください。
必要なライブラリの準備の中で、
import numpy as np
import matplotlib as mpl
import matplotlib.pyplt as plt
print(mpl.__version__)
の3行目は、pypltではなく、pyplotではないでしょうか。
ビーム様
ご指摘の通りでしたので,修正させて頂きました.