フリーランチ食べたい

No Free Lunch in ML and Life. Pythonや機械学習のことを書きます。

PythonでGitHub Starのトレンドを取得してBokehでプロットしはてなブログに埋め込む

最初に結果です。下のようにGitHub StarのトレンドをBokehで可視化してはてなブログに埋め込む方法を解説します。マウスオーバーしてもらうとtooltipが表示される(=画像埋め込みでない)ことが確認していただけると思います。

既存のサイトでもリポジトリ名を入れると可視化してくれるサイトがいくつかあるのですが、細かい見た目を調整したかったため自分でスクリプトを書いてみました。コード全体はGitHubに上がっています。

github.com

GitHub Star取得

まずGitHub Starを取得します。GitHub APIのList Stargazersを使います。

developer.github.com

注意点として日時を取得するためにヘッダーにAccept: application/vnd.github.v3.star+jsonをつけてGitHub API v3を使う必要があります。下がコード例です。

user = "dask"
repo = "dask"
url = f"https://api.github.com/repos/{user}/{repo}/stargazers"

token = os.environ["GITHUB_TOKEN"]
headers = {
    "Accept": "application/vnd.github.v3.star+json",
    "Authorization": f"token {token}"
}

resps = []

while True:
    resp = requests.get(url, headers=headers)
    resps.extend(resp.json())
    if "next" in resp.links:
        url = resp.links['next']['url']
    else:
        break

データ加工

取得した情報をpandasを使って加工していきます。時系列のグルーピングにはdtアクセサを使っています。もし詳しく知りたい方はsinhrksさんのブログを参照してください。

sinhrks.hatenablog.com

df = pd.DataFrame(resps)
df['dt'] = pd.to_datetime(df['starred_at'])
df_counts = df.groupby(df['dt'].dt.date).size().to_frame("count").reset_index()
df_counts['cumsum'] = df_counts['count'].cumsum()

Bokehでの可視化

Bokehで可視化します。ポイントが2種類あります。

  • figuresizing_mode='scale_width'を指定して幅をブログに動的に合わせる
  • bokeh.embed.componentsでHTML全体でなく該当箇所のdivを書き出す

詳しくは以下のコードを参考にしてください。

source = ColumnDataSource(data=df_counts)
colors = Paired[3]

p = figure(
    plot_height=300, toolbar_location=None, tooltips=None,
    y_range=(df_counts['cumsum'].min(), df_counts['cumsum'].max()*1.05),
    x_axis_type="datetime", background_fill_color=colors[0], background_fill_alpha=.1,
    border_fill_color=colors[0], border_fill_alpha=.1,
    sizing_mode='scale_width',
    title=f"{user}/{repo}のGitHubスター数の遷移"
)

# 線グラフ
p.line(
    x='dt', y='cumsum', color=colors[1], line_width=5,
    legend="累計",
    source=source
)

# 増加数
p.extra_y_ranges = {"second": Range1d(start=df_counts['count'].min(), end=s.quantile(0.99))}
p.add_layout(LinearAxis(y_range_name="second"), 'right')
p.vbar(
    x='dt', top='count', width=1,
    y_range_name="second",
    color=colors[2],
    alpha=.6,
    legend="1日ごと",
    source=source
)

# グラフの見た目を調整
p.axis.minor_tick_line_color = None
p.grid.visible = None
p.title.align = "center"


hover = HoverTool(
    tooltips=[("日付", "@dt{%F}"), ("累計", "@cumsum"), ("1日ごと", "@count")],
    formatters={"dt": 'datetime'},
    mode='vline'
)
p.add_tools(hover)
p.legend.location = "top_left"

# 埋め込み用のhtml, jsを取得
script, plot_div = components(p)

# ファイルに書き出す
with open(f"{user}_{repo}.html", mode="w") as f:
    f.write(plot_div + script)

はてなブログ埋め込み

書き出せたら、はてなブログに埋め込みます。はてなブログはJS埋め込みに対応しているので、そのまま埋め込むことができます

先程書き出したHTML+JSに追加でBokehのクライアントJSライブラリをCDNから読み込む必要があるので、下のコードを追加してください。

<script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-1.3.4.min.js"></script>

詳しくはBokehのドキュメントを参照してください。(ただし、CDNのリンクが間違っているのは注意)

bokeh.pydata.org

これで上記のようなグラフを貼り付けることができました。

さいごに

最近Bokehの便利さと描画のキレイさに感銘を受けてmatplotlibから移行しつつあります。今後もBokeh関連のエントリを書いていけたら、と思っています。

f:id:ikedaosushi:20190909043158p:plain ※JSが動かなかった場合に備えて念の為画像も貼っておきます。