Python Matplotlib を使って 2 軸のグラフ(株価のラインと出来高のバー)を作ってみました

2 軸のグラフを作りたかったので、試しに株価のラインと出来高のバーのグラフを作りました。

環境

  • python 3.6.5
  • matplotlib 3.0.2

Matplotlib について

2 軸のグラフは次のリンクが参考になりそうでした。

その前に大切なことを知りました。

matplotlibにはグラフを作る際の二つの流儀がある

Artistの話の前に、新しいユーザーが絶対に知っておくべきplt.plotとax.plotの違いについて述べます。公式チュートリアルでも A note on the Object-Oriented API vs Pyplot や Coding Styles で言及されていますが、matplotlibでグラフを作るには二つの流儀(インターフェース)があります。公式ドキュメントを含めてネット上に大量にあるmatplotlibのコードにはこの二つが混在していますが、これらの違いについて明記してる例はあまり多くないように思えます。また、意味もなく二つを混ぜて使っている例も多く、これが多くの初心者のつまづきの原因になっていると思います。

早く知っておきたかったmatplotlibの基礎知識、あるいは見た目の調整が捗るArtistの話 - Qiita

違いを理解した上で Pyplot を使うのは良いとして、 the Object-Oriented API を使った方が柔軟にグラフを作っていけそうなので、こちらの方法を使って進めることにしました。

2 軸のグラフ

ここから Jupyter Notebook のセルに入力しながらグラフを作っていきたいと思います。 この中で使っているヒストリカルデータは Yahoo Finance の S&P500 のものです。

最初に、モジュールを読み込みました。

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

次に、ヒストリカルデータを読み込みました。

df = pd.read_csv('~/Documents/1/historical-prices/^GSPC.csv', header=0, index_col=0, parse_dates=[0])
df.head()
df.tail()

1950 年からのヒストリカルデータだったので 2017 年だけ使うことにしました。

df2017 = df[df.index.year == 2017]

まず、株価のラインです。

fig, ax1 = plt.subplots()
ax1.plot(df2017.index, df2017['Close'])

結果です。

次に、出来高のバーです。

fig, ax1 = plt.subplots()
ax1.bar(df2017.index, df2017['Volume'])

結果です。

最後に、株価のラインと出来高のバーを一緒に表示しました。

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
c = ax1.bar(df2017.index, df2017['Volume'])
l = ax2.plot(df2017.index, df2017['Close'])
le = ax1.legend((c, *l), ('Volume', 'Close'), loc='upper left')

Axes.twinx() 関数は x 軸を共有した Axes を作ってくれるようです(こうすると y 軸が 2 つになります)。 legend は ax1 の方にまとめて表示するようにしました。

結果です。

style sheets

見た目の設定をしていて、次のリンクを見つけました。

style sheets と rcParams でカスタマイズできるみたいです。

最初に style sheets を試してみました。

モジュールを読み込んで、ヒストリカルデータを読み込んで、 2017 年のデータだけ用意しました。

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.read_csv('~/Documents/1/historical-prices/^GSPC.csv', header=0, index_col=0, parse_dates=[0])
df.head()
df.tail()
df2017 = df[df.index.year == 2017]

利用可能な style を表示してみました。

print(plt.style.available)

結果です。

['Solarize_Light2', '_classic_test', 'bmh', 'classic', 'dark_background', 'fast', 'fivethirtyeight', 'ggplot', 'grayscale', 'seaborn-bright', 'seaborn-colorblind', 'seaborn-dark-palette', 'seaborn-dark', 'seaborn-darkgrid', 'seaborn-deep', 'seaborn-muted', 'seaborn-notebook', 'seaborn-paper', 'seaborn-pastel', 'seaborn-poster', 'seaborn-talk', 'seaborn-ticks', 'seaborn-white', 'seaborn-whitegrid', 'seaborn', 'tableau-colorblind10']

次のようにするとスタイルを使うことができるようです。

plt.style.use('ggplot')

グラフを作ってみました。

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
c = ax1.bar(df2017.index, df2017['Volume'])
l = ax2.plot(df2017.index, df2017['Close'])
le = ax1.legend((c, *l), ('Volume', 'Close'), loc='upper left')

結果です。

style sheets の方は予め決められたテンプレートのようなものがいくつかあって、そこから選択することでお手軽に見た目を変えられるようでした。 このテンプレートのようなものは自分で用意することもできるようでした。

rcParams

今度は rcParams を試してみました。

最初に、モジュールを読み込みました。

%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

mpl を読み込みました。 plt.rcParams でもできるみたいですけれども、チュートリアルは mpl.rcParams だったのでそれに従いました。

次に、ヒストリカルデータを読み込んで、 2017 年のデータだけ用意しました。

df = pd.read_csv('~/Documents/1/historical-prices/^GSPC.csv', header=0, index_col=0, parse_dates=[0])
df.head()
df.tail()
df2017 = df[df.index.year == 2017]

設定可能な rcParams を表示してみました。

mpl.rcParams

結果です。

RcParams({'_internal.classic_mode': False,
          'agg.path.chunksize': 0,
          'animation.avconv_args': [],
          # …略…
          'ytick.minor.visible': False,
          'ytick.minor.width': 0.6,
          'ytick.right': False})

たくさんありましたので省略しました。

次のような設定にしてみました。

mpl.rcParams['axes.spines.bottom'] = False
mpl.rcParams['axes.spines.left'] = False
mpl.rcParams['axes.spines.right'] = False
mpl.rcParams['axes.spines.top'] = False
mpl.rcParams['legend.frameon'] = False
mpl.rcParams['text.color'] = '#666666'
mpl.rcParams['xtick.color'] = '#666666'
mpl.rcParams['ytick.color'] = '#666666'

グラフの枠を表示しないようにしました。 legend の枠を表示しないようにしました。 それからテキストの色を薄くしました。

グラフを作ってみました。

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
c = ax1.bar(df2017.index, df2017['Volume'])
l = ax2.plot(df2017.index, df2017['Close'])
le = ax1.legend((c, *l), ('Volume', 'Close'), loc='upper left')

結果です。

バーとラインの色も指定しました。

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
c = ax1.bar(df2017.index, df2017['Volume'], alpha=0.8, color='#ff6384')
l = ax2.plot(df2017.index, df2017['Close'], alpha=0.8, color='#36a2eb')
le = ax1.legend((c, *l), ('Volume', 'Close'), loc='upper left')

結果です。

この style sheets や rcParams を知る前は color なんかを毎回設定していたのですが、これは一度設定すると次も同じようなグラフが作れるので良さそうでした。

figsize, transparent

グラフのサイズを大きくしたいことがありました。

**fig_kw : All additional keyword arguments are passed to the pyplot.figure call.

matplotlib.pyplot.subplots — Matplotlib 3.0.2 documentation


figsize : tuple of integers, optional, default: None width, height in inches. If not provided, defaults to rcParams[“figure.figsize”] = [6.4, 4.8].

matplotlib.pyplot.figure — Matplotlib 3.0.2 documentation

plt.subplots() 関数の figsize を指定することで変えられるようでした。 これは rcParams でも指定できるようでした。

それからグラフを透明にしたいことがありました。

transparent : bool

If True, the axes patches will all be transparent; the figure patch will also be transparent unless facecolor and/or edgecolor are specified via kwargs. This is useful, for example, for displaying a plot on top of a colored background on a web page. The transparency of these patches will be restored to their original values upon exit of this function.

matplotlib.figure.Figure — Matplotlib 3.0.2 documentation

Figure.savefig() 関数の説明です。 ファイルを保存するときに transparent に True を渡してあげればできるようでした。

グラフを作ってみました。

fig, ax1 = plt.subplots(figsize=(16, 9), dpi=80)
ax2 = ax1.twinx()
c = ax1.bar(df2017.index, df2017['Volume'], alpha=0.8, color='#ff6384')
l = ax2.plot(df2017.index, df2017['Close'], alpha=0.8, color='#36a2eb')
le = ax1.legend((c, *l), ('Volume', 'Close'), loc='upper left')
fig.savefig('png.png', dpi=80, transparent=True)

結果です。

終わり

Matplotlib について少し理解しました。

作った Jupyter Notebook のファイルを Gist にアップロードしておきました。