如何在 Python 上高效地创建交互式有向网络图(带箭头)?

How to efficiently create interactive directed network graphs (with arrows) on Python?

为了构建有向网络图,Plotly目前的做法似乎是使用注解。当边缘很少并且可以通过图形布局手动填充每个边缘时,这会起作用,例如 this example.

但是如果我要创建一个复杂得多的图,有没有一种好的方法可以迭代定义所有边的箭头坐标(我只能考虑构造一个字符串然后使用 eval(),虽然我知道这是不好的做法)? (编辑:似乎这种连接迭代生成的 dict() 定义字符串的方法不起作用——仅适用于一个 dict() 定义)

编辑:添加代码片段以更好地说明场景(注释掉 eval() 行以进行比较):

import plotly.offline as py 
import plotly.graph_objs as go 

trace = go.Scatter( 
    x=[1, 2, 2, 1], 
    y=[3, 4, 3, 4], 
    mode='markers',
    marker=dict(size=[100, 100, 100, 100])
)

fig = go.Figure(
    data=[trace],
    layout=go.Layout(
        annotations = [
            dict(
                ax=1, ay=3, axref='x', ayref='y',
                x=2, y=4, xref='x', yref='y'
            ),
            # eval("dict(ax=2, ay=3, axref='x', ayref='y', x=1, y=4, xref='x', yref='y')")
        ]
    )
) 
py.plot(fig)

如果在 Bokeh 或其他工具下有好的方法,我也愿意尝试其他可视化包。

下面是使用循环在 Plotly 图中创建箭头的示例,它很容易应用于有向图的 NetworkX 可视化。

import plotly.offline as py 
import plotly.graph_objs as go 

trace = go.Scatter( 
    x=[1, 2, 2, 1], 
    y=[3, 4, 3, 4], 
    mode='markers',
    marker=dict(size=[100, 100, 100, 100])
)

# Edges
x0 = [1, 2]
y0 = [3, 3]
x1 = [2, 1]
y1 = [4, 4]

fig = go.Figure(
    data=[trace],
    layout=go.Layout(
        annotations = [
            dict(ax=x0[i], ay=y0[i], axref='x', ayref='y',
                x=x1[i], y=y1[i], xref='x', yref='y',
                showarrow=True, arrowhead=1,) for i in range(0, len(x0))
        ]
    )
) 
py.plot(fig)

是的,我同意注释解决方案效率不高。这是否适用于您正在尝试做的事情:https://github.com/redransil/plotly-dirgraph

import plotly.graph_objects as go
import networkx as nx
import dash
import dash_core_components as dcc
import dash_html_components as html
from addEdge import addEdge

# Controls for how the graph is drawn
nodeColor = 'Blue'
nodeSize = 20
lineWidth = 2
lineColor = '#000000'

# Make a random graph using networkx
G = nx.random_geometric_graph(5, .5)
pos = nx.layout.spring_layout(G)
for node in G.nodes:
    G.nodes[node]['pos'] = list(pos[node])

# Make list of nodes for plotly
node_x = []
node_y = []
for node in G.nodes():
    x, y = G.nodes[node]['pos']
    node_x.append(x)
    node_y.append(y)

# Make a list of edges for plotly, including line segments that result in arrowheads
edge_x = []
edge_y = []
for edge in G.edges():
    # addEdge(start, end, edge_x, edge_y, lengthFrac=1, arrowPos = None, arrowLength=0.025, arrowAngle = 30, dotSize=20)
    start = G.nodes[edge[0]]['pos']
    end = G.nodes[edge[1]]['pos']
    edge_x, edge_y = addEdge(start, end, edge_x, edge_y, .8, 'end', .04, 30, nodeSize)


edge_trace = go.Scatter(x=edge_x, y=edge_y, line=dict(width=lineWidth, color=lineColor), hoverinfo='none', mode='lines')


node_trace = go.Scatter(x=node_x, y=node_y, mode='markers', hoverinfo='text', marker=dict(showscale=False, color = nodeColor, size=nodeSize))

fig = go.Figure(data=[edge_trace, node_trace],
             layout=go.Layout(
                showlegend=False,
                hovermode='closest',
                margin=dict(b=20,l=5,r=5,t=40),
                xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
                yaxis=dict(showgrid=False, zeroline=False, showticklabels=False))
                )

# Note: if you don't use fixed ratio axes, the arrows won't be symmetrical
fig.update_layout(yaxis = dict(scaleanchor = "x", scaleratio = 1), plot_bgcolor='rgb(255,255,255)')

app = dash.Dash()
app.layout = html.Div([dcc.Graph(figure=fig)])

app.run_server(debug=True, use_reloader=False)

示例: directed graph output

library d3graph may be use. You can specify directed edges. It is a force-directed d3-graph. More information is posted here: Django and interactive graph/network visualization