LoginSignup
12
10

More than 3 years have passed since last update.

【Python】Dash Cytoscapeで有向グラフを描く

Posted at

動かせる有向グラフを描きたい

RのvisNetworkのように、ブラウザ上で動かせる有向グラフをPythonでも描きたいと思いました.

Pythonのgraph libraryとしてはNetworkXが有名なようですが、今回RのvisNetworkに近いイメージで使えるのはDash Cytoscapeかなと思ったので、サンプルを書き残してみます.

ローカルでサーバーを立ち上げて、ブラウザからグラフを確認します.

目標

  • from, toの2列のデータフレームから、有向グラフを可視化します.
  • input イメージ
    image.png

  • 成果物イメージ
    forqiita2.gif

環境

  • OS : Windows10
  • Python : Python 3.8.3
  • dash == 1.4.1
  • dash-core-components == 1.3.1
  • dash-html-components == 1.0.1
  • dash-cytoscape == 0.2.0

準備

from・toを持つedgeのデータフレームと、nodesを用意します
app.py
import pandas as pd

edges = pd.DataFrame.from_dict({'from':['TABLE_B', 'TABLE_C', 'TABLE_D', 'TABLE_A', 'TABLE_X', 'TABLE_X'],
                               'to': ['TABLE_A', 'TABLE_A', 'TABLE_A','TABLE_X', 'TABLE_K', 'TABLE_Z']})
nodes = set()
edgeとnodeの内容をリストに格納します
app.py
cy_edges = []
cy_nodes = []

for index, row in edges.iterrows():
    source, target = row['from'], row['to']

    if source not in nodes:
        nodes.add(source)
        cy_nodes.append({"data": {"id": source, "label": source}})
    if target not in nodes:
        nodes.add(target)
        cy_nodes.append({"data": {"id": target, "label": target}})

    cy_edges.append({
        'data': {
            'source': source,
            'target': target
        }
    })

img

node・edgeのstyleを定義する
app.py
stylesheet = [
    {
        "selector": 'node', # すべてのnodeに対して
        'style': {
            "opacity": 0.9,
            "label": "data(label)", # 表示させるnodeのラベル
            "background-color": "#07ABA0", # nodeの色
            "color": "#008B80" # nodeのラベルの色
        }
    },
    {
        "selector": 'edge', # すべてのedgeに対して
        "style": {
            "target-arrow-color": "#C5D3E2", # 矢印の色
            "target-arrow-shape": "triangle", # 矢印の形
            "line-color": "#C5D3E2", # edgeのcolor
            'arrow-scale': 2, # 矢印のサイズ
            'curve-style': 'bezier' # デフォルトのcurve-styleだと矢印が表示されないため指定する
    }
}]
  • 矢印のスタイルなど、以下を参考にカスタマイズすることが可能です

  • 矢印の位置を中央に

app.py
        "style": {
            "mid-target-arrow-color": "#C5D3E2",
            "mid-target-arrow-shape": "triangle",
        }

image.png

  • また、"selector"の中に条件を定義することで、特定のedge, nodeにstyleをあてることも可能です
app.py
stylesheet  = [
    {
        "selector": 'node',
        'style': {
            "opacity": 0.9,
            "label": "data(label)",
            "background-color": "#07ABA0",
            "color": "#008B80"
        }
    },
    {
        "selector": '[label *= "alarm"]', # labelがalarmのnodeのみ"red"にする
        'style': {
            "opacity": 0.9,
            "label": "data(label)",
            "background-color": "red",
            "color": "#008B80"
        }
    },
    {
        "selector": 'edge',
        "style": {

            "target-arrow-color": "#C5D3E2",
            "target-arrow-shape": "triangle",
            "line-color": "#C5D3E2",
            'arrow-scale': 2,
            'curve-style': 'bezier'
        }
    }
]

image.png

layoutを定義する
app.py
app.layout = html.Div([
    dcc.Dropdown(
            id='dropdown-layout',
            options=[
                {'label': 'random',
                 'value': 'random'},
                {'label': 'grid',
                 'value': 'grid'},
                {'label': 'circle',
                 'value': 'circle'},
                {'label': 'concentric',
                 'value': 'concentric'},
                {'label': 'breadthfirst',
                 'value': 'breadthfirst'},
                {'label': 'cose',
                 'value': 'cose'}
            ], value='grid'
        ),
    html.Div(children=[
        cyto.Cytoscape(
            id='cytoscape',
            elements=cy_edges + cy_nodes,
            style={
                'height': '95vh',
                'width': '100%'
            },
            stylesheet=stylesheet # 先ほど定義したstyleを与える
        )
    ])

])
  • 今回はグラフのレイアウトをDropdownから選択できる形にしています.
layoutを更新するcallbackを作成
app.py
@app.callback(Output('cytoscape', 'layout'),
              [Input('dropdown-layout', 'value')])
def update_cytoscape_layout(layout):
    return {'name': layout}

完成

全文
app.py
import pandas as pd

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

import dash_cytoscape as cyto

app = dash.Dash(__name__)
server = app.server

# prepare data
edges = pd.DataFrame.from_dict({'from':['earthquake', 'earthquake', 'burglary', 'alarm', 'alarm'],
                               'to': ['report', 'alarm', 'alarm','John Calls', 'Mary Calls']})
nodes = set()

cy_edges = []
cy_nodes = []

for index, row in edges.iterrows():
    source, target = row['from'], row['to']

    if source not in nodes:
        nodes.add(source)
        cy_nodes.append({"data": {"id": source, "label": source}})
    if target not in nodes:
        nodes.add(target)
        cy_nodes.append({"data": {"id": target, "label": target}})

    cy_edges.append({
        'data': {
            'source': source,
            'target': target
        }
    })

# define stylesheet
stylesheet = [
    {
        "selector": 'node', # すべてのnodeに対して
        'style': {
            "opacity": 0.9,
            "label": "data(label)", # 表示させるnodeのラベル
            "background-color": "#07ABA0", # nodeの色
            "color": "#008B80" # nodeのラベルの色
        }
    },
    {
        "selector": 'edge', # すべてのedgeに対して
        "style": {
            "target-arrow-color": "#C5D3E2", # 矢印の色
            "target-arrow-shape": "triangle", # 矢印の形
            "line-color": "#C5D3E2", # edgeのcolor
            'arrow-scale': 2, # 矢印のサイズ
            'curve-style': 'bezier' # デフォルトのcurve-styleだと矢印が表示されないため指定する
    }
}]

# define layout
app.layout = html.Div([
    dcc.Dropdown(
            id='dropdown-layout',
            options=[
                {'label': 'random',
                 'value': 'random'},
                {'label': 'grid',
                 'value': 'grid'},
                {'label': 'circle',
                 'value': 'circle'},
                {'label': 'concentric',
                 'value': 'concentric'},
                {'label': 'breadthfirst',
                 'value': 'breadthfirst'},
                {'label': 'cose',
                 'value': 'cose'}
            ], value='grid'
        ),
    html.Div(children=[
        cyto.Cytoscape(
            id='cytoscape',
            elements=cy_edges + cy_nodes,
            style={
                'height': '95vh',
                'width': '100%'
            },
            stylesheet=stylesheet
        )
    ])
])

@app.callback(Output('cytoscape', 'layout'),
              [Input('dropdown-layout', 'value')])
def update_cytoscape_layout(layout):
    return {'name': layout}

if __name__ == '__main__':
    app.run_server(debug=False)
実行
$ python app.py

image.png

RのvisNetworkのようなネットワークの可視化が実現できたと思います. 皆さんも是非お試しください.

参考

有向グラフとは何?
Dash Cytoscape

networkx_sample.py
# https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_directed.html#sphx-glr-auto-examples-drawing-plot-directed-py
# Author: Rodrigo Dorantes-Gilardi (rodgdor@gmail.com)
import matplotlib as mpl
import matplotlib.pyplot as plt
import networkx as nx

G = nx.generators.directed.random_k_out_graph(10, 3, 0.5)
pos = nx.layout.spring_layout(G)

node_sizes = [3 + 10 * i for i in range(len(G))]
M = G.number_of_edges()
edge_colors = range(2, M + 2)
edge_alphas = [(5 + i) / (M + 4) for i in range(M)]

nodes = nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='blue')
edges = nx.draw_networkx_edges(G, pos, node_size=node_sizes, arrowstyle='->',
                               arrowsize=10, edge_color=edge_colors,
                               edge_cmap=plt.cm.Blues, width=2)
# set alpha value for each edge
for i in range(M):
    edges[i].set_alpha(edge_alphas[i])

pc = mpl.collections.PatchCollection(edges, cmap=plt.cm.Blues)
pc.set_array(edge_colors)
plt.colorbar(pc)

ax = plt.gca()
ax.set_axis_off()
plt.show()

image.png

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10