4. スケール、軸、凡例#

視覚的エンコーディング – データを位置、サイズ、形状、色などの視覚的変数にマッピングすること – はデータ可視化の中心的な要素です。このマッピングを実際に行う主役がスケールです。スケールは、データ値(スケールのドメイン)を入力として受け取り、ピクセル位置やRGB色などの視覚的な値(スケールのレンジ)を出力する関数です。もちろん、可視化は内容が伝わらなければ意味がありません!グラフィカルマークに加え、チャートには読者がグラフィックを解読するための参照要素、つまりガイドが必要です。(空間的な範囲でスケールを可視化する)や凡例(色、サイズ、形状の範囲でスケールを可視化する)といったガイドは、効果的なデータ可視化の縁の下の力持ちです。

このノートブックでは、スケールマッピング、軸、凡例のカスタマイズ設計をサポートするAltairのオプションを、抗生物質の効果に関する例を使って探っていきます。

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

import pandas as pd
import altair as alt

4.1. 抗生物質データ#

第二次世界大戦後、抗生物質は「奇跡の薬」と見なされ、それまで治療が困難だった病気に対する簡単な治療法として広まりました。どの抗生物質がどの細菌感染症に最も効果的かを調べるため、16種類の細菌に対する3つの最も人気のある抗生物質の効果が収集されました。

vega-datasetsコレクションから抗生物質データセットを使用します。以下の例では、このデータセットのURLをAltairに直接渡します。

antibiotics = 'https://cdn.jsdelivr.net/npm/vega-datasets@1/data/burtin.json'

まず、Pandasを使用してデータを読み込み、データセット全体を表示し、利用可能なフィールドに慣れ親しみましょう:

pd.read_json(antibiotics)
Bacteria Penicillin Streptomycin Neomycin Gram_Staining Genus
0 Aerobacter aerogenes 870.000 1.00 1.600 negative other
1 Bacillus anthracis 0.001 0.01 0.007 positive other
2 Brucella abortus 1.000 2.00 0.020 negative other
3 Diplococcus pneumoniae 0.005 11.00 10.000 positive other
4 Escherichia coli 100.000 0.40 0.100 negative other
5 Klebsiella pneumoniae 850.000 1.20 1.000 negative other
6 Mycobacterium tuberculosis 800.000 5.00 2.000 negative other
7 Proteus vulgaris 3.000 0.10 0.100 negative other
8 Pseudomonas aeruginosa 850.000 2.00 0.400 negative other
9 Salmonella (Eberthella) typhosa 1.000 0.40 0.008 negative Salmonella
10 Salmonella schottmuelleri 10.000 0.80 0.090 negative Salmonella
11 Staphylococcus albus 0.007 0.10 0.001 positive Staphylococcus
12 Staphylococcus aureus 0.030 0.03 0.001 positive Staphylococcus
13 Streptococcus fecalis 1.000 1.00 0.100 positive Streptococcus
14 Streptococcus hemolyticus 0.001 14.00 10.000 positive Streptococcus
15 Streptococcus viridans 0.005 10.00 40.000 positive Streptococcus

表中の数値は、抗生物質の効果を測定する指標である最小発育阻止濃度 (MIC)を示しています。これは、in vitro(試験管内)での細菌の成長を防ぐために必要な抗生物質の濃度(マイクログラム/ミリリットル)を表します。また、細菌の反応を示す名義型フィールドGram_Stainingは、グラム染色と呼ばれる手法によって記録されています。濃青色または紫色に変色する細菌はグラム陽性(Gram-positive)です。それ以外はグラム陰性(Gram-negative)と分類されます。

このデータセットのさまざまな可視化を検討しながら、自問してみてください:

  • 抗生物質の相対的な効果について何を学べるでしょうか?

  • 細菌種の抗生物質反応に基づいて、細菌について何を理解できるでしょうか?

4.2. スケールと軸の設定#

4.2.1. 抗生物質耐性のプロット:スケールタイプの調整#

まず、NeomycinのMICを示すシンプルなドットプロットを見てみましょう。

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q')
)

MIC値が桁違いの範囲にわたることがわかります。ほとんどのポイントは左側に集中し、右側にはいくつかの大きな外れ値があります。

デフォルトでは、Altairはドメイン値(MIC)とレンジ値(ピクセル)の間にlinearマッピングを使用します。データの全体像をよりよく把握するために、別のスケール変換を適用してみましょう。

スケールタイプを変更するには、scale属性を設定し、alt.Scaleメソッドとtypeパラメータを使用します。

以下は平方根(sqrt)スケールタイプを使用した結果です。ピクセル範囲の距離がデータドメインの距離の平方根に対応するようになります。

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          scale=alt.Scale(type='sqrt'))
)

左側のポイントは以前よりも区別しやすくなりましたが、依然として強い偏りが見られます。

次に、対数スケールlog)を使用してみましょう:

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          scale=alt.Scale(type='log'))
)

この結果、データはより均等に分布し、異なる細菌に対して必要な濃度の非常に大きな違いが明確に見えるようになりました。

標準的な線形スケールでは、視覚的な距離(ピクセル単位)10がデータドメインでの加算10に対応することがあります。一方、対数変換では、乗算と加算の間でマッピングが行われ、log(u) + log(v) = log(u*v)が成り立ちます。その結果、対数スケールでは、視覚的な距離10がデータドメインでの乗算10に対応します(対数の底が10の場合)。上記のlogスケールはデフォルトで底10の対数を使用しますが、baseパラメータをスケールに指定することでこの値を調整できます。

4.2.2. 軸のスタイリング#

低い投与量はより高い効果を示します。しかし、一部の人々は「良い」値がチャート内で「右上」に位置することを期待するかもしれません。この慣習に合わせる場合、「効果」を逆方向のMICスケールとしてエンコードするために軸を反転させることができます。

これを実現するには、エンコーディングのsortプロパティを'descending'に設定します。

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log'))
)

残念ながら、この軸は少し混乱を招き始めています。データを対数スケールでプロットし、逆方向に表示し、単位が明確に示されていないからです!

より情報豊かな軸タイトルを追加してみましょう。エンコーディングのtitleプロパティを使用して、目的のタイトルテキストを設定します。

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log'),
          title='Neomycin MIC (μg/ml, reverse log scale)')
)

ずっと良くなりましたね!

デフォルトでは、Altairはx軸をチャートの下部に配置します。このデフォルトを変更するには、axis属性を追加し、orient='top'を指定します。

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log'),
          axis=alt.Axis(orient='top'),
          title='Neomycin MIC (μg/ml, reverse log scale)')
)

同様に、y軸はデフォルトで'left'(左側)に配置されますが、'right'(右側)に設定することもできます。

4.2.3. 抗生物質の比較:グリッドライン、目盛りの数、およびサイズの調整#

Neomycinは、streptomycinやpenicillinのような他の抗生物質とどのように比較されるのでしょうか?

この質問に答え始めるために、散布図を作成し、y軸に別の抗生物質のエンコーディングを追加します。これは、x軸に設定したneomycinのデザインを反映させたものです。

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log'),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Streptomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log'),
          title='Streptomycin MIC (μg/ml, reverse log scale)')
)

Neomycinとstreptomycinは高い相関があるようで、細菌株が両方の抗生物質に対して類似した反応を示していることがわかります。

次に、neomycinとpenicillinを比較してみましょう:

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log'),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log'),
          title='Penicillin MIC (μg/ml, reverse log scale)')
)

ここでは、より区別された反応が見られます。一部の細菌はneomycinにはよく反応するがpenicillinには反応せず、その逆も見られます!

このプロットは有用ですが、さらに改善できます。x軸とy軸は同じ単位を使用していますが、範囲(幅が高さより大きい)やドメイン(x軸は0.001から100、y軸は0.001から1,000)が異なります。

軸を統一してみましょう。チャートに明示的なwidthheightの設定を追加し、スケールのdomainプロパティを使用して一致するドメインを指定します。

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          title='Penicillin MIC (μg/ml, reverse log scale)')
).properties(width=250, height=250)

このプロットはよりバランスが取れており、微妙な誤解を招きにくくなりました!

しかし、現在のグリッドラインはやや密集しています。グリッドラインを完全に削除したい場合は、axis属性にgrid=Falseを追加できます。しかし、代わりに目盛り線の数を減らし、例えば桁ごとにグリッドラインを含めたい場合はどうすればよいでしょうか?

目盛りの数を変更するには、AxisオブジェクトのtickCountプロパティで目標とする目盛り数を指定できます。tickCountはAltairにとって提案として扱われ、人間に優しい間隔など他の要素と一緒に考慮されます。正確に指定した目盛りの数を得られるわけではありませんが、近いものが得られるはずです。

alt.Chart(antibiotics).mark_circle().encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)')
).properties(width=250, height=250)

tickCountを5に設定することで、目的の効果が得られました。

散布図のポイントが少し小さく感じられます。デフォルトのサイズを変更するには、サークルマークのsizeプロパティを設定します。このサイズ値は、マークの面積をピクセル単位で指定します。

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'), 
).properties(width=250, height=250)

ここでは、サークルマークの面積を80ピクセルに設定しました。必要に応じて値をさらに調整してください!

4.3. 色凡例の設定#

4.3.1. グラム染色による色分け#

上記では、neomycinが一部の細菌に対してより効果的であり、penicillinが他の細菌に対してより効果的であることがわかりました。しかし、特定の細菌種がわからない場合、どの抗生物質を使用すればよいかをどうやって判断すれば良いのでしょうか?グラム染色は、細菌の分類を区別する診断手法として役立ちます!

Gram_Stainingを名義型データとしてcolorチャネルにエンコードしてみましょう:

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Gram_Staining:N')
).properties(width=250, height=250)

グラム陽性細菌はペニシリンに最も感受性が高く、グラム陰性細菌にはネオマイシンの方が効果的であるようです!

上記のカラースキームは、名義型データ(等しいか等しくないかの比較)のために知覚的に区別しやすい色を自動的に選択しています。しかし、使用する色をカスタマイズしたい場合があります。この場合、グラム染色の結果は物理的に特徴的な色分け:グラム陰性はピンク、グラム陽性は紫となります。

データのdomain(ドメイン)から色のrange(レンジ)への明示的なスケールマッピングを指定して、それらの色を使用してみましょう:

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Gram_Staining:N',
          scale=alt.Scale(domain=['negative', 'positive'], range=['hotpink', 'purple'])
    )
).properties(width=250, height=250)

デフォルトでは、凡例はチャートの右側に配置されます。軸と同様に、orientパラメータを使用して凡例の配置を変更できます。

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Gram_Staining:N',
          scale=alt.Scale(domain=['negative', 'positive'], range=['hotpink', 'purple']),
          legend=alt.Legend(orient='left')
    )
).properties(width=250, height=250)

凡例を完全に削除するには、legend=Noneを指定します。

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Gram_Staining:N',
          scale=alt.Scale(domain=['negative', 'positive'], range=['hotpink', 'purple']),
          legend=None
    )
).properties(width=250, height=250)

4.3.2. 種ごとの色分け#

これまで、抗生物質の有効性について考えてきました。次に視点を変えて、抗生物質の反応が異なる細菌種について私たちに何を教えてくれるかを考えてみましょう。

まず、名義型データフィールドであるBacteriacolorチャネルでエンコードしてみましょう:

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Bacteria:N')
).properties(width=250, height=250)

結果は少し混乱しています! 細菌の種類が十分に多いため、Altairは名義値のデフォルト10色パレットを繰り返し使用しています。

カスタムカラーを使用するには、色のエンコーディングのscaleプロパティを更新できます。一つの方法は、上記のグラム染色の例と同様に、スケールのdomain(ドメイン)とrange(レンジ)の値を明示的に指定し、それぞれの値に対する正確な色のマッピングを示すことです。もう一つの方法は、代替のカラースキームを使用することです。Altairには、さまざまな組み込みのカラースキームが含まれています。完全なリストについては、Vegaのカラースキームドキュメントを参照してください。

組み込みの20色スキームであるtableau20に切り替え、スケールのschemeプロパティを使用して設定してみましょう。

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Bacteria:N',
          scale=alt.Scale(scheme='tableau20'))
).properties(width=250, height=250)

これで各細菌にユニークな色が割り当てられましたが、チャートはまだ混乱しています。特に問題なのは、エンコーディングが同じ属(genus)に属する細菌を考慮していない点です。例えば、上記のチャートでは、2つの異なるサルモネラ(Salmonella)株が非常に異なる色相(ティールとピンク)で表されていますが、生物学的には近い親戚です。

別のスキームを試すには、データ型を名義型(nominal)から順序型(ordinal)に変更することもできます。デフォルトの順序型スキームは、青の階調を使用し、薄い青から濃い青へと変化します:

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Bacteria:O')
).properties(width=250, height=250)

Some of those blue shades may be hard to distinguish.

For more differentiated colors, we can experiment with alternatives to the default blues color scheme. The viridis scheme ramps through both hue and luminance:

alt.Chart(antibiotics).mark_circle(size=80).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Bacteria:O',
          scale=alt.Scale(scheme='viridis'))
).properties(width=250, height=250)

Bacteria from the same genus now have more similar colors than before, but the chart still remains confusing. There are many colors, they are hard to look up in the legend accurately, and two bacteria may have similar colors but different genus.

4.3.3. Color by Genus#

Let’s try to color by genus instead of bacteria. To do so, we will add a calculate transform that splits up the bacteria name on space characters and takes the first word in the resulting array. We can then encode the resulting Genus field using the tableau20 color scheme.

(Note that the antibiotics dataset includes a pre-calculated Genus field, but we will ignore it here in order to further explore Altair’s data transformations.)

alt.Chart(antibiotics).mark_circle(size=80).transform_calculate(
    Genus='split(datum.Bacteria, " ")[0]'
).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Genus:N',
          scale=alt.Scale(scheme='tableau20'))
).properties(width=250, height=250)

Hmm… While the data are better segregated by genus, this cacapohony of colors doesn’t seem particularly useful.

If we look at some of the previous charts carefully, we can see that only a handful of bacteria have a genus shared with another bacteria: Salmonella, Staphylococcus, and Streptococcus. To focus our comparison, we might add colors only for these repeated genus values.

Let’s add another calculate transform that takes a genus name, keeps it if it is one of the repeated values, and otherwise uses the string "Other".

In addition, we can add custom color encodings using explicit domain and range arrays for the color encoding scale.

alt.Chart(antibiotics).mark_circle(size=80).transform_calculate(
  Split='split(datum.Bacteria, " ")[0]'
).transform_calculate(
  Genus='indexof(["Salmonella", "Staphylococcus", "Streptococcus"], datum.Split) >= 0 ? datum.Split : "Other"'
).encode(
    alt.X('Neomycin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Neomycin MIC (μg/ml, reverse log scale)'),
    alt.Y('Penicillin:Q',
          sort='descending',
          scale=alt.Scale(type='log', domain=[0.001, 1000]),
          axis=alt.Axis(tickCount=5),
          title='Penicillin MIC (μg/ml, reverse log scale)'),
    alt.Color('Genus:N',
          scale=alt.Scale(
            domain=['Salmonella', 'Staphylococcus', 'Streptococcus', 'Other'],
            range=['rgb(76,120,168)', 'rgb(84,162,75)', 'rgb(228,87,86)', 'rgb(121,112,110)']
          ))
).properties(width=250, height=250)

We now have a much more revealing plot, made possible by customizations to the axes and legend. Take a moment to examine the plot above. Notice any surprising groupings?

The upper-left region has a cluster of red Streptococcus bacteria, but with a grey Other bacteria alongside them. Meanwhile, towards the middle-right we see another red Streptococcus placed far away from its “cousins”. Might we expect bacteria from the same genus (and thus presumably more genetically similar) to be grouped closer together?

As it so happens, the underlying dataset actually contains errors. The dataset reflects the species designations used in the early 1950s. However, the scientific consensus has since been overturned. That gray point in the upper-left? It’s now considered a Streptococcus! That red point towards the middle-right? It’s no longer considered a Streptococcus!

Of course, on its own, this dataset doesn’t fully justify these reclassifications. Nevertheless, the data contain valuable biological clues that went overlooked for decades! Visualization, when used by an appropriately skilled and inquisitive viewer, can be a powerful tool for discovery.

This example also reinforces an important lesson: always be skeptical of your data!

4.3.4. Color by Antibiotic Response#

We might also use the color channel to encode quantitative values. Though keep in mind that typically color is not as effective for conveying quantities as position or size encodings!

Here is a basic heatmap of penicillin MIC values for each bacteria. We’ll use a rect mark and sort the bacteria by descending MIC values (from most to least resistant):

alt.Chart(antibiotics).mark_rect().encode(
    alt.Y('Bacteria:N',
      sort=alt.EncodingSortField(field='Penicillin', op='max', order='descending')
    ),
    alt.Color('Penicillin:Q')
)

We can further improve this chart by combining features we’ve seen thus far: a log-transformed scale, a change of axis orientation, a custom color scheme (plasma), tick count adjustment, and custom title text. We’ll also exercise configuration options to adjust the axis title placement and legend title alignment.

alt.Chart(antibiotics).mark_rect().encode(
    alt.Y('Bacteria:N',
      sort=alt.EncodingSortField(field='Penicillin', op='max', order='descending'),
      axis=alt.Axis(
        orient='right',     # orient axis on right side of chart
        titleX=7,           # set x-position to 7 pixels right of chart
        titleY=-2,          # set y-position to 2 pixels above chart
        titleAlign='left',  # use left-aligned text
        titleAngle=0        # undo default title rotation
      )
    ),
    alt.Color('Penicillin:Q',
      scale=alt.Scale(type='log', scheme='plasma', nice=True),
      legend=alt.Legend(titleOrient='right', tickCount=5),
      title='Penicillin MIC (μg/ml)'
    )
)

Alternatively, we can remove the axis title altogether, and use the top-level title property to add a title for the entire chart:

alt.Chart(antibiotics, title='Penicillin Resistance of Bacterial Strains').mark_rect().encode(
    alt.Y('Bacteria:N',
      sort=alt.EncodingSortField(field='Penicillin', op='max', order='descending'),
      axis=alt.Axis(orient='right', title=None)
    ),
    alt.Color('Penicillin:Q',
      scale=alt.Scale(type='log', scheme='plasma', nice=True),
      legend=alt.Legend(titleOrient='right', tickCount=5),
      title='Penicillin MIC (μg/ml)'
    )
).configure_title(
  anchor='start', # anchor and left-align title
  offset=5        # set title offset from chart
)

4.4. Summary#

Integrating what we’ve learned across the notebooks so far about encodings, data transforms, and customization, you should now be prepared to make a wide variety of statistical graphics. Now you can put Altair into everyday use for exploring and communicating data!

Interested in learning more about this topic?