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

asp*_*ire 3 python directed-graph networkx plotly

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

但是,如果我要创建一个更复杂的图形,是否有一种好方法可以迭代地定义所有边的箭头坐标(我只能考虑构造一个字符串然后使用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)
Run Code Online (Sandbox Code Playgroud)

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

小智 8

我是gravis的作者,这是一个 Python 中的交互式图形可视化包。它可以识别来自多个网络分析包(例如 NetworkX、igraph 或 graph-tool)的图形对象。

这是使用 NetworkX 创建有向图并使用 gravis 对其进行可视化的最小示例。

import gravis as gv
import networkx as nx

g = nx.DiGraph()
g.add_edges_from([(1, 2), (2, 3), (2, 4), (4, 5), (4, 7), (5, 6), (1, 6), (6, 7)])
gv.d3(g)
Run Code Online (Sandbox Code Playgroud)

  • 我建议更改您的答案,以通过页面中嵌入的交互式可视化来反映 OP 问题。然后人们就会看到编码是多么容易以及结果是多么漂亮。 (3认同)
  • 很棒的图书馆! (2认同)

asp*_*ire 5

下面是使用循环在 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)
Run Code Online (Sandbox Code Playgroud)


小智 2

是的,我同意注释解决方案效率不高。这是否适合您想要做的事情: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)
Run Code Online (Sandbox Code Playgroud)

示例: 有向图输出