HoloViewsの基本#
HoloViewsは他の描画ライブラリに基づいた可視化およびデータ分析拡張ライブラリです。データに可視化タイプや次元などの注釈情報を追加することでデータの可視化を実現します。具体的なチャートのレンダリングはmatplotlibやBokehなどの描画ライブラリによって行われます。HoloViewsのドキュメントは非常に充実しており、ドキュメントサイトでは各種チャートのサンプルコードを見つけることができます。このセクションでは、主にさまざまな例を通じてHoloViewsの基本概念を紹介します。
Jupyter NotebookでHoloviewsを使用する場合、まずextension()
を呼び出してチャートをレンダリングする描画ライブラリを選択する必要があります。例えば、Bokeh描画ライブラリを使用する場合、まず以下を実行します:
hv.extension('bokeh')
この章では、HoloViewsを使用してデータ可視化アプリケーションを作成する方法を紹介します。描画関連の内容に加えて、この章ではParamとPanelライブラリの使用方法も紹介します。これら2つのライブラリをHoloViewsの描画機能と組み合わせることで、インタラクティブな可視化アプリケーションを簡単に作成できます。
import pprint
import holoviews as hv
from holoviews import streams
import numpy as np
import pandas as pd
from holoviews import opts
import panel as pn
hv.extension("bokeh", inline=False, logo=False)
pn.extension()
Elementオブジェクト#
HoloViewsでは、Element
の派生クラスを使用してデータに注釈を付けます。次はElement
の継承ツリーを示しています。例えば、曲線を表すCurve
クラスはChart
から継承し、Chart
はDataset
とElement2D
から継承します。
from helper.python import print_subclasses
text = print_subclasses(hv.Element, return_str=True)
pn.widgets.TextAreaInput(
value=text, width=500, height=300, styles={"font-family": "monospace"}
)
Element
オブジェクトは、複数のデータに注釈を付けることをサポートしています。例えば、以下のプログラムは4種類のデータ型(タプル型、DataFrame型、ndarray配列型、辞書型)を作成し、Curve
クラスでそれぞれのデータをラップします。区別しやすいように、各Curve
オブジェクトのlabel
属性に異なる値を設定します。最後に、各オブジェクトのdata
属性の型を確認します。c1
とc2
はDataFrame
オブジェクトでデータを保存し、c1
を作成する際に、タプルデータを自動的にDataFrame
データに変換します。c3
のデータ型はndarray
で、c4
のデータ型はOrderedDict
です。
x = np.linspace(0, 4 * np.pi, 200)
y = np.sin(x)
tuple_data = (x, y)
df_data = pd.DataFrame(dict(x=x, y=y))
arr_data = np.c_[x, y]
dict_data = dict(x=x, y=y)
c1 = hv.Curve(tuple_data, label="c1")
c2 = hv.Curve(df_data, label="c2")
c3 = hv.Curve(arr_data, label="c3")
c4 = hv.Curve(dict_data, label="c4")
curves = [c1, c2, c3, c4]
print(" ".join(f"{c.label}: {type(c.data).__name__}" for c in curves))
c1: DataFrame c2: DataFrame c3: ndarray c4: OrderedDict
異なるタイプのデータは異なるインターフェースで管理されます。以下に、各Curve
オブジェクトのinterface
属性を確認します。
print(" ".join(f"{c.label}: {c.interface.__name__}" for c in curves))
c1: PandasInterface c2: PandasInterface c3: ArrayInterface c4: DictInterface
c3
は配列を使用してデータを保存していますが、dframe()
メソッドを呼び出すことで、内部のデータをDataFrame
オブジェクトに変換できます。
print(c3.dframe().head())
x y
0 0.000000 0.000000
1 0.063148 0.063106
2 0.126295 0.125960
3 0.189443 0.188312
4 0.252590 0.249913
HoloViewsのすべてのクラスはparam.Parameterized
から継承しているため、.param.values()
を使用してオブジェクトのすべてのParameter属性を確認できます。kdims
、vdims
、group
、label
の4つの属性は重要で、後の章で詳しく説明します。
c4.param.values()
{'cdims': {},
'datatype': ['dataframe',
'dictionary',
'grid',
'xarray',
'multitabular',
'spatialpandas',
'dask_spatialpandas',
'dask',
'cuDF',
'array',
'ibis'],
'extents': (None, None, None, None),
'group': 'Curve',
'kdims': [Dimension('x')],
'label': 'c4',
'name': 'Curve00272',
'vdims': [Dimension('y')]}
Tip
Notebookでは、セル内のコードの最後の行の演算結果がHoloViewsの表示可能なオブジェクトである場合、指定された描画ライブラリを使用してチャートとして表示されます。また、IPythonのdisplay()
を使用して表示することもできます。例えば:
from IPython.display import display
display(c1)
Dimensionオブジェクト#
Holoviewsでは、Dimension
オブジェクトを使用して次元関連の情報を保存します。次のテーブルはDimension
オブジェクトのすべての属性をリストしています。
属性名 |
タイプ |
デフォルト値 |
説明 |
---|---|---|---|
name |
String |
‘Dimension’ |
次元の名前。有効なPython識別子を使用することを推奨します |
label |
String |
None |
次元の説明ラベル。表示用で、LaTeXを使用して数式を表示できます |
cyclic |
Boolean |
False |
値の範囲が周期的かどうか。例えば角度 |
value_format |
Callable |
None |
値のフォーマット関数 |
range |
Tuple |
(None, None) |
値の範囲 |
soft_range |
Tuple |
(None, None) |
参照値の範囲 |
type |
Parameter |
None |
次元値のタイプ |
default |
Parameter |
None |
デフォルト値 |
step |
Number |
None |
ステップ。スライダコントロールのステップを設定するために使用されます |
unit |
String |
None |
単位 |
values |
List |
[] |
候補値リスト |
Element
オブジェクトでは、kdims
属性を使用してキー次元リストを保存し、vdims
属性を使用して値次元リストを保存します。キー次元と値次元はどちらもDimension
オブジェクトです。異なるタイプのElement
オブジェクトは、これらの次元に対応するデータを異なる方法で表示します。例えば、以下はCurve
オブジェクトのデフォルトのキー次元と値次元を示しています。キー次元”x”は曲線上の各点のX軸座標に対応し、値次元”y”は曲線上の各点のY軸座標に対応します。
print(hv.Curve.kdims, hv.Curve.vdims)
[Dimension('x')] [Dimension('y')]
複数の線分を表すSegments
オブジェクトは4つのキー次元を持ち、それぞれ2つの端点のX-Y軸座標に対応します。デフォルトの値次元はありません。
print(hv.Segments.kdims, hv.Segments.vdims)
[Dimension('x0'), Dimension('y0'), Dimension('x1'), Dimension('y1')] []
データ分布を表す箱ひげ図BoxWhisker
はキー次元を持たず、デフォルトの値次元が1つだけあります。
print(hv.BoxWhisker.kdims, hv.BoxWhisker.vdims)
[] [Dimension('y')]
Element
オブジェクトを作成する際、kdims
とvdims
を使用してそのキー次元と値次元を設定できます。以下に、3つの方法で次元を設定します。c5
は文字列を使用して次元を設定します。これはDimension
オブジェクトのname
とlabel
属性を同時に設定するのと同じです。c6
は2つの要素のタプルを使用して次元を設定します。これらの要素はそれぞれ次元のname
とlabel
属性を設定するために使用されます。c7
は直接Dimension
オブジェクトを使用して次元を設定します。これにより、キーワード引数を使用して次元のすべての属性を設定できます。kdims
とvdims
パラメータにリストでないオブジェクトを渡すと、長さ1のリストを渡すのと同じです。
c5 = hv.Curve(tuple_data, kdims="time", vdims="position", label="c5")
c6 = hv.Curve(tuple_data, kdims=("t", "Time"), vdims=("x", "Position"), label="c6")
c7 = hv.Curve(
tuple_data,
label="c7",
kdims=hv.Dimension("t", label="Time", unit="s"),
vdims=hv.Dimension("x", label="Position", unit="mm"),
)
for c in (c5, c6, c7):
kdim = c.kdims[0]
vdim = c.vdims[0]
print(f"{c.label}: {kdim.name=} {kdim.label=} {kdim.unit=} ")
c5: kdim.name='time' kdim.label='time' kdim.unit=None
c6: kdim.name='t' kdim.label='Time' kdim.unit=None
c7: kdim.name='t' kdim.label='Time' kdim.unit='s'
チャートとして表示する場合、X軸のタイトルはキー次元のlabel
とunit
属性によって決定され、Y軸のタイトルは値次元のlabel
とunit
属性によって決定されます。例えば、前述のc7
のチャートは次のように示されています。
c7.opts(width=600, show_grid=True)
次元情報を変更する必要がある場合、直接次元オブジェクトの属性を設定することもできますが、HoloViewsではredim
を使用することを推奨しています。これは元のElement
オブジェクトの次元情報を変更せず、新しいElement
オブジェクトを返します。直接redim()
を呼び出すことで、次元オブジェクト全体を変更できます。❶例えば、c1
の2つの次元名はx
とy
です。redim()
のキーワード引数を使用してこれらの次元を設定します。パラメータx
に渡されるのは文字列で、HoloViewsはその文字列を名前とする次元オブジェクトを作成します。パラメータy
には直接次元オブジェクトを渡します。
redim
は単純なメソッドではなく、アクセサオブジェクトです。そのメソッドを使用して、指定された次元の異なる属性を設定できます。❷redim.unit()
は指定された次元のunit
属性を変更し、❸redim.range()
は指定された次元のrange
属性を変更します。次元のrange
属性を設定すると、チャートのX軸とY軸の表示範囲はその属性によって決定され、対応するデータの範囲ではなくなります。
c8 = c1.redim(x="time", y=hv.Dimension("position", label="Position")) # ❶
c9 = c5.redim.unit(time="s", position="mm") # ❷
c10 = c7.redim.range(t=(0, 8), x=(-2, 2)) # ❸
print(f"{c7.kdims[0].range = }, {c10.kdims[0].range = }")
c7.kdims[0].range = (None, None), c10.kdims[0].range = (0, 8)
Datasetからチャートへ#
通常、データ可視化を行う際には、データのクリーニングが既に行われているため、前述の方法でElement
オブジェクトを作成し、その次元情報を設定するのは面倒です。このセクションでは、整然データから迅速にチャートを生成する方法を紹介します。
整然データ(Tidy Data)を2次元テーブルで表す場合、各列(各変数)は次元と見なすことができ、各行は1つの観測結果に対応します。変数は独立変数と従属変数に分けられます。独立変数は研究者が操作する変数で、従属変数は測定または記録される変数です。例えば、以下のプログラムは「gapminder_CJU.csv」からデータを読み取り、DataFrameオブジェクトdf
を取得します。そのcountry
列は国、year
列は年、gdp_capita
、lift
、population
、ppp
などの列はそれぞれ1人当たりGDP、平均寿命、1人当たりPPP、人口です。国と年は統計調査の前に決定できる変数で、独立変数です。他の変数は統計または観測の結果で、従属変数です。Holoviewsでは、通常、キー次元(kdims)を使用して独立変数を表し、値次元(vdims)を使用して従属変数を表します。独立変数、従属変数、キー次元、値次元の関係は絶対的なものではありません。例えば、複数の従属変数の関係を研究する場合、Scatter
を使用して散布図を描画することができます。この場合、そのキー次元と値次元を2つの関連する従属変数に設定できます。
import pandas as pd
df = pd.read_csv("data/gapminder_CJU.csv")
df.sample(5).sort_index()
country | year | gdp_capita | life | ppp_capita | population | |
---|---|---|---|---|---|---|
20 | China | 2010 | 4550.0 | 75.8 | 9250.0 | 1370000000 |
22 | China | 2012 | 5330.0 | 76.2 | 11200.0 | 1380000000 |
25 | China | 2015 | 6480.0 | 76.5 | 12900.0 | 1410000000 |
36 | Japan | 1997 | 41900.0 | 80.8 | 25100.0 | 127000000 |
53 | Japan | 2014 | 46500.0 | 83.8 | 39200.0 | 128000000 |
以下に、df
を使用してDataset
データセットを作成し、country列とyear列をキー次元として指定します。残りの列は自動的に値次元になります。Dataset
を表示する際、キー次元は角括弧内に、値次元は丸括弧内に表示されます。
ds = hv.Dataset(df, kdims=["country", "year"])
ds
:Dataset [country,year] (gdp_capita,life,ppp_capita,population)
以下に、Dataset.to.curve()
を呼び出して、データセットds
をCurve
オブジェクトのコンテナに変換します。パラメータkdims
とvdims
を使用して、Curve
オブジェクトのキー次元と値次元を指定します。ds
のキー次元country
が指定されていないため、デフォルトでそれをグループ次元として使用します。グループ化操作の結果はHoloMap
オブジェクトで、グループ次元country
はそのキー次元になります。print()
を使用してその内部構造を確認できます。出力から、hm
はHoloMap
オブジェクトで、そのキー次元はcountryです。この次元の各値はCurve
オブジェクトに対応し、そのキー次元はyear、値次元はppp_capitaです。
hm = ds.to.curve(kdims="year", vdims="ppp_capita")
print(hm)
:HoloMap [country]
:Curve [year] (ppp_capita)
HoloMap
はUniformNdMapping
から継承します。以下の図はUniformNdMapping
のすべての派生クラスを示しています。これらのクラスは複数の要素を含むコンテナクラスで、異なる方法で複数の要素を表示しますが、内部のデータ保存方法は同じです。
print_subclasses(hv.core.UniformNdMapping)
└──UniformNdMapping
├──NdLayout
├──NdOverlay
├──HoloMap
│ └──DynamicMap
└──GridSpace
└──GridMatrix
以下に、各オブジェクトのkdims
とvdims
属性を確認します。HoloMap
オブジェクトは添字演算をサポートしているため、hm['China']
は次元country
の値が’China’の要素を取得します。
print(f"{hm.kdims = }")
print(f'{hm["China"].kdims = }')
print(f'{hm["China"].vdims = }')
hm.kdims = [Dimension('country')]
hm["China"].kdims = [Dimension('year')]
hm["China"].vdims = [Dimension('ppp_capita')]
以下に、各オブジェクトのdata
属性を確認します。これは実際のデータを保存します。HoloMap
は順序付き辞書を使用してデータを保存し、Curve
はDataFrame
オブジェクトを使用してデータを保存します。
print("HoloMap data:")
pprint.pp(hm.data)
print()
print("Curve data:")
print(hm["China"].data.head())
HoloMap data:
{('China',): :Curve [year] (ppp_capita),
('Japan',): :Curve [year] (ppp_capita),
('United States',): :Curve [year] (ppp_capita)}
Curve data:
country year gdp_capita life ppp_capita population
0 China 1990 729.0 68.7 982.0 1180000000
1 China 1991 786.0 68.8 1090.0 1190000000
2 China 1992 887.0 69.1 1260.0 1210000000
3 China 1993 998.0 69.4 1460.0 1220000000
4 China 1994 1120.0 69.7 1660.0 1230000000
hm
を直接表示すると、次のグラフ(左)に示すようなドロップダウンボックスを使用して、次元countryの値を選択できます。ドロップダウンボックスの値が変更されると、チャート内の曲線が自動的に更新されます。
hm.opts(width=400)
複数の曲線を同時に表示する必要がある場合、overlay()
メソッドを呼び出してNdOverlay
オブジェクトに変換できます。NdOverlay
オブジェクトは複数の要素を重ねて表示し、キー次元の値は凡例として表示されます。結果は次のグラフ(中)に示されています。
year_ppp_plot = hm.overlay()
print(year_ppp_plot)
year_ppp_plot
:NdOverlay [country]
:Curve [year] (ppp_capita)
以下の例では、国ごとにグループ化し、各要素はScatter
オブジェクトで、そのキー次元はppp_capita、値次元はlifeとyearです。NdOverlay
を使用して複数の要素を重ねて表示します。Scatter
オブジェクトはキー次元をX軸座標として使用し、最初の値次元(life)を縦座標として使用します。出力チャートにマウスホバーツールhover
を追加しているため、マウスを各散布点上に置くと、表示に使用されていない値次元year
の値を見ることができます。その結果は次のグラフ(右)に示されています。
gdp_life_plot = ds.to.scatter(
kdims=["ppp_capita"], vdims=["life", "year"], groupby=["country"]
).overlay()
gdp_life_plot.opts(width=400)
さらに、HoloMap.layout()
を使用してHoloMap
オブジェクトをNdLayout
オブジェクトに変換できます。これにより、複数のチャートを並べて表示することができます。次のグラフに示されています。
hm.layout()
配置と重ね合わせコンテナ#
NdLayout
とNdOverlay
は、複数の要素を並列または重ねて表示することができます。これらはNd
で始まり、複数の次元を持つコンテナであることを示しています。HoloViewsはまた、次元情報を持たないLayout
とOverlay
も提供しており、これらのコンテナを使用して、次元に関連のない複数の要素を並列または重ねて表示することができます。これら2つのコンテナを簡単に作成するために、HoloViewsは+
と*
の2つの演算子をオーバーロードしています。+
を使用して複数の要素を接続すると、Layout
コンテナが作成され、これらの要素が並列表示されます。*
を使用して複数の要素を接続すると、Overlay
コンテナが作成され、これらの要素が重ねて表示されます。例えば、以下のコードは、同じチャート内に1つの曲線と1組の散布点を重ねて表示します。
hv.Curve((x1, y1)) * hv.Scatter((x2, y2))
以下のコードは、これらを並列表示します:
hv.Curve((x1, y1)) + hv.Scatter((x2, y2))
以下では、複数の要素を並列および重ねて表示する方法を、より複雑な例で紹介します。その結果は次のグラフに示されています。全体のチャートは2つの並列サブチャートで構成され、各サブチャートには3つの曲線とその局所的な極値が表示され、テキストで極値の大きさが表示されます。
以下のorth_poly()
は、サブチャート内の1つの曲線とその極値情報を作成する関数です。プログラムでは、まずパラメータに基づいて曲線上の各点のX軸とY軸の座標値を計算します。そしてscipy.signal.find_peaks()
を使用して局所的な最大値と最小値のインデックスを見つけます。❶次に、曲線を表すCurve
オブジェクトを作成します。ここでは、このオブジェクトのgroup
とlabel
属性を設定しています。これらの属性は、同じコンテナ内で異なる要素を区別するために使用できます。曲線のlabel
属性は凡例に表示されます。❷極値座標を使用して散布点を表すScatter
オブジェクトを作成し、ここで異なるgroup
属性を設定します。❸最大値と最小値の数値を表示するために2つのLabels
オブジェクトを作成します。Labels
には、テキストの座標を表す2つのキー次元と、テキストの内容を表す1つの値次元があります。これら2つのLabels
オブジェクトを区別するために、異なるgroup
属性を設定します。❹最後に、乗算記号を使用してこれら4つの要素を重ねて表示します。
import numpy as np
from scipy import special
from scipy import signal
def orth_poly(f, n, x0=-1, x1=1):
x = np.linspace(x0, x1, 100)
y = f(n, x)
index_hi, _ = signal.find_peaks(y)
index_lo, _ = signal.find_peaks(-y)
name = f.__name__.split("_")[-1]
label = f"{name} {n}"
curve = hv.Curve((x, y), group="curve", label=label) # ❶
index = np.r_[index_hi, index_lo]
xp = x[index]
yp = y[index]
peak = hv.Scatter((xp, yp), group="peak", label=label) # ❷
xh = x[index_hi]
yh = y[index_hi]
texth = ["{:4.2f}".format(v) for v in yh]
text_hi = hv.Labels((xh, yh, texth), group="text_hi", label=label) # ❸
xl = x[index_lo]
yl = y[index_lo]
textl = ["{:4.2f}".format(v) for v in yl]
text_lo = hv.Labels((xl, yl, textl), group="text_lo", label=label) # ❸
return curve * peak * text_hi * text_lo # ❹
以下では、orth_poly()
を使用して4次のチェビシェフ多項式のチャートcurve1
を作成します。これはOverlay
コンテナで、順序付き辞書OrderedDict
を使用してその中の4つの要素を保存します。各要素に対応するキー値は、要素のgroup
属性とlabel
属性を使用して作成されます。キー値のテキストは大文字で始まることに注意してください。
from pprint import pp
curve1 = orth_poly(special.eval_chebyu, 4)
print(curve1)
print()
pp(curve1.data)
:Overlay
.Curve.Chebyu_4 :Curve [x] (y)
.Peak.Chebyu_4 :Scatter [x] (y)
.Text_hi.Chebyu_4 :Labels [x,y] (Label)
.Text_lo.Chebyu_4 :Labels [x,y] (Label)
{('Curve', 'Chebyu_4'): :Curve [x] (y),
('Peak', 'Chebyu_4'): :Scatter [x] (y),
('Text_hi', 'Chebyu_4'): :Labels [x,y] (Label),
('Text_lo', 'Chebyu_4'): :Labels [x,y] (Label)}
属性と同じ方法でその中の要素を取得できます。例えば、1次属性を使用して、すべてのgroup
属性が同じ要素を取得し、新しいOverlay
オブジェクトを取得できます。2次属性を使用すると、その中の1つの要素オブジェクトを取得できます。
print(curve1.Curve)
print()
print(curve1.Curve.Chebyu_4)
:Overlay
.Chebyu_4 :Curve [x] (y)
:Curve [x] (y)
要素の数が不確定な場合、直接Overlay
を使用する方が便利です。以下では、orth_poly()
をループで呼び出し、3つの異なる次数のチェビシェフ多項式のOverlay
コンテナを作成し、hv.Overlay()
を使用してこれらのコンテナを重ね合わせます。最終的に得られるOverlay
オブジェクトchebyc_plot
には12の要素が含まれています。1次属性chebyc_plot.Peak
を使用して、新しいOverlay
オブジェクトを取得できます。その中の3つの要素のgroup
属性はすべてpeak
です。
chebyc_plot = hv.Overlay([orth_poly(special.eval_chebyu, n) for n in [2, 3, 4]])
print(chebyc_plot)
print()
print(chebyc_plot.Peak)
:Overlay
.Curve.Chebyu_2 :Curve [x] (y)
.Peak.Chebyu_2 :Scatter [x] (y)
.Text_hi.Chebyu_2 :Labels [x,y] (Label)
.Text_lo.Chebyu_2 :Labels [x,y] (Label)
.Curve.Chebyu_3 :Curve [x] (y)
.Peak.Chebyu_3 :Scatter [x] (y)
.Text_hi.Chebyu_3 :Labels [x,y] (Label)
.Text_lo.Chebyu_3 :Labels [x,y] (Label)
.Curve.Chebyu_4 :Curve [x] (y)
.Peak.Chebyu_4 :Scatter [x] (y)
.Text_hi.Chebyu_4 :Labels [x,y] (Label)
.Text_lo.Chebyu_4 :Labels [x,y] (Label)
:Overlay
.Chebyu_2 :Scatter [x] (y)
.Chebyu_3 :Scatter [x] (y)
.Chebyu_4 :Scatter [x] (y)
上記と同じ方法を使用して、3つの異なる次数のルジャンドル多項式のOverlay
コンテナlegendre_plot
を作成し、加算記号を使用してこれら2つのOverlay
コンテナを並列表示します。得られる結果はLayout
オブジェクトです。その中の2つのOverlay
オブジェクトにgroup
とlabel
属性を指定していないため、Holoviewsは自動的に対応するキー値を指定します。
legendre_plot = hv.Overlay([orth_poly(special.eval_legendre, n) for n in [2, 3, 4]])
plots = chebyc_plot + legendre_plot
print(plots)
:Layout
.Overlay.I :Overlay
.Curve.Chebyu_2 :Curve [x] (y)
.Peak.Chebyu_2 :Scatter [x] (y)
.Text_hi.Chebyu_2 :Labels [x,y] (Label)
.Text_lo.Chebyu_2 :Labels [x,y] (Label)
.Curve.Chebyu_3 :Curve [x] (y)
.Peak.Chebyu_3 :Scatter [x] (y)
.Text_hi.Chebyu_3 :Labels [x,y] (Label)
.Text_lo.Chebyu_3 :Labels [x,y] (Label)
.Curve.Chebyu_4 :Curve [x] (y)
.Peak.Chebyu_4 :Scatter [x] (y)
.Text_hi.Chebyu_4 :Labels [x,y] (Label)
.Text_lo.Chebyu_4 :Labels [x,y] (Label)
.Overlay.II :Overlay
.Curve.Legendre_2 :Curve [x] (y)
.Peak.Legendre_2 :Scatter [x] (y)
.Text_hi.Legendre_2 :Labels [x,y] (Label)
.Text_lo.Legendre_2 :Labels [x,y] (Label)
.Curve.Legendre_3 :Curve [x] (y)
.Peak.Legendre_3 :Scatter [x] (y)
.Text_hi.Legendre_3 :Labels [x,y] (Label)
.Text_lo.Legendre_3 :Labels [x,y] (Label)
.Curve.Legendre_4 :Curve [x] (y)
.Peak.Legendre_4 :Scatter [x] (y)
.Text_hi.Legendre_4 :Labels [x,y] (Label)
.Text_lo.Legendre_4 :Labels [x,y] (Label)
多段属性を使用して、その中の任意の要素を取得できます。例えば:
el = plots.Overlay.II.Peak.Legendre_3
print(f"{el}, {el.group=}, {el.label=}")
:Scatter [x] (y), el.group='peak', el.label='legendre 3'
最後に、plots
を表示する際に、opts()
を使用してその中の各要素の表示オプションを設定します。特定の要素に属性を設定する必要がある場合、最初のパラメータを使用してそのオプションに対応するgroup
とlabel
属性を指定できます。ここでは、group
属性がtext_hi
のLabels
オブジェクトとtext_lo
のLabels
オブジェクトに異なるオプションを設定しています。
plots.opts(
opts.Layout(shared_axes=False),
opts.Scatter(size=10, marker="x"),
opts.Curve(alpha=0.7),
opts.Overlay(show_grid=True, width=400, legend_cols=3),
opts.Labels(text_font_size="9pt"),
opts.Labels("text_hi", text_baseline="bottom"),
opts.Labels("text_lo", text_baseline="top"),
)
コンテナのネスト#
UniformNdMapping
の派生クラスは多重にネストでき、複雑なグラフを描画します。以下に、コンテナタイプのネストを紹介するための例を示します。以下のrun_sim()
は、scipy.integrate.solve_ivp()
を使用して質量、バネ、ダンパーシステムの微分方程式を解き、Curve曲線要素を返します。
from scipy import integrate
import numpy as np
def spring_sys(t, y, m, b, k, F):
x, v = y
dx = v
dv = (F - k * x - b * v) / m
return dx, dv
def spring_sim(x0, v0, m, b, k, F, tend, n):
t = np.linspace(0, tend, n)
res = integrate.solve_ivp(spring_sys, (0, tend), (x0, v0), t_eval=t, args=(m, b, k, F))
return res.t, res.y[0], res.y[1]
dim_time = hv.Dimension('t', label='time', unit='s')
dim_pos = vdims=hv.Dimension('x', label='x', unit='m')
def run_sim(x0, v0, m, b, k, F, tend, n):
t, x, _ = spring_sim(x0, v0, m, b, k, F, tend, n)
return hv.Curve((t, x), kdims=dim_time, vdims=dim_pos)
以下では、numpy.mgrid[]
を使用して、m
、b
、k
、F
の4つのパラメータの4次元グリッドを迅速に作成します。そして、各パラメータの組み合わせに対してrun_sim()
を呼び出してHoloMap
オブジェクトhm
を作成します。出力からわかるように、これは4つのキーディメンションを持ち、各キーディメンションの値はCurve要素に対応し、Curve要素のキーディメンションはt
、値ディメンションはx
です。
from itertools import product
hm = hv.HoloMap(kdims=[
hv.Dimension('m', label='mass', unit='kg'),
hv.Dimension('b', label='dumper', unit=''),
hv.Dimension('k', label='spring', unit='N/m'),
hv.Dimension('F', label='force', unit='N'),
])
for args in np.mgrid[0.5:2:3j, 0.1:1.0:3j, 0.5:3:3j, 0:1:3j].reshape(4, -1).T:
hm[tuple(args)] = run_sim(1.0, 0.0, *args, tend=10, n=50)
print(hm)
:HoloMap [m,b,k,F]
:Curve [t] (x)
以下では、コンテナオブジェクトのoverlay()
とlayout()
メソッドをチェーンしてNdLayout
コンテナオブジェクトを取得し、そのcols()
を呼び出してコンテナオブジェクトの列数を設定し、最後にopts()
を呼び出して各オブジェクトの表示オプションを設定します。出力からわかるように、plots
はNdLayout
コンテナであり、m
とb
の2つのキーディメンションを持ちます。その各要素はHoloMap
コンテナであり、F
の1つのキーディメンションを持ちます。その各要素はNdOverlay
コンテナであり、k
の1つのキーディメンションを持ちます。
options = [
opts.NdOverlay(legend_position='top_left', legend_cols=3),
opts.Curve(show_grid=True, width=300)
]
plots = hm.overlay('k').layout(['m', 'b']).cols(3).opts(*options)
print(plots)
:NdLayout [m,b]
:HoloMap [F]
:NdOverlay [k]
:Curve [t] (x)
plots
plots
の表示結果からわかるように、Ndlayout
はm
とb
の2つのディメンションを並列表示し、NdOverlay
はディメンションk
を1つのサブプロットに重ねて表示し、HoloMap
はディメンションF
をスライダーコントロールとして表示します。このコントロールを使用して、ディメンションF
に対応する値を変更できます。
プロットオプション#
HoloViewsでは、要素とコンテナオブジェクトはデータと次元間の関係を記述するためにのみ使用され、色や線種などのプロット関連の属性は持っていません。しかし、実際のプロットでは、これらのプロット関連のオプションが不可欠です。異なる要素のプロットオプションは異なるため、HoloViewsはこれらのオプションを簡単に作成するためにopts
オブジェクトを提供しています。opts
オブジェクトのメソッド名は要素のクラス名と同じで、キーワード引数を使用して各オプションの値を設定します。例えば、以下はCurve
とScatter
に適用されるオプションを作成します。width
はチャートの幅を設定し、show_grid
は座標グリッドを表示するかどうかを設定し、line_width
は線幅を設定し、color
は線の色を設定します。オプションを作成する際に、対応するgroup
属性とlabel
属性を指定することもできます。例えば、scatter_opt2
はgroup
属性がLarge
のオブジェクトにのみ有効です。異なるオブジェクトは異なるオプションをサポートしています。hv.help()
関数を使用して、各要素がサポートするオプションのリストとそのヘルプ説明を参照できます。例えば、hv.help(hv.Curve)
です。
from holoviews import opts
curve_opt = opts.Curve(width=600, show_grid=True, line_width=4, color="green")
scatter_opt = opts.Scatter(size=16, marker="x", color="red")
scatter_opt2 = opts.Scatter("Large", size=24, color="blue")
要素とコンテナオブジェクトのopts()
とoptions()
メソッドを使用して、そのオブジェクトにオプションを指定できます。これらは複数のオプションオブジェクトを受け取ることができます。obj.opts()
はobj
のオプションを設定し、obj.options()
はobj
を複製し、複製オブジェクトのオプションを設定して、複製オブジェクトを返します。opts.info()
はオブジェクトに関連するオプションを表示するために使用できます。show_defaults
パラメータがTrue
の場合、各オプションのデフォルトオプションが表示されます。opts.clear()
を使用して、オブジェクトに対応するすべてのオプションを削除できます。オプション関連の情報はオブジェクト内に保存されていません。HoloViewsは、すべてのオプションとオブジェクト間の関係を保存するためにグローバルオブジェクトを使用しています。
以下では、まずOverlay
オブジェクトを作成します。次に、上記で作成した2つのオプションオブジェクトをopts()
メソッドに渡します。
x = np.linspace(0, 4 * np.pi, 50)
y = np.sin(x)
data = x, y
data2 = x[::5], y[::5]
overlay = hv.Curve(data) * hv.Scatter(data) * hv.Scatter(data2, group="Large")
overlay.opts(curve_opt, scatter_opt, scatter_opt2)
overlay.opts.info()
print()
overlay.opts.clear()
overlay.opts.info(show_defaults=True)
:Overlay
.Curve.I :Curve [x] (y)
| Options(color='green', line_width=4, show_grid=True, width=600)
.Scatter.I :Scatter [x] (y)
| Options(color='red', marker='x', size=16)
.Large.I :Scatter [x] (y)
| Options(color='blue', marker='x', size=24)
:Overlay
| Options(axiswise=False, click_policy='mute', framewise=False)
.Curve.I :Curve [x] (y)
| Options(axiswise=False, color=Cycle(), framewise=False, line_width=2, muted_alpha=0.2)
.Scatter.I :Scatter [x] (y)
| Options(axiswise=False, cmap='kbc_r', color=Cycle(), framewise=False, muted_alpha=0.2,
| size=np.float64(2.449489742783178))
.Large.I :Scatter [x] (y)
| Options(axiswise=False, cmap='kbc_r', color=Cycle(), framewise=False, muted_alpha=0.2,
| size=np.float64(2.449489742783178))
デフォルトオプションはopts.defaults()
を使用して設定できます。例えば、この章のNotebookの冒頭部分で、以下のプログラムを使用してScatter
、Curve
、およびNdOverlay
のデフォルトオプションを設定しています。
opts.defaults(
opts.Scatter(tools=['hover'], width=500, show_grid=True),
opts.Curve(tools=['hover'], width=500, show_grid=True),
opts.NdOverlay(legend_position='left'),
)
opts()
を使用して、データの特定の次元に基づいてプロット属性を決定することができます。例えば、Points
オブジェクトは2次元の散布点を表し、デフォルトではその2つのキー次元が散布点の座標を決定します。opts()
を使用して、散布点のサイズ、色、形状などのプロット属性をデータの特定の次元に関連付けることができます。以下の例では、散布点を使用してlife
、ppp_capita
、year
、およびpopulation
の4つの次元間の関係を表示します。ここで、life
とppp_capita
は点のX-Y軸座標であり、year
は点の色を決定し、population
は点のサイズを決定します。次元を使用してプロット属性を計算する場合、dim
オブジェクトを使用してその次元をラップする必要があります。dim
オブジェクトはすべての演算子といくつかの一般的なメソッドをサポートしています。例えば、以下では、population
次元を使用して散布点のサイズを計算する際に、まずそのnorm()
メソッドを呼び出してデータを正規化し、0〜1の範囲にマッピングし、その平方根を取り、拡大係数10を乗算し、小さな点を表示するために定数を加えます。その表示結果は次のグラフに示されています。
from holoviews import dim
points = ds.to.points(
kdims=["life", "ppp_capita"], vdims=["year", "population"], groupby=[]
).opts(
opts.Points(
color=dim("year"),
size=dim("population").norm() ** 0.5 * 10 + 2,
colorbar=True,
cmap="Viridis",
width=500,
show_grid=True,
)
)
points
dim
オブジェクトで構成される式は、計算プロセス全体を保存するだけで、実際の計算は行われません。指定されたデータに適用して結果を計算する必要があります。points
オブジェクトを表示する際に、dim
オブジェクトを含む式が計算されます。これはdim
式のapply()
メソッドを呼び出すことと同等です。
size_expr = dim("population").norm() ** 0.5 * 10 + 2
size = size_expr.apply(ds)
print(size_expr)
print(f"{size.min() = }, {size.max() = }")
(((dim('population').norm())**0.5)*10)+2
size.min() = np.float64(2.0), size.max() = np.float64(12.0)