この投稿は 「Jupyter Advent Calendar 2017 - Qiita」 の 22日目の記事です。
こんにちは、パンダ好きの akiyoko です。
上野動物園の赤ちゃんパンダ、シャンシャン可愛いですよね〜。
Ueno Panda Live.jp というサイトで、開園日なら毎日(ただし開園時間のみ)パンダのライブ映像が見れる(オススメは3カメ!)のは皆さんもうご存知ですよね。しかしながら、たまに小一時間ほどパンタ達がじーーーっと動かないことがあるんですね。ただ寝てるだけという。こっちはあのモフモフが愛くるしく動く姿が見たいのに!! 見たいだけなのにぃ!!!!
(親子水入らずで全く動かない様子。キャプチャ元:Ueno Panda Live.jp)
ハァハァ。少々取り乱してしまいましたが、ところで、あなたのパンダ(pandas)も止まってしまっていませんか? というのが今回のテーマです。 *1
最近、この本を少しずつ読んでいます。
Python 界隈で大注目のデータ分析ツール「Jupyter Notebook」を中心に、1次元・2次元データを便利に扱うための「pandas」、2次元データからグラフを描画するための「Matplotlib」、インタラクティブなグラフを描画するための「Bokeh」などについて 400ページを超える大容量ボリュームで書かれており、動的・対話的なグラフを Jupyter Notebook 上に描くことが一つのゴールとなっています。
また、fin-py にも何度か参加させていただいているのですが、LT でたまに見かける、Jupyter Notebook 上でグリングリン動かせるインタラクティブなチャートに憧れを抱いていたのは私ではないはず。
いろいろ調べたり聞いたりしたところ、動的にチャートを動かす部分は Bokeh、ウィジェットの部分は ipywidgets がよさげということが分かりました。 *2
上記 Jupyter本の共著者で fin-py 主催の driller さんも昨年の「jupyter notebook Advent Calendar 2016 - Qiita」で「ipywidgetsとBokeh使ってインタラクティブな可視化をする」というズバリそのままの記事をアップしていたりします。
一周遅れになった感はありますが、今回は「Bokeh と ipywidgets でインタラクティブ Jupyter のススメ」と称して、Bokeh と ipywidgets を使って Jupyter Notebook 上の pandas のチャートをグリングリン動かしてみたい と思います。
チャートに使うデータは、CoinMarketCap から取得した今年1月からの仮想通貨の価格、および米 Yahoo Finance から取得したドル円為替を使います。
Bokeh, ipywidgets のインストール
現在のローカル環境は以下の通りです。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.12.6 BuildVersion: 16G1114 $ python -V Python 3.5.2 :: Anaconda custom (64-bit)
Bokeh
Anaconda をインストールしていれば、Bokeh は標準で入っていると思います。
(参考)Anaconda package lists — Anaconda 2.0 documentation
ipywidgets
一方の ipywidgets のインストール方法は、「Jupyter 本」の付録「A-1 ipywidgets の使い方」に詳しく書かれているのですが、私の環境ではそのままではうまく動きませんでした。
$ conda install -conda-forge ipywidgets
として、
from ipywidgets import interact @interact(n=5) def f(n): return n
を実行してもウィジェットが起動せず。実行するたびに、Jupyter Notebook のログに
[IPKernelApp] WARNING | Widget Javascript not detected. It may not be installed or enabled properly.
というワーニングが出ている状態。
結局、ipywidgets が利用している widgetsnbextension のバージョンが「3.0.0」だとダメっぽいと判断。一旦、2系に戻しました。
$ conda install 'widgetsnbextension<3'
とし、
$ jupyter notebook
を止めて、再度立ち上げ直せば動くようになりました。
$ pip list | grep bokeh bokeh (0.12.2) $ pip list | grep widgets ipywidgets (6.0.0) widgetsnbextension (2.0.0)
データ収集
仮想通貨の日足チャート
Scrapy という Python 製のクローリング・スクレイピングツールを使って CoinMarketCap から 1月からの日足チャートを取得し、ローカルに CSVファイルとして保存しました。
《過去記事》
akiyoko.hatenablog.jp
なお、Scrapy を使った今回のスクレイピング実行コードは
github.com
に置いてあります。
実行環境は Python 3.5 です。
取得したデータはこのような形式で保存されています。
BTC.csv
symbol,date,open,high,low,close,volume,market_cap BTC,"Dec 20, 2017",17760.30,17934.70,16077.70,16624.60,"22,149,700,000","297,526,000,000" BTC,"Dec 19, 2017",19118.30,19177.80,17275.40,17776.70,"16,894,500,000","320,242,000,000" BTC,"Dec 18, 2017",19106.40,19371.00,18355.90,19114.20,"14,839,500,000","320,000,000,000" ... BTC,"Jan 03, 2017",1021.60,1044.08,1021.60,1043.84,"185,168,000","16,426,600,000" BTC,"Jan 02, 2017",998.62,1031.39,996.70,1021.75,"222,185,000","16,055,100,000" BTC,"Jan 01, 2017",963.66,1003.08,958.70,998.33,"147,775,000","15,491,200,000"
ETH.csv
symbol,date,open,high,low,close,volume,market_cap ETH,"Dec 20, 2017",827.52,845.06,756.00,819.09,"3,969,940,000","79,810,600,000" ETH,"Dec 19, 2017",793.90,881.94,785.34,826.82,"4,096,550,000","76,552,200,000" ETH,"Dec 18, 2017",721.73,803.93,689.23,794.64,"3,249,230,000","69,578,400,000" ... ETH,"Jan 03, 2017",8.37,10.00,8.32,9.73,"33,625,200","732,988,000" ETH,"Jan 02, 2017",8.17,8.44,8.05,8.38,"14,579,600","714,900,000" ETH,"Jan 01, 2017",7.98,8.47,7.98,8.17,"14,731,700","698,149,000"
ローカルに保存した仮想通貨の日足チャートの CSVファイルを全て読み込みます。1ファイルごとに 1通貨のチャートが入っているので、とりあえず行方向に連結しておきます。
import glob import pandas as pd pd.options.display.max_rows = 10 df_all = pd.DataFrame() paths = glob.glob('/Users/akiyoko/PycharmProjects/ccspider/downloads/*.csv') for path in paths: df = pd.read_csv(path, index_col='date', parse_dates=True) df = df.sort_index() df_all = df_all.append(df) df_all
例えば、BTC のチャートを取り出すときは以下のようにすればよいでしょう。
df_all[df_all.symbol == 'BTC']
pandas.DataFrame の条件による切り出しについては、過去記事に詳しい説明があります。
《過去記事》
akiyoko.hatenablog.jp
ドル円為替の日足チャート
ドル円為替は、pandas-datareader というライブラリを使って、米 Yahoo Finance から日足チャートデータを取得しました。
import pandas_datareader.data as DataReader df_usdjpy = DataReader.get_data_yahoo('JPY=X', start='2017-01-01', end='2017-12-31') df_usdjpy
「pandasで為替データUSD/JPYの取得 - はしくれエンジニアもどきのメモ」を参考にさせていただきました。
それにしても、pandas-datareader スゴイ!
たった一行で為替チャートが取ってこれるのですね。他にも株価や世界人口、GDP のデータも簡単に取れるようです。
(参考)pandas-datareaderで株価や人口のデータを取得 | note.nkmk.me
実践
移動平均線
まず、n日移動平均のチャートをハンドルで動かせるようにしてみます。ここまではほぼ「Jupyter 本」のパクリです。
from bokeh.io import output_notebook, push_notebook, show from bokeh.plotting import figure from ipywidgets import interact output_notebook() plot = figure(plot_width=600, plot_height=300, x_axis_type='datetime') df = df_all[df_all.symbol == 'BTC'] close = plot.line(df.index, df['close']) ma = plot.line(df.index, df['close'].rolling(10).mean(), color='red') handle = show(plot, notebook_handle=True) @interact(n=(0, 30)) def redraw(n=10): ma.data_source.data = {'x': df.index, 'y': df['close'].rolling(n).mean()} push_notebook(handle=handle)
ハンドルを動かすと、赤色の移動平均線がグリグリ動きます。気持ちいいです。
ドロップダウンで通貨を入れ替え
次に、pandas.DataFrame の条件による切り出しを使って、ドロップダウンの条件によって通貨を入れ替えられるようにしてみました。
from bokeh.io import output_notebook, push_notebook, show from bokeh.plotting import figure from ipywidgets import interact output_notebook() plot = figure(plot_width=600, plot_height=300, x_axis_type='datetime') df = df_all[df_all.symbol == 'BTC'] close = plot.line(df.index, df['close']) ma = plot.line(df.index, df['close'].rolling(10).mean(), color='red') handle = show(plot, notebook_handle=True) @interact(symbol=['BTC', 'ETH', 'BCH', 'XRP', 'LTC', 'MIOTA', 'ADA', 'DASH', 'XEM', 'BTG'], n=(0, 30)) def redraw(symbol='BTC', n=10): df = df_all[df_all.symbol == symbol] close.data_source.data = {'x': df.index, 'y': df['close']} ma.data_source.data = {'x': df.index, 'y': df['close'].rolling(n).mean()} push_notebook(handle=handle)
Market Cap の上位10通貨を切り替えられるようにしてみましたが、なかなか面白いです。
USD ⇔ JPY の切り替え
最後に、USD と JPY の価格表示が切り替えられるようなチェックボックスを付けてみました。
df に「close_jpy」列を追加し、通貨の終値(USD)とドル円為替の終値(1USD あたりの JPY)を掛けた値を入れています。なお、為替は相場が休みの日はデータが取れない(N/Aになる)ため、「fillna(method='ffill')」を使って前日のデータで補完しています。
from bokeh.io import output_notebook, push_notebook, show from bokeh.plotting import figure from ipywidgets import interact output_notebook() plot = figure(plot_width=600, plot_height=300, x_axis_type='datetime') df = df_all[df_all.symbol == 'BTC'] close = plot.line(df.index, df['close']) ma = plot.line(df.index, df['close'].rolling(10).mean(), color='red') handle = show(plot, notebook_handle=True) @interact(symbol=['BTC', 'ETH', 'BCH', 'XRP', 'LTC', 'MIOTA', 'ADA', 'DASH', 'XEM', 'BTG'], n=(0, 30), jpy=False) def redraw(symbol='BTC', n=10, jpy=False): df = df_all[df_all.symbol == symbol] # interpolate() causes ValueError: Out of range float values are not JSON compliant... why? #df = df.assign(close_jpy=(df['close'] * df_usdjpy['Close']).interpolate()) df = df.assign(close_jpy=(df['close'] * df_usdjpy['Close']).fillna(method='ffill')) y = df['close_jpy'] if jpy else df['close'] close.data_source.data = {'x': df.index, 'y': y} ma.data_source.data = {'x': df.index, 'y': y.rolling(n).mean()} push_notebook(handle=handle)
全体的に 5月、12月に何があったの??という感じのチャートになっています。
まとめ
pandas.DataFrame は時系列データを保持したりチャートを描いたりするのに非常に便利に使えますが、Bokeh と ipywidgets でインタラクティブにチャートを描画できるようにすれば、見栄えもよくもっと強力なツールになります。
そんな Bokeh や ipywidgets についてもしっかり書かれている「Jupyter 本」はスゲエなってことを今回再認識しました。つまり、買えってことです(笑)。
ちなみに今回の Advent Calendar では当初、pandas.DataFrame の resample を使ってインタラクティブに5分足 ⇔ 1時間足 ⇔ 日足といったようにチャートを書き換えられるようにしたかったのですが、うまく動かすことができませんでした。figure の X軸を書き換えるのは難しいのかもしれませんね。。
明日は、u1and0 さんの「Jupyter Advent Calendar 2017 - Qiita」 23日目の記事です。よろしくお願いします。
おまけ
今回の件とは直接関係ありませんが、notebook 保存時に以下のエラーが出てファイルを保存できなくなって困ってしまいました。
結局、
python - plotly plots in jupyter notebooks: Validation fails when saving - Stack Overflow
に書かれているように、
$ conda update nbformat
を更新してから、
$ jupyter notebook
を止めて再度立ち上げ直せば、保存できるようになりました。
原因は不明ですが、急に動かなくなると焦りますよね。
*1:なお、pandas の名前の由来は「panel data」らしいです。 pandasで使われるデータ構造 ~1次元、2次元、3次元のデータの扱い方~ - 今日も窓辺でプログラム