Dash Bio③(サーコスプロットの外側にヒストグラム表示)

サーコスプロットの外側にヒストグラム表示

サーコスプロットの外側にヒストグラムを表示してみます。

グラフの種類に‘HISTOGRAM’を設定するとヒストグラムを表示することができます。(24行目)

[ソースコード]

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
import json
import urllib.request as urlreq

import dash
import dash_bio as dashbio
from dash import html, dcc
from dash.dependencies import Input, Output, State

app = dash.Dash(__name__)

data = urlreq.urlopen(
"https://git.io/circos_graph_data.json"
).read().decode("utf-8")

circos_graph_data = json.loads(data)

app.layout = html.Div(
[
dashbio.Circos(
id="circos",
layout=circos_graph_data["GRCh37"],
tracks=[
{
'type':'HISTOGRAM',
'data':circos_graph_data["histogram"],
'config':{ # ヒストグラムを外側に表示
'innerRadius':300,
'outerRadius':500
}
}
]
)
]
)

if __name__ == "__main__":
app.run_server(debug=True)

ヒストグラムを外側に表示するためにconfig属性を設定する必要はないのですが、デフォルトだとヒストグラムが小さく見づらいので、ヒストグラムが見やすくなるようにconfig属性を設定します。

‘innerRadius’には外側の半径、‘outerRadius’には外側の半径を設定しています。(26~29行目)

[ブラウザで表示]

サーコスプロットの外側にヒストグラムを表示することができました。

Dash Bio②(サーコスプロットの内側にヒストグラム表示)

サーコスプロットの内側にヒストグラム表示

サーコスプロットの内側にヒストグラムを表示してみます。

グラフの種類に‘HISTOGRAM’を設定するとヒストグラムを表示することができます。(24行目)

[ソースコード]

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
import json
import urllib.request as urlreq

import dash
import dash_bio as dashbio
from dash import html, dcc
from dash.dependencies import Input, Output, State

app = dash.Dash(__name__)

data = urlreq.urlopen(
"https://git.io/circos_graph_data.json"
).read().decode("utf-8")

circos_graph_data = json.loads(data)

app.layout = html.Div(
[
dashbio.Circos(
id="circos",
layout=circos_graph_data["GRCh37"],
tracks=[
{
'type':'HISTOGRAM',
'data':circos_graph_data["histogram"],
'config':{ # ヒストグラムを内側に表示
'innerRadius':40,
'outerRadius':230
}
}
]
)
]
)

if __name__ == "__main__":
app.run_server(debug=True)

ヒストグラムを内側に表示するためにconfig属性に描画する位置を指定する必要があります。

‘innerRadius’には内側の半径、‘outerRadius’には外側の半径を設定します。(26~29行目)

[ブラウザで表示]

サーコスプロットの内側にヒストグラムを表示することができました。

Dash Bio①(インストール/サーコスプロット)

Dash Bioコンポーネントを使うと、バイオインフォマティクスのデータを短いコードで可視化、分析することができます。

Dash Bioのインストール

Dash Bioをインストールするためには、下記のコマンドを実行します。

[コマンド]

1
pip install dash-bio

Windows環境で実行していてMicrosoft Visual C++ 14.0 or greater is required.というエラーが発生した場合、下記のサイトを参考にMicrosoft C++ Build Toolsをインストールしてください。

参考サイト - 「Microsoft Visual C++ 14.0 or greater is required.」が出た場合の対処方法

サーコスプロット

Circosコンポーネントを使ってデータ間の繋がりを円形で可視化します。

サーコスプロットは円形のレイアウトで、要素間の関係を可視化することができ、バイオインフォマティクスの分野以外にもいろいろな分野で利用できます。

[ソースコード]

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
import dash
import dash_bio as dashbio
import dash_html_components as html

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

# レイアウト作成
app.layout = html.Div(
[
dashbio.Circos(
# 要素
layout=[
{'id':'1', 'label':'1', 'color':'yellow', 'len':100},
{'id':'2', 'label':'2', 'color':'skyblue','len':180},
{'id':'3', 'label':'3', 'color':'purple', 'len':250},
{'id':'4', 'label':'4', 'color':'pink', 'len':200},
{'id':'5', 'label':'5', 'color':'lime', 'len': 70}
],
# 要素の関係
tracks=[
{
# グラフの種類
'type': 'CHORDS',
# 要素間のデータ
'data':[
{
'source':{'id':'1', 'start':50, 'end':100},
'target':{'id':'3', 'start':30, 'end':50},
},
{
'source':{'id':'1', 'start':30, 'end':50},
'target':{'id':'4', 'start': 0, 'end':70},
},
{
'source':{'id':'2', 'start':100, 'end':150},
'target':{'id':'5', 'start': 30, 'end': 50},
},
{
'source':{'id':'3', 'start':100, 'end':150},
'target':{'id':'3', 'start': 0, 'end': 30},
}
],
'opacity':0.8,
'color':{'name':'color'},
'config':{
# マウスオーバー時に表示するデータ
'tooltipContent':{
'source':'source',
'sourceID':'id',
'target':'target',
'targetID':'id',
'targetEnd':'end'
}
}
}
]
)
]
)

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

Circosコンポーネントは、引数 layoutに外側の円の要素、引数 tracksに要素同士の関連データを渡してグラフを作成します。

作成するグラフの種類は引数 tracksに渡す辞書のキー‘type’に値を設定します。

各属性の主な設定方法は以下の通りです。

  • layout (辞書:必須) サーコスプロットの外側の部分を設定
    • id (文字列:必須) ブロックID
    • label (文字列:必須) ブロックのラベル
    • color (文字列:必須) ブロックの色
    • len (数値:必須) ブロックの長さ
  • tracks (辞書:任意) トラックの設定
    • id (文字列:認知) トラックのデータID
    • data (リスト:必須) トラックのデータ(JSONデータ)
    • type (文字列:任意) トラックの種類
    • config (辞書:任意) トラックのレイアウト

[ブラウザで表示]

サーコスプロットが表示され、円形のレイアウトでそれぞれの要素間の繋がりを確認することができます。

Dash 第62回(DashCanvas 画像の切り抜き)

DashCanvas 画像の切り抜き

画像を表示したキャンバス上で輪郭を指定して、その範囲の画像の切り抜きを行ってみます。

superpixel_color_segmentation関数を使って、ペンシルツールで指定した輪郭の内側を切り抜いて表示します。

[ソースコード]

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
import dash
import dash_html_components as html
import numpy as np
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
from dash_canvas import DashCanvas
from dash_canvas.utils import (array_to_data_url, image_string_to_PILImage,
parse_jsonstring, superpixel_color_segmentation)
from skimage import io

filepath = 'cherry.png'
image = array_to_data_url(io.imread(filepath))
app = dash.Dash(__name__) # Dashインスタンスを生成

# レイアウト作成
app.layout = html.Div(
[
html.Div(
[
DashCanvas(
id='image1',
image_content=image,
width=400,
lineWidth=5,
lineColor='lime',
goButtonTitle='remove'
)
],
),
html.Div(
[html.Img(id='image2',width=400)]
)
]
)

# 選択した長方形の範囲をテーブルに表示するコールバック
@app.callback(
Output('image2', 'src'),
Input('image1', 'json_data'),
Input('image1', 'image_content')
)
def remove_background(json_data, image):
if json_data:
if image: # imageが値を持つ場合
# もとの画像をnumpy.ndarrayに変換する
image_array = image_string_to_PILImage(image)
image_array = np.asarray(image_array)
else:
image_array = io.imread(filepath)
# 画像のarrayサイズを変数shapeに代入する
shape = image_array.shape[:2]

# アノテーションのjsonデータをパースし、ブール値に変換する
try:
mask = parse_jsonstring(json_data, shape=shape)
except IndexError:
raise PreventUpdate

if mask.sum() > 0:
seg = superpixel_color_segmentation(image_array, mask)
else:
seg = np.ones(shape)
filled_image = np.copy(image_array)
filled_image[np.logical_not(seg)] = np.array([255, 255, 255], dtype='uint8')
return array_to_data_url(filled_image)
else:
raise PreventUpdate

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

画像の背景を取り除く処理はremove_backgroundコールバック関数で行っています。(42行目)

入力項目は、画像へのアノテーションデータをもつjson_data属性と、画像データ文字列のimage_contents属性です。

処理の流れは以下の通りです。

  1. 画像データを取得し、numpy.ndarray型に変換する。(46~47行目)
  2. 画像へのアノテーションデータと画像の形状からブール値に変換する。(55行目)
  3. マスク処理したデータが存在する場合、superpixel_color_segmentation関数を使って類似した色を分割する。(60行目)
  4. 画像から3の結果を反転した領域を、白色(255.255.255)にする。(64行目)
  5. 処理した画像(numpy.ndarray)を文字列に変換する。(65行目)

[ブラウザで表示]

対象を選択しremoveボタンを押すと、選択範囲が切り抜かれてツールの下に表示される・・・・はずなんですがはっきり切り抜きできませんでした。

2回、3回同じ範囲を選択するとうまく切り抜かれていくようです。

改善の余地がありそうですね😥

Dash 第61回(DashCanvas 選択情報をテーブル表示)

選択情報をテーブル表示

キャンバス上で長方形の選択をした箇所をテーブルに表示してみます。

長方形で選択した範囲をparse_jsonstring_rectangleで取得し(67行目)、そのデータをDataFrameに変換してDataTableコンポーネントに設定・表示します。(68~70行目)

また、線の色を選択できるラジオボタンも作成しています。(23~27行目、51~56行目)

[ソースコード]

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
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import pandas as pd
from dash.dependencies import Input, Output
from dash_canvas import DashCanvas
from dash_canvas.utils import array_to_data_url, parse_jsonstring_rectangle
from skimage import io

filename = array_to_data_url(io.imread('cherry.png'))
pen_color = ['black', 'lime', 'red', 'blue', 'yellow']
columns = ['width', 'height', 'left', 'top']

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

# レイアウト作成
app.layout = html.Div(
[
html.Div(
[
# 線の色を選択するラジオボタン
dcc.RadioItems(
id='color1',
options=[{'value':c, 'label':c} for c in pen_color],
value='lime'
),
DashCanvas(
id='image1',
image_content=filename,
width=400,
lineWidth=5,
goButtonTitle='to_table'
)
],
),
html.Div(
[
# 選択した長方形の範囲を表示するテーブル
dash_table.DataTable(
id='table1',
columns=[{'name':i, 'id':i} for i in columns],
export_format='csv'
)
]
)
]
)

# 線の色を更新するコールバック
@app.callback(
Output('image1', 'lineColor'),
Input('color1', 'value')
)
def update_color(selected_color):
return selected_color

# 選択した長方形の範囲をテーブルに表示するコールバック
@app.callback(
Output('table1', 'data'),
Input('image1', 'json_data')
)
def showLine(json_data):
if not json_data:
raise dash.exceptions.PreventUpdate
else:
data = parse_jsonstring_rectangle(json_data)
if len(data):
df = pd.DataFrame(data, columns=columns)
return df.to_dict('records')

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

[ブラウザで表示]

キャンバス上をいくつか長方形で選択をした後に、to_tableボタンを押すと選択情報がテーブルに表示されます。

またExportボタンを押すと選択情報をCSVファイルに出力することができます。

Dash 第60回(DashCanvas 画像表示)

DashCanvas 画像表示

キャンバスに画像を表示します。

画像を表示する手順は、以下の通りです。

  1. imread関数を使って、画像をnumpy.ndarray型に変換する。(7行目)
  2. array_to_data_url関数を使って、文字列に変換する。(7行目)
  3. 文字列に変換した画像データを、DashCanvasの引数 image_contentに設定し、キャンバスに画像を表示する。(13行目)

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import dash
from dash_canvas import DashCanvas
from dash_canvas.utils import array_to_data_url
from skimage import io

# 画像を変数に渡す
data = array_to_data_url(io.imread('cherry.png'))
app = dash.Dash(__name__) # Dashインスタンスを生成

# レイアウト作成
app.layout = DashCanvas(
id='image1',
image_content=data, # コンポーネントへ画像を設定
width=500, # キャンバスの横幅
lineWidth=12, # アノテーションの横幅
goButtonTitle='Test', # Saveボタンのタイトル
lineColor='lime', # アノテーションの線の色
hide_buttons=['zoom', 'pan', 'line'] # ボタンを非表示
)

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

キャンバスの横幅や、編集ツールの設定も行っています。(14~18行目)

[ブラウザで表示]

キャンバスに画像が表示され、編集ツールを使って画像を編集することができました。

Dash 第59回(DashCanvasクラスの属性)

DashCanvasクラスの属性

DashCanvasクラスの属性を一覧に表します。

属性 データ型 内容
id 文字列 コンポーネントのIDを設定。
image_content 文字列 PNGまたはJPEGフォーマットの画像データ文字列を設定。
array_to_data_url関数を用いて生成する。
zoom 数値 画像の拡大を設定。
width 数値 キャンバスの横幅を設定。
height 数値 キャンバスの高さを設定。
scale 数値 キャンバスの横幅と画像の横幅の縮尺比を設定。
tool ‘pencil’
‘pen’
‘circle’
‘rectangle’
‘select’
‘line’
描画ツールの初期値を設定。
lineWidth 数値 ペンシルモード時の線幅を設定。
lineColor 文字列 ペンシルモード時の線の色、色名、RGB、RGBAを指定可能。
goButtonTitle 文字列 Saveボタンのタイトルを設定。
filename 文字列 ロードする画像ファイル名(URL文字列)を設定。
trigger 数値 Saveボタンが押された回数データを保持。
json_data 文字列 アノテーションの内容を保持。
hide_buttons ‘pencil’,’zoom’などのリスト型 非表示にする描画ツールを指定。

Dash 第58回(DashCanvas アノテーション表示)

アノテーション表示

json_data属性を使って、キャンバスのアノテーションの内容を表示するアプリケーションを作成します。

レイアウトとしてDashCanvasコンポ―ネントの下に、アノテーションの内容を表示するDivコンポーネントを配置します。(10行目)

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import json
import dash
import dash_html_components as html
from dash.dependencies import Input, Output
from dash_canvas import DashCanvas

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

# レイアウト作成
app.layout = html.Div([DashCanvas(id='canvas1'), html.Div(id='div1')])

@app.callback(
# 入力項目にID'canvas1'のjson_data属性を設定
Output('div1', 'children'),
Input('canvas1', 'json_data')
)
def show_json(json_data):
if json_data: # json_data属性にデータが存在するかどうか
return json.dumps(json_data) # json_dataを文字列にして返す
else:
raise dash.exceptions.PreventUpdate # コールバックを更新しない

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

コールバック関数では、出力項目をDivコンポーネントのchildren属性、入力項目をDashCanvasコンポーネントのjson_data属性としています。(14~15行目)

json_data属性にデータがある場合、json_data属性の値を返し、データがない場合はコールバックの更新を行いません。(18~21行目)

[ブラウザで表示]

キャンバスに線を描画し、Saveボタンをクリックするとアノテーションの内容が表示されることを確認できます。

Dash 第57回(DashCanvas)

DashCanvasコンポーネントは、画像編集ツールをもつキャンバスです。

キャンバス上に画像を表示し、アノテーションを用いて画像処理をすることができます。

インストール

DashCanvasコンポーネントを使うためには、下記のコマンドを実行しインストールを行います。

[コマンド]

1
pip install dash-canvas

DashCanvasサンプルコード

デフォルト設定のDashCanvasコンポーネントを作成するコードは下記の通りです。

[ソースコード]

1
2
3
4
5
6
7
import dash
from dash_canvas import DashCanvas

app = dash.Dash(__name__) # Dashインスタンスを生成
app.layout = DashCanvas()
if __name__ == '__main__':
app.run_server(debug=True) # アプリケーションを起動

[ブラウザで表示]

線や枠を描画したり、移動・拡大・縮小などができます。

編集ツール右端のSaveボタンでは、画像の保存ではなくDashCanvasコンポーネントのjson_data属性の更新を行います。

json_data属性は描き込みツールを使って描いたアノテーションのデータをもつ属性で、データはJSON形式で保存されます、

Dash 第56回(テーブルデータを地図に表示)

テーブルデータを地図に表示

テーブル上の位置データを地図上に表示します。

地図を表示するためにmapboxよりアクセストークンを取得しておく必要があります。(12行目で設定)

[ソースコード]

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
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import pandas as pd
import plotly.express as px
from dash.dependencies import Input, Output

df = pd.read_csv('401307compatibleformatshelter.csv', encoding='cp932')

# mapboxのアクセストークンを設定
px.set_mapbox_access_token('<<mapboxで取得したトークン>>')

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

app.layout = html.Div(
[
html.Div(
[
html.H2('避難所マップ'),
dash_table.DataTable(
id='table1',
# セルサイズの設定
style_cell={
'textAliign':'center', # テキストを中央寄せ
'maxWidth':'80px', # 最大横幅
'minWidth':'80px', # 最小横幅
'whiteSpace':'normal' # 文字を折り返して表示
},
fixed_rows={'headers':True}, # 縦スクロール時にヘッダーを固定
columns=[{'name':col, 'id':col} for col in df.columns],
data=df.to_dict('records')
)
]
),
dcc.Graph(id='map1')
]
)

@app.callback(
Output('map1', 'figure'),
Input('table1', 'columns'),
Input('table1', 'derived_virtual_data')
)
def update_map(columns, rows):
df = pd.DataFrame(rows, columns=[c['name'] for c in columns])
return px.scatter_mapbox(
df,
lat='緯度',
lon='経度',
zoom=10,
hover_data=['名称', '名称かな表記', '住所表記']
)

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

コールバック関数には、テーブルの引数columnsderived_virtual_dataを入力としています。

derived_virtual_dataは、テーブルの全ページデータです。

update_map関数では、列名と全データからDataFrameを作成し、そのDataFrameをscatter_mapbox関数に設定し、地図上に散布図を描画したfigureを返しています。(46~53行目)

[ブラウザで表示]

テーブルの位置データが地図上に散布図として描画されていることを確認できました。