luggage baggage

Machine learning, data analysis, web technologies and things around me.

Python データ可視化ライブラリ altair で高解像度 png 画像を保存するには

Python のデータ可視化ライブラリは複数あり、matplotlib や seaborn を使う場合が多いのではないかと思います。特に seaborn は出版品質の図が手軽に作れるため便利で、実際に機械学習系の論文ではこのライブラリで用意したであろうグラフをよく見かけます。

Altair(アルタイルと読む)は、R でいうところの ggplot2 に近いノリの可視化ライブラリで、プロットに関する制御性が高いことが特徴です。例えば、複数の機械学習モデルに対し精度が得られているとして、精度順にソートされたモデル名を x 軸のラベルとしたプロットをするには、altair では実質1行追加するだけで対応できます。また最近は可視化ライブラリのスタンダードとしても認知されてきており、スタンフォード大学のデータ可視化の授業 CS 448B Visualization (Winter 2020) では altair が使用されています。


先日 altair を使っていたところ、 png 画像としてグラフを保存すると解像度が低くプレゼンテーション資料に掲載するには向かない、ということを経験しました。この記事ではその対策を書いておこうかと思います。

Altair でのグラフ描画と保存

Altair でのデータ可視化は、Chart に pandas のデータフレームを読み込ませることで実施できます。

import altair as alt
from vega_datasets import data

wheat = data.wheat()
chart = alt.Chart(wheat).mark_point().encode(
    y="wheat",
    x="year:O"
)
chart

この出力を png 画像として保存した結果が次の図です。

f:id:yoshidabenjiro:20200815181103p:plain

きれいに描画されているように見えますが、拡大するとにじみが目立つようになります。これは altair がデフォルトで画面の解像度を読んでいるためだそう*1で、特に Jupyter notebook / lab を使う限り変更する手段がなさそうでした。4K ディスプレイを使ったりすると良いのかもしれません。プログラム的な対策としては次のようにします。

import altair_saver as saver
import chromedriver_binary

saver.save(chart, "chart.png", scale_factor=2, method="selenium", webdriver="chrome")

Google Chrome が使える環境かつ事前に selenium と chromedriver_binary を pip install しておく必要がありますが、altair_saver を使って上記のように指定すると、解像度を scale_factor 倍にした状態で画像保存できます。Firefox でも対応するドライバを使えば同じことができます。

f:id:yoshidabenjiro:20200815184455p:plain

はてなブログの仕様上違いが見えなくなっていますが、実際にコードを実行して上のものと比べると描画がはっきりしていることがわかります。なお、オプションの指定が正しくないと

ValueError: No enabled saver found that supports format='png'

みたいなエラーが出てしまいます。

Altair / notebook をサーバ上で稼働する場合にどうするか

ところで、Jupyter を計算サーバで動かす場合、Chrome バイナリがインストールされておらず、かつ自分には権限がなくて好きにインストールできないことがあります。実際、Chrome バイナリが見つからない場合、

selenium.common.exceptions.WebDriverException: Message: unknown error: cannot find Chrome binary

のようなエラーが出ます。サーバ環境で Chromium をビルドする方法もありますが、それよりは次のようにした方が楽かもしれません。

まず、サーバ環境で png 画像を保存することは諦め、描画結果を json で保存しておきます:

# saver.save(chart, "chart.png", scale_factor=2, method="selenium", webdriver="chrome")
saver.save(chart, "chart.json")

次に、手元の Chrome バイナリが使用可能な環境でこの json を読み出し、chart を復元した上で好きな scale_factor を指定して保存します:

import json
with open("chart.json") as f:
    spec = json.load(f)
chart = alt.Chart.from_dict(spec)
saver.save(chart, "chart.png", scale_factor=2, method="selenium", webdriver="chrome")

こうすると元の描画データを完全に再現した状態で高解像度画像が得られます。

Altair は javascript 用のデータ可視化ライブラリ vega / vega-lite に基づくものなので、vega 形式での json 出力ができるのですね。ベガとアルタイルということで、しゃれてます。関連ライブラリでデネブとかも出てくるのかな。

動作確認環境
  • Google Chrome: Version 84.0.4147.125 (Official Build) (64-bit)
  • chromedriver_binary: ChromeDriver 84.0.4147.30 (48b3e868b4cc0aa7e8149519690b6f6949e110a8-refs/branch-heads/4147@{#310})
  • altair: 4.1.0
  • altair_saver: 0.5.0
  • selenium: 3.141.0