Dash㉟(Dropdownコンポーネント/複数選択)

Dropdownのオプション

Dropdownコンポーネントには下記のようなオプションがあります。

オプション データ型 内容
clearable ブール型 Trueの場合、ドロップダウンの右側に全ての要素を消去するための×印を表示する。デフォルト値はTrue。
multi ブール型 Trueの場合、複数選択が有効になる。デフォルト値はFalse。
options 辞書型 選択肢を次の3つのキーを用いて作成する。
label 選択肢のラベル(文字列または数値:必須)
value 選択肢の値(文字列または数値:必須)
disable Trueが渡された選択を無効にする(ブール型:任意)
searchable ブール型 Trueが渡された場合、選択肢の検索機能が利用できる。デフォルト値はTrue。

Dropdownコンポーネント(複数選択)

Dropdownのオプション multiTrueを設定し複数選択を可能にします。(18行目)

初期値には、選択肢を複数指定するためにリスト型を設定しています。(17行目)

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
dcc.Dropdown(id='dropdown1',
options=[
{'label':'愛媛', 'value':'ehime'},
{'label':'香川', 'value':'kagawa'},
{'label':'徳島', 'value':'tokushima'},
{'label':'高知', 'value':'kouchi'}
],
value=['ehime', 'kouchi'], # 選択初期値を複数指定
multi=True # 複数選択可能に
),
html.Div(id='div1')
],
style={'width':'80%', 'margin':'auto'}
)

# コールバック
@app.callback(
Output('div1', 'children'),
Input('dropdown1', 'value')
)
def change_img(dropdown1):
return '選択しているのは"{}"です'.format(dropdown1)

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

[ブラウザで表示]

ドロップダウンから値を選択すると、選択済みの項目として追加されます。

また選択済みの項目にそれぞれ表示されている×印をクリックすると、その項目の選択が解除されます。

Dash㉞(Dropdownコンポーネント)

Dropdownコンポーネント

Dropdownコンポーネントは、一覧から値を選択できるコンポーネントです。

選択の方法は単一選択(デフォルト)または複数選択を設定できます。

今回はドロップダウンから値を選択すると、その選択値をドロップダウンの下に表示するアプリケーションを作成します。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
dcc.Dropdown(id='dropdown1',
options=[
{'label':'愛媛', 'value':'ehime'},
{'label':'香川', 'value':'kagawa'},
{'label':'徳島', 'value':'tokushima'},
{'label':'高知', 'value':'kouchi'}
],
value='ehime'
),
html.Div(id='div1')
],
style={'width':'80%', 'margin':'auto'}
)

# コールバック
@app.callback(
Output('div1', 'children'),
Input('dropdown1', 'value')
)
def change_img(dropdown1):
return '選択しているのは"{}"です'.format(dropdown1)

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

Dropdownコンポーネントは、以下のように設定します。

  1. 引数 optionsに 選択肢の辞書型データのリストを設定
  2. 引数 valueに ドロップダウンの初期値を設定
  3. 引数 styleに ドロップダウンのスタイルを設定

[ブラウザで表示]

ドロップダウンから値を選択するとその value値 が、ドロップダウンの下に表示されることを確認できます。

Dash㉝(標準コンポーネント/ボタン)

ボタン

ボタンをクリックすると、イメージが切り替わるアプリケーションを作成します。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from datetime import datetime

import dash
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
html.Div(
[
html.H1(),
html.Img(id='img1', style={'height':300})
]
),
html.Button(id='btn1', children='1'),
html.Button(id='btn2', children='2'),
html.Button(id='btn3', children='3')
],
style={'width':'80%', 'margin':'auto'}
)

# コールバック
@app.callback(
Output('img1', 'src'),
Input('btn1', 'n_clicks'),
Input('btn2', 'n_clicks'),
Input('btn3', 'n_clicks')
)
def change_img(n_clicks1, n_clicks2, n_clicks3):
selected_id = dash.callback_context.triggered[0]['prop_id'].split('.')[0]
print(selected_id)
if selected_id == 'btn1':
return 'assets/cat.png'
elif selected_id == 'btn2':
return 'assets/dog.png'
else:
return 'assets/cherry.png'

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

コールバックで、ImgコンポーネントButtonコンポーネントを使って、ボタンをクリックするとイメージを切り替えます。

dash.callback_contextは、コールバック関数の引数のIDや属性、値をもち、コールバック関数の中からのみ利用することができます。

dash.callback_context.triggeredプロパティを参照して、押されたボタンのIDを取得し、ボタンのIDに対応した画像のパスを返します。(32~39行目)

[ブラウザで表示]

ボタンを押すと、イメージを切り替えることができます。

Dash㉜(標準コンポーネント/文字列)

HTMLコンポーネント

Dashでは、いろいろなコンポーネントが提供されています。

主なコンポーネントを下記のテーブルに表します。

クラス 内容
Div コンテンツ分割
H1~H6 見出し
P テキストの段落
Link リンク
Button ボタン
Img 画像の埋め込み

文字列用コンポーネント

H1~H6Divなどのコンポーネントには、引数 children に値を設定することで表示する文字列を変更することができます。

今回はコンポーネントのクリック回数を数えるn_clicks属性と表示/非表示を切り替えるhidden属性を使って、文字列をクリックするとその文字列コンポーネントを非表示にするアプリケーションを作成してみます。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import json

import dash
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
html.H1('日本'),
html.H2('関東'),
html.P('茨城', n_clicks=0, id='p1'),
html.P('千葉', n_clicks=0, id='p2'),
html.P('東京', n_clicks=0, id='p3'),
html.P('埼玉', n_clicks=0, id='p4'),
html.P('神奈川', n_clicks=0, id='p5')
],
style={'textAlign':'center'}
)

for id in ['p1', 'p2', 'p3', 'p4', 'p5']:
# コールバック
@app.callback(
Output(id, 'hidden'),
Input(id, 'n_clicks')
)
def text_hide(n_clicks):
if n_clicks % 2 == 1:
return True

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

コールバック関数では、コンポーネントをクリックするとhidden属性にTrueが設定され、そのコンポーネントが非表示になるように設定しています。

[ブラウザで表示]

文字列をクリックすると、その文字列が非表示になることが確認できます。

Dash㉛(連鎖コールバック)

連鎖コールバック

連鎖コールバックとは、コールバックの出力を別のコールバックの入力に使用するコールバックです。

コールバックを連鎖させることにより、より複雑な動作を実現することができるようになります。

ドロップダウンでグラフの種類を選択し、ラジオボタンでグラフの表示データを変更するアプリケーションを作成します。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import json

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output, State

tips = px.data.tips() # データの読み込み

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
html.H1(id='title1'),
html.Div(
[
dcc.Dropdown(
id='dropdown1',
options=[
{'value':'bar', 'label':'棒グラフ'},
{'value':'scatter', 'label':'散布図'}
],
value='bar'
),
html.Div(
[
html.P(id='title2'),
dcc.RadioItems(id='radio1')
]
)
],
style={'width':'35%', 'float':'left'}
),
html.Div(
[dcc.Graph(id='graph1')],
style={'width':'65%', 'height':800, 'display':'inline-block'}
)
]
)

# コールバック1
@app.callback(
Output('radio1', 'options'), # RadioItems(id='radio1')の選択肢
Output('radio1', 'value'), # RadioItems(id='radio1')の値
Output('title1', 'children'), # H1(id='title1')に表示する文字
Output('title2', 'children'), # P(id='title2')に表示する文字
Input('dropdown1', 'value') # Dropdown(id='dropdown1')で選択された値
)
def update_selecter(dropdown1):
if dropdown1 == 'bar':
return (
[
{'value':'total_bill', 'label':'総額'},
{'value':'sex', 'label':'性別'},
{'value':'smoker', 'label':'喫煙/禁煙'},
{'value':'time', 'label':'時間帯(昼/夜)'}
],
'total_bill',
'チップデータ(棒グラフ)',
'棒グラフ選択肢'
)
else:
return (
[
{'value':'total_bill', 'label':'総額'},
{'value':'sex', 'label':'性別'},
{'value':'smoker', 'label':'喫煙/禁煙'},
{'value':'time', 'label':'時間帯(昼/夜)'}
],
'smoker',
'チップデータ(散布図)',
'散布図選択肢'
)

# コールバック2
@app.callback(
Output('graph1', 'figure'), # グラフ
Input('radio1', 'value'), # RadioItems(id='radio1')の選択肢
State('dropdown1', 'value') # Dropdown(id='dropdown1')で選択されている値
)
def update_graph(radio1, dropdown1):
if dropdown1 == 'bar':
return px.bar(
tips,
x='day',
y='total_bill',
color=radio1,
barmode='group',
height=600,
title='チップデータ棒グラフ(要素:{})'.format(radio1)
)
else:
return px.scatter(
tips,
x='total_bill',
y='tip',
color=radio1,
height=600,
title='チップデータ散布図(要素:{})'.format(radio1)
)

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

連鎖コールバックの処理フローは以下の通りです。

  1. Dropdownコンポーネントのvalue属性の変化により、コールバックが呼び出され、RadioItemsコンポーネントの選択肢などを返す。(1つめのコールバック)
  2. RadioItemsコンポーネントのvalue属性の変化をきっかけに、グラフの種類とグラフのデータを反映したfigure(グラフ)を返す。(2つめのコールバック)

[ブラウザで表示]

ドロップダウンでグラフの種類が変更でき、ラジオボタンでグラフのデータが変更できることが確認できます。

Dash㉚(コールバック停止/部分)

コールバック停止(部分)

no_updateを使って、部分的にコールバックの更新を停止してみます。

コールバックのOuputを2つ設定し、値が存在しなければ片方の出力のみコールバック処理を停止します。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import json

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output

gapminder = px.data.gapminder() # データの読み込み
gapminder2007 = gapminder[gapminder['year'] == 2007]

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
html.H1('Gapminder Graph'),
# 散布図
dcc.Graph(
id='graph1',
figure=px.scatter(
gapminder2007, x='gdpPercap', y='lifeExp', hover_name='country'
)
),
# ホバーデータを表示するコンポーネント
html.P(id='hover_text1', style={'fontSize':'40', 'textAlign':'center', 'backgroundColor':'#CCFFFF'}),
html.P(id='hover_text2', style={'fontSize':'40', 'textAlign':'center', 'backgroundColor':'#FFD5EC'})
],
style={'width':'90%', 'fontSize':'40', 'textAlign':'center'}
)


# コールバック
@app.callback(
Output('hover_text1', 'children'), # データ状態に関係なくコールバック実行
Output('hover_text2', 'children'), # Noneであればコールバックを停止
Input('graph1', 'hoverData')
)
def show_text1(text):
if text is None:
return json.dumps(text), dash.no_update
return json.dumps(text), json.dumps(text)

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

コールバック関数ではInputのhoverData属性が値をもたなければ、no_updateクラスを使って部分的にコールバックを停止しています。(40行目)

[ブラウザで表示]

画面をロードした直後は、1つめのPコンポーネントには‘null’が表示されていますが、2つめのPコンポーネントには何も表示されていません。(ホバーデータがないのでコールバック処理が部分的に停止されたため)

グラフ上のデータをマウスオバーすると、両方のPコンポーネントに同じデータが表示されます。(ホバーデータがありコールバック処理が停止されなかったため)

Dash㉙(コールバック停止/全体)

コールバック停止の方法

コールバック特定の条件で停止することができます。

コールバックの停止の方法は下記の2種類があります。

  • 全体停止
    PreventUpdateクラスを使用
  • 部分停止
    no_updateクラスを使用

コールバック停止(全体)

PreventUpdateクラスを使って、hoverData属性が値を持たない場合コールバックを停止してみます。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import json

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output

gapminder = px.data.gapminder() # データの読み込み
gapminder2007 = gapminder[gapminder['year'] == 2007]

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
html.H1('Gapminder Graph'),
# 散布図
dcc.Graph(
id='graph1',
figure=px.scatter(
gapminder2007, x='gdpPercap', y='lifeExp', hover_name='country'
)
),
# ホバーデータを表示するコンポーネント
html.P(id='hover_text1', style={'fontSize':'40', 'textAlign':'center', 'backgroundColor':'#CCFFFF'}),
html.P(id='hover_text2', style={'fontSize':'40', 'textAlign':'center', 'backgroundColor':'#FFD5EC'})
],
style={'width':'90%', 'fontSize':'40', 'textAlign':'center'}
)


# コールバック1
@app.callback(
Output('hover_text1', 'children'),
Input('graph1', 'hoverData')
)
def show_text1(text):
return json.dumps(text)

# コールバック2
@app.callback(
Output('hover_text2', 'children'),
Input('graph1', 'hoverData')
)
def show_text2(text):
if text is None:
raise dash.exceptions.PreventUpdate # コールバック処理を停止
return json.dumps(text)

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

ホバーデータを返すコールバックを2つ定義し、2つ目のコールバックではホバーデータがない場合コールバック処理を停止しています。(47行目)

[ブラウザで表示]

画面をロードした直後は、1つめのPコンポーネントには‘null’が表示されていますが、2つめのPコンポーネントには何も表示されていません。(ホバーデータがないのでコールバック処理が停止されたため)

グラフ上のデータをマウスオバーすると、両方のPコンポーネントに同じデータが表示されます。(ホバーデータがありコールバック処理が停止されなかったため)

Dash㉘(マウス動作 ドラッグ操作)

ドラッグ操作

selectedData属性では、ドラッグ操作で複数要素を選択することもできます。

scatter関数の引数templateのdragmode属性‘select’を設定し、複数要素選択に設定します。(デフォルトでは‘zoom’となっています。)

レイアウトにGraphコンポーネントを2つ設定して、ドラッグして選択した複数の要素を2種類の折れ線グラフに反映します。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import json

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output

gapminder = px.data.gapminder() # データの読み込み
gapminder2007 = gapminder[gapminder['year'] == 2007]

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
html.H1('Gapminder Graph'),
# 散布図
dcc.Graph(
id='graph1',
figure=px.scatter(
gapminder2007, x='gdpPercap', y='lifeExp', hover_name='country',
template={'layout':{'dragmode':'select'}} # ドラッグモードをselectにする
)
),
html.Div(
[
dcc.Graph(id='graph2', className='six columns'),
dcc.Graph(id='graph3', className='six columns')
]
)
],
style={'width':'90%', 'fontSize':'40', 'textAlign':'center'}
)


# コールバック
@app.callback(
Output('graph2', 'figure'),
Output('graph3', 'figure'),
Input('graph1', 'selectedData') # GraphのselectedData属性を指定
)
def show_text(selectedData):
if selectedData:
selected_countries = [data['hovertext'] for data in selectedData['points']]
selected_df = gapminder[gapminder['country'].isin(selected_countries)]
fig2 = px.line(selected_df, x='year', y='pop', color='country', title='各国の人口')
fig3 = px.line(selected_df, x='year', y='lifeExp', color='country', title='各国の平均寿命')
return fig2, fig3
raise dash.exceptions.PreventUpdate

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

コールバック関数では、ドラッグで選択された複数の国を抽出し、その国の人口と平均寿命を折れ線グラフに描画しています。

[ブラウザで表示]

散布図上でドラッグ複数要素を選択すると、その要素をもとに下側の折れ線グラフが描画されることが確認できます。

Dash㉗(マウス動作 複数選択)

複数選択 (selectedData属性)

selectedData属性を使うと、グラフ上のデータを複数選択することができます。

複数選択できるようにするためには、scatter関数の引数templateのキー‘layout’のclickmode属性に‘event+select’を設定します。(23行目)

また、コールバックの入力項目に‘selectedData’を設定します。(35行目)

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import json

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output

gapminder = px.data.gapminder() # データの読み込み
gapminder2007 = gapminder[gapminder['year'] == 2007]

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
html.H1('Gapminder Graph'),
# 散布図
dcc.Graph(
id='graph1',
figure=px.scatter(
gapminder2007, x='gdpPercap', y='lifeExp', hover_name='country',
template={'layout':{'clickmode':'event+select'}} # クリック+Shiftで複数データを選択
)
),
# ホバーデータを表示するコンポーネント
html.P(id='hover_text', style={'fontSize':'40', 'textAlign':'center'})
],
style={'width':'90%', 'fontSize':'40', 'textAlign':'center'}
)


# コールバック
@app.callback(
Output('hover_text', 'children'),
Input('graph1', 'selectedData') # GraphのselectedData属性を指定
)
def show_text(text):
return json.dumps(text)

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

[ブラウザで表示]

Shiftキーを押しながら、グラフ上のデータを選択すると複数選択でき、そのデータがグラフ下に表示されます。

Dash㉖(グラフ上のマウス動作)

マウス動作

DashではGraphコンポーネントの属性から、グラフ上の要素の情報をマウス操作から取得することができます。

マウス操作で取得できる情報は下記の通りです。

属性 内容
hoverData グラフ上の1つの要素をホバーで取得する。
clickData グラフ上の1つの要素をクリックで取得する。
relayoutData グラフ上の指定した範囲の位置データを取得する。
selectedData グラフ上の複数要素を「右クリック+Shift」またはドラッグで取得する。

ホバー取得 (hoverData属性)

hoverData属性を使って、マウスカーソルをあてた散布図のデータを表示するアプリケーションを作成します。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import json

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output

gapminder = px.data.gapminder() # データの読み込み
gapminder2007 = gapminder[gapminder['year'] == 2007]

app = dash.Dash(__name__) # Dashインスタンスを生成

app.layout = html.Div(
[
html.H1('Gapminder Graph'),
# 散布図
dcc.Graph(
id='graph1',
figure=px.scatter(
gapminder2007, x='gdpPercap', y='lifeExp', hover_name='country'
)
),
# ホバーデータを表示するコンポーネント
html.P(id='hover_text', style={'fontSize':'40', 'textAlign':'center'})
],
style={'width':'90%', 'fontSize':'40', 'textAlign':'center'}
)


# コールバック
@app.callback(
Output('hover_text', 'children'),
Input('graph1', 'hoverData')
)
def show_text(text):
return json.dumps(text)

if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

コールバック関数の入力項目にはhoverDataを指定し、関数内ではhoverData属性から取得した文字列を返しています。

[ブラウザで表示]

グラフ上をマウスオーバーすると、グラフの下にカーソルをあてた箇所のデータが表示されます。