2. データ型、グラフィカルマーク、およびビジュアルエンコーディングチャネル#

可視化は、データを一連の_グラフィカルマーク_(棒、線、点など)を使用して表現します。マークの位置、形状、サイズ、色などの属性は、基礎となるデータ値をエンコードするための_チャネル_として機能します。

データ型、マーク、エンコーディングチャネルという基本的なフレームワークを使用することで、幅広い種類の可視化を簡潔に作成できます。このノートブックでは、これらの各要素を詳しく探り、それらを使用してカスタム統計グラフィックを作成する方法を示します。

このノートブックは、データ可視化カリキュラム の一部です。

import pandas as pd
import altair as alt

2.1. Global Development Data#

1955年から2005年までの期間にわたる、複数の国における世界の健康と人口に関するデータを可視化します。このデータはGapminder Foundationによって収集され、Hans Rosling氏の有名なTEDトークで共有されました。まだこのトークを見ていない方は、ぜひ最初にご覧ください!

まず、vega-datasetsコレクションからデータセットを読み込み、Pandasデータフレームに取り込みましょう。

from vega_datasets import data as vega_data
data = vega_data.gapminder()

データのサイズはどれくらいでしょうか?

data.shape
(693, 6)

693行と6列です!データの内容を少し覗いてみましょう:

data.head(5)
year country cluster pop life_expect fertility
0 1955 Afghanistan 0 8891209 30.332 7.7
1 1960 Afghanistan 0 9829450 31.997 7.7
2 1965 Afghanistan 0 10997885 34.020 7.7
3 1970 Afghanistan 0 12430623 36.088 7.7
4 1975 Afghanistan 0 14132019 38.438 7.7

country(国)とyear(5年ごとの間隔)について、以下の指標が含まれています:

  • 1人の女性あたりの子どもの数を表す出生率(fertility

  • 年数で表した平均寿命(life_expect

  • 総人口(pop

さらに、clusterフィールドには整数コードが含まれています。これが何を表しているのか、データを可視化しながらこの謎を解き明かしてみましょう!

Let’s also create a smaller data frame, filtered down to values for the year 2000 only:

以下のように、2000年の値に絞り込んだ小さいデータフレームを作成しましょう:

data2000 = data.loc[data['year'] == 2000]
data2000.head(5)
year country cluster pop life_expect fertility
9 2000 Afghanistan 0 23898198 42.129 7.4792
20 2000 Argentina 3 37497728 74.340 2.3500
31 2000 Aruba 3 69539 73.451 2.1240
42 2000 Australia 4 19164620 80.370 1.7560
53 2000 Austria 1 8113413 78.980 1.3820

2.2. データ型#

効果的な可視化の最初の要素は入力データです。データ値はさまざまな測定形式を表すことができます。それらの測定がどのような比較をサポートするか、またそれに応じた視覚エンコーディングをどのようにサポートするかを考える必要があります。

ここでは、Altairが視覚エンコーディングの選択に役立てる基本的なデータ型について説明します。これらのデータ型は、可能な比較の種類を決定し、それにより可視化デザインの決定を導きます。

2.2.1. 名義型 (N)#

名義型カテゴリ型とも呼ばれる)は、カテゴリ名から成るデータです。

名義型データでは、値が等しいかどうかを比較できます:値Aは値Bと同じか、それとも異なるか?(A = B)。例えば、「AはBと等しい」または「AはBと等しくない」という主張をサポートします。上記のデータセットでは、countryフィールドが名義型です。

名義型データを可視化する際には、値が同じか異なるかを簡単に判別できる必要があります。位置、色相(青、赤、緑など)、形状が役立ちます。ただし、サイズチャネルを名義型データに使用すると、存在しないランク順や大きさの違いを示唆する可能性があるため、誤解を招くかもしれません!

2.2.2. 順序型 (O)#

順序型データは、特定の順序を持つ値から成ります。

順序型データでは、値のランク順を比較できます:値Aは値Bの前に来るか後に来るか?(A < B)。例えば、「AはBより小さい」または「AはBより大きい」という主張をサポートします。上記のデータセットでは、yearフィールドを順序型として扱うことができます。

順序型データを可視化する際には、ランク順が感覚的にわかるようにする必要があります。位置、サイズ、または色の明るさ(輝度)が適切ですが、色相(知覚的な順序がないため)はあまり適切ではありません。

2.2.3. 量的型 (Q)#

量的型データでは、値間の数値的な違いを測定できます。量的データにはいくつかのサブタイプがあります:

  • 間隔型データでは、ポイント間の距離(間隔)を測定できます:値Aは値Bからどれだけ離れているか?(A - B)。例えば、「AはBから12単位離れている」という主張をサポートします。

  • 比率型データでは、ゼロポイントに意味があるため、比率やスケール係数を測定できます:値Aは値Bの何倍か?(A / B)。例えば、「AはBの10%である」または「BはAの7倍大きい」という主張をサポートします。

上記のデータセットでは、yearは量的な間隔型フィールド(「年0」の値は主観的)ですが、fertilitylife_expectは量的な比率型フィールド(ゼロに意味があり、比率計算が可能)です。Vega-Liteは量的データを表しますが、間隔型と比率型の区別は行いません。

量的値は、位置、サイズ、色の明るさなどのチャネルを使用して可視化できます。比率値の比例比較にはゼロ基準の軸が重要ですが、間隔比較では省略可能です。

2.2.4. 時間型 (T)#

時間型データは、時間のポイントまたは間隔を測定します。この型は量的値(タイムスタンプ)の特殊なケースであり、豊富なセマンティクスと慣例(例:グレゴリオ暦)を持っています。Vega-Liteの時間型は、時間単位(年、月、日、時など)についての推論をサポートし、特定の時間間隔を要求するためのメソッドを提供します。

例として、"2019-01-04""Jan 04 2019"といった日付文字列、またはISO日付形式"2019-01-04T17:50:35.643Z"などがあります。

上記の世界開発データセットには、yearフィールドが単なる整数としてエンコードされているため、時間型の値は含まれていません。Altairで時間型データを使用する詳細については、時刻と日付に関するドキュメントをご覧ください。

2.2.5. まとめ#

これらのデータ型は互いに排他的ではなく、階層を形成します:順序型データは名義型(等式)比較をサポートし、量的データは順序型(ランク順)比較をサポートします。

さらに、これらのデータ型は固定的な分類を提供するものではありません。例えば、データフィールドが数値で表現されているからといって、必ずしも量的型として扱う必要はありません。例えば、一連の年齢(10歳、20歳など)を名義型(未成年か成年か)、順序型(年ごとにグループ化)、または量的型(平均年齢を計算)として解釈することができます。

それでは、これらのデータ型をどのように視覚的にエンコードするかを見ていきましょう!

2.3. エンコーディングチャネル#

Altairの中心となるのは、データフィールド(特定のデータ型を持つ)を、選択したマークタイプの利用可能なエンコーディングチャネルに結び付けるエンコーディングの利用です。このノートブックでは、次のエンコーディングチャネルについて説明します:

  • x: マークの水平位置(x軸)。

  • y: マークの垂直位置(y軸)。

  • size: マークのサイズ。マークのタイプによって面積または長さに対応します。

  • color: マークの色。有効なCSSカラーとして指定します。

  • opacity: マークの不透明度。0(完全に透明)から1(完全に不透明)の範囲で指定します。

  • shape: pointマークのプロット記号の形状。

  • tooltip: マウスホバー時に表示されるツールチップテキスト。

  • order: マークの順序。線/エリアのポイント順序や描画順序を決定します。

  • column: データを水平方向に揃えたサブプロットに分割します。

  • row: データを垂直方向に揃えたサブプロットに分割します。

利用可能なチャネルの完全なリストについては、Altairエンコーディングドキュメントを参照してください。

2.3.1. X#

xエンコーディングチャネルは、マークの水平位置(x座標)を設定します。また、軸やタイトルのデフォルト設定が自動的に選択されます。以下のチャートでは、量的データ型を選択した結果、連続的な線形軸スケールが使用されています:

alt.Chart(data2000).mark_point().encode(
    alt.X('fertility:Q')
)

2.3.2. Y#

yエンコーディングチャネルは、マークの垂直位置(y座標)を設定します。ここでは、clusterフィールドを順序型(O)のデータ型として追加しました。その結果、各ユニークな値に対してサイズ付きのバンドを持つ離散軸が生成され、デフォルトのステップサイズが適用されています:

alt.Chart(data2000).mark_point().encode(
    alt.X('fertility:Q'),
    alt.Y('cluster:O')
)

上記のチャートで、O型とQ型を入れ替えるとどうなるでしょうか?

代わりに、life_expectフィールドを量的(Q)変数として追加すると、両方の軸が線形スケールを持つ散布図が生成されます:

alt.Chart(data2000).mark_point().encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q')
)

デフォルトでは、量的な線形スケールの軸にはゼロが含まれ、比率データの適切な基準線が確保されます。ただし、場合によっては、ゼロ基準が意味を持たない場合や、間隔の比較に焦点を当てたい場合があります。ゼロの自動挿入を無効にするには、エンコーディングのscale属性を使用してスケールマッピングを設定します:

alt.Chart(data2000).mark_point().encode(
    alt.X('fertility:Q', scale=alt.Scale(zero=False)),
    alt.Y('life_expect:Q', scale=alt.Scale(zero=False))
)

これで、軸スケールにゼロが含まれなくなりました。ただし、軸のドメイン(範囲)の終端は、自動的に5や10などの きりの良い 数値に調整されるため、若干の余白が残ります。

上記のスケール属性にさらにnice=Falseを追加するとどうなるでしょうか?

2.3.3. サイズ#

sizeエンコーディングチャネルは、マークのサイズや範囲を設定します。このチャネルの意味は、マークの種類に応じて変化します。例えば、pointマークの場合、sizeチャネルはプロット記号のピクセル面積にマッピングされます。そのため、ポイントの直径はサイズ値の平方根に一致します。

散布図を拡張し、sizeチャネルに人口(pop)をエンコードしてみましょう。その結果、サイズ値を解釈するための凡例もチャートに追加されます。

alt.Chart(data2000).mark_point().encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q')
)

場合によっては、デフォルトのサイズ範囲に満足できないこともあります。カスタマイズしたサイズの範囲を指定するには、scale属性のrangeパラメータを設定し、最小サイズと最大サイズを示す配列を指定します。

ここでは、サイズエンコーディングを更新し、ゼロ値の場合は0ピクセル、スケールドメイン内の最大値の場合は1,000ピクセルに設定してみましょう:

alt.Chart(data2000).mark_point().encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000]))
)

2.3.4. 色と不透明度#

colorエンコーディングチャネルは、マークの色を設定します。色のエンコーディングスタイルはデータ型に大きく依存します。名義型データの場合は、通常、複数の色相を持つ定性的なカラースキームが使用されます。一方、順序型や量的データでは、知覚的に順序付けられた色のグラデーションが使用されます。

ここでは、colorチャネルと名義型(N)データ型を使用してclusterフィールドをエンコードし、各クラスタ値に異なる色相を割り当てます。clusterフィールドが何を示しているか、推測し始めることができますか?

alt.Chart(data2000).mark_point().encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000])),
    alt.Color('cluster:N')
)

塗りつぶした形状を使用したい場合、mark_pointメソッドにfilled=Trueパラメータを渡すことができます:

alt.Chart(data2000).mark_point(filled=True).encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000])),
    alt.Color('cluster:N')
)

デフォルトでは、Altairはプロットの重なりを軽減するために、わずかに透明度を適用します。透明度は、mark_*メソッドにデフォルト値を渡すか、専用のエンコーディングチャネルを使用してさらに調整することができます。

ここでは、データフィールドをバインドする代わりに、エンコーディングチャネルに一定の値を提供する方法を示します:

alt.Chart(data2000).mark_point(filled=True).encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000])),
    alt.Color('cluster:N'),
    alt.OpacityValue(0.5)
)

2.3.5. 形状#

shapeエンコーディングチャネルは、pointマークで使用される幾何学的な形状を設定します。これまでに見た他のチャネルとは異なり、shapeチャネルは他のマークタイプでは使用できません。また、shapeエンコーディングチャネルは名義型データでのみ使用するべきです。なぜなら、知覚的なランク順や大きさの比較には対応していないためです。

ここでは、clusterフィールドをshapecolorの両方でエンコードしてみましょう。同じ基礎データフィールドに複数のチャネルを使用することは、冗長エンコーディングと呼ばれます。この結果として得られるチャートは、色と形状の情報を組み合わせた単一のシンボル凡例を持ちます:

alt.Chart(data2000).mark_point(filled=True).encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000])),
    alt.Color('cluster:N'),
    alt.OpacityValue(0.5),
    alt.Shape('cluster:N')
)

2.3.6. ツールチップと順序#

ここまで進めると、少しもどかしさを感じるかもしれません。チャートを構築してきましたが、可視化されたポイントがどの国に対応しているのかまだ分からないからです!インタラクティブなツールチップを追加して、探索を可能にしましょう。

tooltipエンコーディングチャネルは、ユーザーがマークにマウスカーソルを移動したときに表示されるツールチップテキストを決定します。countryフィールドのツールチップエンコーディングを追加して、どの国が表されているのか調べてみましょう。

alt.Chart(data2000).mark_point(filled=True).encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000])),
    alt.Color('cluster:N'),
    alt.OpacityValue(0.5),
    alt.Tooltip('country')
)

マウスを動かしていると、一部のポイントを選択できないことに気付くかもしれません。例えば、最も大きな濃い青色の円はインドを表しており、より小さい人口の国の上に描画されているため、その国の上にマウスをホバーできません。この問題を解決するには、orderエンコーディングチャネルを使用します。

orderエンコーディングチャネルは、データポイントの順序を決定します。これにより、描画の順序や、lineおよびareaマークにおけるポイント同士の接続順序に影響を与えます。

ここでは、人口(pop)による降順で値を並べ替え、大きな円の後に小さな円が描画されるように設定してみましょう:

alt.Chart(data2000).mark_point(filled=True).encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000])),
    alt.Color('cluster:N'),
    alt.OpacityValue(0.5),
    alt.Tooltip('country:N'),
    alt.Order('pop:Q', sort='descending')
)

これで、インドによって隠されていた小さな国を特定できました。それはバングラデシュです!

また、clusterフィールドが何を表しているかも推測できるようになります。さまざまな色のポイントにマウスをホバーして、自分なりの説明を考えてみてください。

ここまでで、ツールチップに基礎データレコードの単一プロパティを表示するようにしました。複数の値を表示するには、tooltipチャネルにエンコーディングの配列を渡します。この配列には、表示したい各フィールドを指定します:

alt.Chart(data2000).mark_point(filled=True).encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000])),
    alt.Color('cluster:N'),
    alt.OpacityValue(0.5),
    alt.Order('pop:Q', sort='descending'),
    tooltip = [
        alt.Tooltip('country:N'),
        alt.Tooltip('fertility:Q'),
        alt.Tooltip('life_expect:Q')
    ]   
)

これで、マウスホバー時に複数のデータフィールドを表示できるようになりました!

2.3.7. 列と行のファセット#

空間的な位置は視覚エンコーディングにおいて最も強力で柔軟なチャネルの1つですが、すでにフィールドをxyチャネルに割り当てている場合はどうすればよいでしょうか?有用な手法の1つは、データのサブセットを表示するサブプロットからなるトレリスプロットを作成することです。トレリスプロットは、データをスモールマルチプルのビューで表示する一般的な手法の一例です。

columnrowエンコーディングチャネルは、指定されたデータフィールドに従ってデータを分割し、横方向(列)または縦方向(行)にサブプロットを生成します。

以下は、データをclusterの値ごとに1列に分割したトレリスプロットの例です:

alt.Chart(data2000).mark_point(filled=True).encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000])),
    alt.Color('cluster:N'),
    alt.OpacityValue(0.5),
    alt.Tooltip('country:N'),
    alt.Order('pop:Q', sort='descending'),
    alt.Column('cluster:N')
)

上記のプロットは画面に収まりきらず、すべてのサブプロットを比較するのが難しくなっています。デフォルトのwidthheightプロパティを設定して、より小さなスモールマルチプルを作成できます。また、列ヘッダーがすでにclusterの値をラベルとして表示しているため、colorの凡例をNoneに設定して削除しましょう。さらに、スペースを有効活用するために、sizeの凡例をチャートの'bottom'に配置するよう設定することもできます。

alt.Chart(data2000).mark_point(filled=True).encode(
    alt.X('fertility:Q'),
    alt.Y('life_expect:Q'),
    alt.Size('pop:Q', scale=alt.Scale(range=[0,1000]),
             legend=alt.Legend(orient='bottom', titleOrient='left')),
    alt.Color('cluster:N', legend=None),
    alt.OpacityValue(0.5),
    alt.Tooltip('country:N'),
    alt.Order('pop:Q', sort='descending'),
    alt.Column('cluster:N')
).properties(width=135, height=135)

columnrowのエンコーディングは、内部的にはfacetビュー構成オペレーターを使用する新しい仕様に変換されます。このfacetについては後ほどさらに詳しく取り上げます!

それまでの間に、上記のチャートを列ではなく行に分割するように書き換えることができますか?

2.3.8. 先取り: インタラクティブフィルタリング#

後のモジュールでは、データ探索のためのインタラクション手法を詳しく学びます。ここではその先取りとして、yearフィールドにレンジスライダーをバインドし、各年のデータをインタラクティブにスクラブできる機能を紹介します。この時点で以下のコードが少し難しく感じられても心配しないでください。後でインタラクションについて詳しく説明します。

スライダーを前後に動かして、データ値が時間とともにどのように変化するかを確認してください!

select_year = alt.selection_single(
    name='select', fields=['year'], init={'year': 1955},
    bind=alt.binding_range(min=1955, max=2005, step=5)
)

alt.Chart(data).mark_point(filled=True).encode(
    alt.X('fertility:Q', scale=alt.Scale(domain=[0,9])),
    alt.Y('life_expect:Q', scale=alt.Scale(domain=[0,90])),
    alt.Size('pop:Q', scale=alt.Scale(domain=[0, 1200000000], range=[0,1000])),
    alt.Color('cluster:N', legend=None),
    alt.OpacityValue(0.5),
    alt.Tooltip('country:N'),
    alt.Order('pop:Q', sort='descending')
).add_selection(select_year).transform_filter(select_year)
/Users/yuichiyazaki/.pyenv/versions/miniforge3-4.10.3-10/lib/python3.9/site-packages/altair/utils/deprecation.py:65: AltairDeprecationWarning: 'selection_single' is deprecated.  Use 'selection_point'
  warnings.warn(message, AltairDeprecationWarning, stacklevel=1)
/Users/yuichiyazaki/.pyenv/versions/miniforge3-4.10.3-10/lib/python3.9/site-packages/altair/vegalite/v5/api.py:417: AltairDeprecationWarning: Use 'value' instead of 'init'.
  warnings.warn(
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [22], line 1
----> 1 select_year = alt.selection_single(
      2     name='select', fields=['year'], init={'year': 1955},
      3     bind=alt.binding_range(min=1955, max=2005, step=5)
      4 )
      6 alt.Chart(data).mark_point(filled=True).encode(
      7     alt.X('fertility:Q', scale=alt.Scale(domain=[0,9])),
      8     alt.Y('life_expect:Q', scale=alt.Scale(domain=[0,90])),
   (...)
     13     alt.Order('pop:Q', sort='descending')
     14 ).add_selection(select_year).transform_filter(select_year)

File ~/.pyenv/versions/miniforge3-4.10.3-10/lib/python3.9/site-packages/altair/utils/deprecation.py:66, in _deprecate.<locals>.new_obj(*args, **kwargs)
     63 @functools.wraps(obj)
     64 def new_obj(*args, **kwargs):
     65     warnings.warn(message, AltairDeprecationWarning, stacklevel=1)
---> 66     return obj(*args, **kwargs)

File ~/.pyenv/versions/miniforge3-4.10.3-10/lib/python3.9/site-packages/altair/vegalite/v5/api.py:753, in selection_single(**kwargs)
    747 @utils.deprecation.deprecated(
    748     message="'selection_single' is deprecated.  Use 'selection_point'"
    749 )
    750 @utils.use_signature(core.PointSelectionConfig)
    751 def selection_single(**kwargs):
    752     """'selection_single' is deprecated.  Use 'selection_point'"""
--> 753     return _selection(type="point", **kwargs)

File ~/.pyenv/versions/miniforge3-4.10.3-10/lib/python3.9/site-packages/altair/vegalite/v5/api.py:478, in _selection(type, **kwds)
    475 else:
    476     raise ValueError("""'type' must be 'point' or 'interval'""")
--> 478 return param(select=select, **param_kwds)

File ~/.pyenv/versions/miniforge3-4.10.3-10/lib/python3.9/site-packages/altair/vegalite/v5/api.py:444, in param(name, value, bind, empty, expr, **kwds)
    442     parameter.param_type = "selection"
    443 else:
--> 444     parameter.param = core.SelectionParameter(
    445         name=parameter.name, bind=bind, value=value, expr=expr, **kwds
    446     )
    447     parameter.param_type = "selection"
    449 return parameter

TypeError: altair.vegalite.v5.schema.core.SelectionParameter() got multiple values for keyword argument 'value'

2.4. グラフィカルマーク#

上記でエンコーディングチャネルを探求する際には、pointマークを使用してデータを可視化してきました。しかし、pointマークはデータを視覚的に表現するために使用できる多くの幾何学的形状の1つに過ぎません。Altairには、以下を含む多くの組み込みマークタイプが用意されています:

  • mark_area() - トップラインとベースラインで定義された塗りつぶしエリア。

  • mark_bar() - 長方形の棒グラフ。

  • mark_circle() - 散布図のポイントとして使用される塗りつぶした円。

  • mark_line() - 接続された線分。

  • mark_point() - 形状を設定可能な散布図のポイント。

  • mark_rect() - 塗りつぶされた長方形(ヒートマップに便利)。

  • mark_rule() - 軸をまたぐ垂直または水平の線。

  • mark_square() - 散布図のポイントとして使用される塗りつぶした四角形。

  • mark_text() - テキストで表現された散布図のポイント。

  • mark_tick() - 垂直または水平の目盛り線。

完全なリストや例へのリンクについては、Altairマークのドキュメントを参照してください。次に、統計グラフィックで最も一般的に使用されるマークタイプをいくつか順に紹介します。

2.4.1. ポイントマーク#

pointマークタイプは、散布図ドットプロットのように特定のポイントを表現します。xおよびyエンコーディングチャネル(2Dポイントの位置を指定)に加えて、pointマークはcolorsizeshapeのエンコーディングを使用して追加のデータフィールドを伝達できます。

以下は、fertilityのドットプロットで、clusterフィールドをyshapeチャネルの両方で冗長的にエンコードしています。

alt.Chart(data2000).mark_point().encode(
    alt.X('fertility:Q'),
    alt.Y('cluster:N'),
    alt.Shape('cluster:N')
)

エンコーディングチャネルに加えて、mark_*()メソッドに値を渡すことでマークをスタイリングすることができます。

例えば、ポイントマークはデフォルトではアウトライン付きで描画されますが、filled形状を使用するように指定することも可能です。同様に、デフォルトのsizeを設定してポイントマークの総ピクセル面積を指定することもできます。

alt.Chart(data2000).mark_point(filled=True, size=100).encode(
    alt.X('fertility:Q'),
    alt.Y('cluster:N'),
    alt.Shape('cluster:N')
)

2.4.2. サークルマーク#

circleマークタイプは、塗りつぶされた円として描画されるpointマークの便利な省略形です。

alt.Chart(data2000).mark_circle(size=100).encode(
    alt.X('fertility:Q'),
    alt.Y('cluster:N'),
    alt.Shape('cluster:N')
)

2.4.3. スクエアマーク#

squareマークタイプは、塗りつぶされた四角形として描画されるpointマークの便利な省略形です。

alt.Chart(data2000).mark_square(size=100).encode(
    alt.X('fertility:Q'),
    alt.Y('cluster:N'),
    alt.Shape('cluster:N')
)

2.4.4. ティックマーク#

tickマークタイプは、短い線分または「目盛り」を使用してデータポイントを表現します。これらは、1次元に沿った値を最小限の重なりで比較するのに特に有用です。ティックマークで描画されたドットプロットは、ストリッププロットと呼ばれることもあります。

alt.Chart(data2000).mark_tick().encode(
    alt.X('fertility:Q'),
    alt.Y('cluster:N'),
    alt.Shape('cluster:N')
)

2.4.5. バーマーク#

barマークタイプは、位置、幅、高さを持つ長方形を描画します。

以下のプロットは、各国の人口(pop)を示すシンプルな棒グラフです。

alt.Chart(data2000).mark_bar().encode(
    alt.X('country:N'),
    alt.Y('pop:Q')
)

棒の幅はデフォルトのサイズに設定されています。棒の幅を調整する方法については、このノートブックの後半で説明します。(軸、スケール、および凡例の設定に関しては、後のノートブックで詳しく見ていきます。)

棒を積み重ねることも可能です。xエンコーディングをclusterフィールドに変更し、countrycolorチャネルでエンコードしてみましょう。また、すべての国の色を表示すると凡例が非常に長くなるため、凡例を無効にし、国名を表示するためにツールチップを使用します。

alt.Chart(data2000).mark_bar().encode(
    alt.X('cluster:N'),
    alt.Y('pop:Q'),
    alt.Color('country:N', legend=None),
    alt.Tooltip('country:N')
)

上記のチャートでは、colorエンコーディングチャネルの使用により、Altair / Vega-Liteが自動的にバーを積み重ねています。これがなければ、バーが互いに重なって描画されてしまいます!yエンコーディングチャネルにパラメータstack=Noneを追加して、積み重ねを適用しない場合に何が起こるか試してみてください…

上記の例では、棒グラフはゼロ基準から作成されており、yチャネルは非ゼロ値(または棒の高さ)のみをエンコードしています。しかし、バーのマークでは、範囲を表現するために開始点と終了点を指定することも可能です。

以下のチャートでは、x(開始点)とx2(終了点)チャネルを使用して、各地域クラスター内の平均寿命の範囲を示しています。この例では、範囲の終端を決定するためにminmaxの集計関数を使用しています。集計については、次のノートブックで詳しく説明します!

また、xwidthを使用して、開始点とオフセットを指定することで、x2 = x + widthのように終了点を設定することもできます。

alt.Chart(data2000).mark_bar().encode(
    alt.X('min(life_expect):Q'),
    alt.X2('max(life_expect):Q'),
    alt.Y('cluster:N')
)

2.4.6. ラインマーク#

lineマークタイプは、プロットされたポイントを線分で接続します。例えば、線の傾きが変化率に関する情報を伝えるために使用されます。

ここでは、フィルタリングされていない世界開発データフレーム全体を使用して、年ごとの各国の出生率を示すラインチャートを作成します。凡例は非表示にし、代わりにツールチップを使用します。

alt.Chart(data).mark_line().encode(
    alt.X('year:O'),
    alt.Y('fertility:Q'),
    alt.Color('country:N', legend=None),
    alt.Tooltip('country:N')
).properties(
    width=400
)

国ごとに興味深い変動が見られますが、全体としては家族あたりの子どもの数が時間とともに減少する傾向が確認できます。また、幅を400ピクセルにカスタマイズした点にも注目してください。幅を変更または削除して、どのように変化するか試してみてください!

次に、デフォルトのマークパラメータを変更してプロットをカスタマイズします。strokeWidthを設定して線の太さを決定し、opacityで透明度を追加することができます。デフォルトでは、lineマークはデータポイントを直線で接続しますが、場合によっては線を滑らかにしたいこともあります。データポイントを接続する際の補間方法は、interpolateマークパラメータを設定することで調整できます。ここでは、滑らかな線を提供すると同時に補間によって「偽の」最小値や最大値が生成されないよう保証する'monotone'補間を使用します。

alt.Chart(data).mark_line(
    strokeWidth=3,
    opacity=0.5,
    interpolate='monotone'
).encode(
    alt.X('year:O'),
    alt.Y('fertility:Q'),
    alt.Color('country:N', legend=None),
    alt.Tooltip('country:N')
).properties(
    width=400
)

lineマークは、スロープグラフを作成するためにも使用できます。スロープグラフは、2つの比較ポイント間の値の変化を線の傾きで強調するチャートです。

以下では、データセット全体の最小年(1955年)と最大年(2005年)における各国の人口を比較するスロープグラフを作成します。まず、それらの年に絞り込んだ新しいPandasデータフレームを作成し、その後Altairを使用してスロープグラフを作成します。

デフォルトでは、Altairは年をx軸上で近接して配置します。x軸上の年をより適切に間隔を空けて配置するために、以下のコメントで示されているように、チャートの幅における離散的なステップのサイズ(ピクセル単位)を指定できます。以下のstep値を調整して、チャートがどのように変化するかを確認してみてください。

dataTime = data.loc[(data['year'] == 1955) | (data['year'] == 2005)]

alt.Chart(dataTime).mark_line(opacity=0.5).encode(
    alt.X('year:O'),
    alt.Y('pop:Q'),
    alt.Color('country:N', legend=None),
    alt.Tooltip('country:N')
).properties(
    width={"step": 50} # adjust the step parameter
)

2.4.7. エリアマーク#

areaマークタイプは、lineマークとbarマークの特徴を組み合わせています。データポイント間の接続(傾き)を視覚化すると同時に、塗りつぶされた領域も表示します。この塗りつぶし領域の1つの端は、デフォルトでゼロ値の基準線に設定されます。

以下のチャートは、アメリカ合衆国における時間の経過に伴う人口を示すエリアチャートです:

dataUS = data.loc[data['country'] == 'United States']

alt.Chart(dataUS).mark_area().encode(
    alt.X('year:O'),
    alt.Y('fertility:Q')
)

lineマークと同様に、areaマークもinterpolateパラメータをサポートしています。

alt.Chart(dataUS).mark_area(interpolate='monotone').encode(
    alt.X('year:O'),
    alt.Y('fertility:Q')
)

barマークと同様に、areaマークも積み重ねをサポートしています。ここでは、北米の3か国のデータを含む新しいデータフレームを作成し、それをareaマークとcolorエンコーディングチャネルを使用して、国ごとに積み重ねたチャートを作成します。

dataNA = data.loc[
    (data['country'] == 'United States') |
    (data['country'] == 'Canada') |
    (data['country'] == 'Mexico')
]

alt.Chart(dataNA).mark_area().encode(
    alt.X('year:O'),
    alt.Y('pop:Q'),
    alt.Color('country:N')
)

デフォルトでは、積み重ねはゼロ基準に対して行われます。ただし、他のstackオプションも利用可能です:

  • center - チャートの中央に基準線を設定し、ストリームグラフのビジュアライゼーションを作成。

  • normalize - 各積み重ねポイントで合計データを100%に正規化し、割合比較を可能にします。

以下では、yエンコーディングのstack属性をcenterに設定してチャートを調整します。代わりにnormalizeを設定すると、どのように変化するでしょうか?

alt.Chart(dataNA).mark_area().encode(
    alt.X('year:O'),
    alt.Y('pop:Q', stack='center'),
    alt.Color('country:N')
)

積み重ねを完全に無効にするには、stack属性をNoneに設定します。また、デフォルトのマークパラメータとしてopacityを追加することで、重なり合った領域を視認しやすくすることができます!

alt.Chart(dataNA).mark_area(opacity=0.5).encode(
    alt.X('year:O'),
    alt.Y('pop:Q', stack=None),
    alt.Color('country:N')
)

areaマークタイプは、データに基づいた基準線もサポートしており、上下の系列がデータフィールドによって決定されます。barマークと同様に、xx2(またはyy2)チャネルを使用して、エリアマークの終端ポイントを指定することができます。

以下のチャートは、北米諸国における年ごとの出生率の最小値と最大値の範囲を可視化しています:

alt.Chart(dataNA).mark_area().encode(
    alt.X('year:O'),
    alt.Y('min(fertility):Q'),
    alt.Y2('max(fertility):Q')
).properties(
    width={"step": 40}
)

1995年には、約4未満から約7未満までの広い範囲の値が見られます。一方、2005年には出生率全体の値とその変動幅が共に減少し、家族あたり子ども2人を中心に収束していることがわかります.

上記のすべてのareaマークの例では、縦方向のエリアを使用しています。しかし、AltairとVega-Liteは横方向のエリアもサポートしています。xチャネルとyチャネルを入れ替えるだけで、上記のチャートを横方向に変換してみましょう。

alt.Chart(dataNA).mark_area().encode(
    alt.Y('year:O'),
    alt.X('min(fertility):Q'),
    alt.X2('max(fertility):Q')
).properties(
    width={"step": 40}
)

2.5. まとめ#

データ型、エンコーディングチャネル、グラフィカルマークの探索を完了しました!これで、エンコーディング、マークタイプ、およびマークパラメータの世界をさらに探求する準備が整いました。より包括的なリファレンス(ここで省略した機能も含む!)については、Altairのマークエンコーディングのドキュメントをご覧ください。

次のモジュールでは、データを要約したり、新しい派生フィールドを可視化したりするためのデータ変換の使用方法を見ていきます。また、後のモジュールでは、スケール、軸、凡例を変更してチャートをさらにカスタマイズする方法を学びます。

視覚エンコーディングについてもっと学びたいですか?


Mike Bostockによる適応版、Sémiologie Graphiqueからのベルタンの視覚エンコーディング分類。

  • マーク、視覚エンコーディング、および基盤となるデータ型の体系的な研究は、ジャック・ベルタン (Jacques Bertin)が1967年に発表した先駆的な著書 Sémiologie Graphique (グラフィックの記号論) によって始まりました。上記の画像は、位置、サイズ、値(明るさ)、テクスチャ、色相、向き、形状チャネルを示し、それらがサポートするデータ型に関するベルタンの推奨を説明しています。

  • データ型、マーク、チャネルのフレームワークは、1986年に発表されたMackinlayのAPT (A Presentation Tool)を皮切りに、VoyagerDracoのような近年のシステムに至るまで、_自動化_された可視化デザインツールにも影響を与えています。

  • 名義型、順序型、間隔型、比率型の識別は、少なくともS. S. Stevenが1947年に発表した記事 On the theory of scales of measurement までさかのぼります。