🐕

Altairメモ

2020/09/17に公開

Altair

新興の可視化ライブラリ。

簡単に書きやすいらしい。

Vega を使うから Altair (アルタイル) らしい。

インストール

https://altair-viz.github.io/getting_started/installation.html

JupyterLab、Jupyter Notebook、Google Colabそれぞれでインストール方法が異なる。

Jupyter Notebook

$ pip install -U altair vega_datasets notebook vega

動作確認

import altair as alt
from vega_datasets import data

# for the notebook only (not for JupyterLab) run this command once per session
alt.renderers.enable('notebook')

iris = data.iris()

alt.Chart(iris).mark_point().encode(
    x='petalLength',
    y='petalWidth',
    color='species'
)

左の図は、上のコードの出力結果。

なんと画像セーブメニューまで標準でついている。

チュートリアル

Altairのデータは pandas DataFrame を使う。

import pandas as pd
data = pd.DataFrame({'a': list('CCCDDDEEE'),
                     'b': [2, 7, 4, 1, 2, 6, 8, 4, 7]})

最も基本的なオブジェクトは Chart である。

import altair as alt
chart = alt.Chart(data)

この Chartに操作指示を出していくことになる。

エンコーディングとマーキング

データを可視化するには mark 属性を使う。この属性にアクセスするには Chart.mark_* を使う。

alt.Chart(data).mark_point()

これは、1行ごとに1つの点をプロットしている。しかしポジションを指示していない。

点を分割描画するには encoding channel あるいは単に channel を使う。この例では、変数 ax チャネルにエンコードしている。 x チャネルはx軸を表す。

alt.Chart(data).mark_point().encode(
    x='a',
)

チャネルには x y color shape size などがある。

Altairはチャネルに渡すデータの型を自動で見てくれる。以下の例では、 bが数値型だと自動で判別している。

alt.Chart(data).mark_point().encode(
    x='a',
    y='b'
)

データ変換: 集約

Altairはビルトインの集約文法を持っている。

alt.Chart(data).mark_point().encode(
    x='a',
    y='average(b)'
)

集約された値に対しては普通散布図ではなく棒グラフを使う。ここで、 mark_point()mark_bar() に変えてみる。

alt.Chart(data).mark_bar().encode(
    x='a',
    y='average(b)'
)

縦軸でなくて、横軸にしてみる。 xy を逆にしてみる。

alt.Chart(data).mark_bar().encode(
    x='average(b)',
    y='a'
)

to_json() を使ったVega-Liteへのエクスポート

Vega-Lite使わないので省略

チャート編集の基本操作いくつか

バーの色を変える

Chart.mark_* のcolorオプションを変更する。

alt.Chart(data).mark_bar(color="firebrick").encode(
    x='average(b)',
    y='a'
)

軸のタイトルをつける

x チャネルではなく alt.Xを使う。

alt.Chart(data).mark_bar().encode(
    alt.Y('a', title='category'),
    alt.X('average(b)', title='avg(b) by category')
)

チャートを出力する

HTMLに出力する

chart = alt.Chart(data).mark_bar().encode(
    x='a',
    y='average(b)',
)
chart.save('chart.html')

動作環境

PyCharm + Jupyter notebook

Frequently Asked Questions - Altair 3.2.0 documentation

一応ここに方法っぽいものが書いてあるが、動かすとブラウザを起動してそこに結果を表示するというとんでもないシロモノだった。

おとなしく Jupyter notebook 起動した方がいい。

Chart

プロパティ

altair.Chart - Altair 3.2.0 documentation

チャートトップレベルのプロパティを指定できる。

# Base chart for data tables
ranked_text = alt.Chart(df).mark_text().encode(
    y=alt.Y('row_number:O',axis=None)
).transform_window(
    row_number='row_number()'
).transform_filter(
    brush
).transform_window(
    rank='rank(row_number)'
).transform_filter(
    alt.datum.rank<50
)

# Data Tables
threshold = ranked_text.encode(text='threshold:N').properties(title='Threshold')
precision = ranked_text.encode(text='precision:N').properties(title='Precision')
recall = ranked_text.encode(text='recall:N').properties(title='Recall')
text = alt.hconcat(threshold, precision, recall) # Combine data tables

Encoding

チャネルオプション

Encodings - Altair 3.2.0 documentation

color 色を指定する

色そのものを指定するのではなく、色を指定するために参照するデータを与える。

alt.Chart(cars).mark_point().encode(
    x='Acceleration:Q',
    y='Miles_per_Gallon:Q',
    color='Origin:N'
)

この例では、 Origin という Nnominal data を色分けの参照データとしている。

sort データをソートする

ソートのデフォルトは昇順(ascending)。

alt.Chart(iris).mark_text().encode(
    y=alt.Y('petalWidth', axis=None), text='petalWidth'
)

sort="descending" を設定することで降順にソートできる。

alt.Chart(iris).mark_text().encode(
    y=alt.Y('petalWidth', axis=None, sort="descending"), text='petalWidth'
)

インタラクティブなチャートの作成

Selection

Bindings, Selections, Conditions: Making Charts Interactive - Altair 3.2.0 documentation

Selection は、マウスを使ったインタラクティブな選択を行うときに使う。

Interval

brush = alt.selection(type='interval')

このIntervalだけでは単に画面が選択可能になるだけで何も意味はない。画面に様々な効果を付与するには、後述の Condition と組み合わせる必要がある。

chart に登録するときは add_selectionを使う。

alt.Chart(cars).mark_point().encode(
    x='Miles_per_Gallon:Q',
    y='Horsepower:Q',
    color=alt.condition(brush, 'Origin:N', alt.value('lightgray'))
).add_selection(
    brush
)

実際に操作して試してほしいが、ドラッグして範囲を選択すると、その範囲だけが選択され、その範囲だけの値が右側に表示される。

Brushing Scatter Plot to show data on a table - Altair 3.2.0 documentation

Condition

Bindings, Selections, Conditions: Making Charts Interactive - Altair 3.2.0 documentation

brush = alt.selection(type='interval')

alt.Chart(cars).mark_point().encode(
    x='Miles_per_Gallon:Q',
    y='Horsepower:Q',
    color=alt.condition(brush, 'Origin:N', alt.value('lightgray'))
).add_selection(
    brush
)

上記の例の場合、brush (selection) で選択されているエリアは True となり 'Origin:N' をベースにした色付けがなされ、選択されていないエリアは False となり alt.value('lightgray') 色が使われる。

マーク

グラフの種類を規定するコンポーネント

mark_text

テキストの表示を規定する。グラフ上のラベルづけや表の作成などに使う。

import altair as alt
from vega_datasets import data

source = data.wheat()

bars = alt.Chart(source).mark_bar().encode(
    x='wheat:Q',
    y="year:O"
)

text = bars.mark_text(
    align='left',
    baseline='middle',
    dx=3  # Nudges text to right so it doesn't appear on top of the bar
).encode(
    text='wheat:Q'
)

(bars + text).properties(height=900)

Bar Chart with Labels - Altair 3.2.0 documentation

データ変換(transform)

Data Transformations - Altair 3.2.0 documentation

基本的には、AltairはAltairに来る前段階(つまりpandas DataFrame)でデータ変換処理を行うことを推奨している。しかし、データがJSON/CSVだった場合など、そうもいかない場合もある。また、同一のデータを複数の組み合わせのビューで閲覧したい場合もある。そこで、Altairはデータ変換のための関数群を用意している。

Window Transform

Data Transformations - Altair 3.2.0 documentation

transform_window() を使うことで、 ranklag といったウィンドウ関数を適用できる。

import altair as alt
from vega_datasets import data

alt.Chart(data.movies.url).transform_window(
    sort=[{'field': 'IMDB_Rating'}],
    frame=[None, 0],
    cumulative_count='count(*)',
).mark_area().encode(
    x='IMDB_Rating:Q',
    y='cumulative_count:Q',
)

横軸全体 frame=[None, 0] に対して、累積カウント cumulative_count='count(*)' をしている。

Data Transformations - Altair 3.2.0 documentation

Filter Transform

Data Transformations - Altair 3.2.0 documentation

フィルタ機能。不要なデータを除去する。

import altair as alt
from altair import datum

from vega_datasets import data
pop = data.population.url

alt.Chart(pop).mark_area().encode(
    x='age:O',
    y='people:Q',
).transform_filter(
    (datum.year == 2000) & (datum.sex == 1)
)


Data Transformations - Altair 3.2.0 documentation

ここでフィルタに Selection を設定すると、選択した項目だけにフィルタされる。

brush = alt.selection(type='interval')

points = chart.mark_point().encode(
    alt.Y('precision', title='Precision'),
    alt.X('recall', title='Recall'),
    color=alt.condition(brush, 'threshold', alt.value('grey'))

).add_selection(brush)

# Base chart for data tables
ranked_text = alt.Chart(df).mark_text().encode(
    y=alt.Y('row_number:O',axis=None)
).transform_window(
    row_number='row_number()'
).transform_filter(
    brush
)

トップNでフィルタする

transform_filter(
    (alt.datum.rank < 10)
)

複合チャート(Compound Chart)

水平結合(hconcat)

Compound Charts: Layer, HConcat, VConcat, Repeat, Facet - Altair 3.2.0 documentation

import altair as alt
from vega_datasets import data

iris = data.iris.url

chart1 = alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
).properties(
    height=300,
    width=300
)

chart2 = alt.Chart(iris).mark_bar().encode(
    x='count()',
    y=alt.Y('petalWidth:Q', bin=alt.Bin(maxbins=30)),
    color='species:N'
).properties(
    height=300,
    width=100
)

chart1 | chart2

|hconcat とも書ける。

alt.hconcat(chart1, chart2)

Tips

箱ひげ図(boxplot)における外れ値(outliers)の色は、カテゴリーごとに自動で色分けしてくれない

以下のような書き方をすると、箱は色分けしてくれるが外れ値は色分けしてくれない(デフォルトの色のまま)

alt.Chart(df).mark_boxplot().encode(
		x="name:N", 
		y="value:Q", 
		color="category:N"
		)

色分けするには別々にChartを作るしかない。

chart_cat1 = alt.Chart(df[df["category"] == "cat1"]).mark_boxplot(color="blue").encode(
				x="name:N", 
				y="value:Q"
				) 
chart_cat2 = alt.Chart(df[df["category"] == "cat2"]).mark_boxplot(color="orange").encode(
				x="name:N", 
				y="value:Q"
				) 

(chart_cat1 + chart_cat2)

参考

Apply color to outliers in boxplot · Issue #1591 · altair-viz/altair

Discussion