问题描述
我想通过使用networkx
#Graph1
g1 = nx.read_edgelist('sample.txt', nodetype=str)
pos = nx.shell_layout(g)
plt.figure(figsize=(10, 10))
nx.draw_networkx_edges(g, pos, edge_color='khaki', alpha=1)
nx.draw_networkx_nodes(g,pos,node_color='r',alpha=0.5,node_size=1000)
nx.draw_networkx_labels(g, pos, font_size=10,font_family='IPAexGothic')
plt.axis('off')
#Graph2
g2 = nx.read_edgelist('sample2.txt', nodetype=str)
pos = nx.shell_layout(g)
plt.figure(figsize=(10, 10))
nx.draw_networkx_edges(g, pos, edge_color='khaki', alpha=1)
nx.draw_networkx_nodes(g,pos,node_color='r',alpha=0.5,node_size=1000)
nx.draw_networkx_labels(g, pos, font_size=10,font_family='IPAexGothic')
plt.axis('off')
推荐答案
networkx
中没有当前支持分层布局的功能,更不用说显示的可视化了.因此,我们需要自己动手.
There is no functionality within networkx
that currently supports a layered layout, much less a visualization as shown. So we need to roll our own.
以下实现LayeredNetworkGraph
假定您具有表示不同层的图[g1, g2, ..., gn]
的列表.在一个层中,相应的(子)图定义了连通性.在各层之间,如果后续层中的节点具有相同的节点ID,则将它们连接起来.
The following implementation LayeredNetworkGraph
assumes that you have a list of graphs [g1, g2, ..., gn]
that represent the different layers. Within a layer, the corresponding (sub-) graph defines the connectivity. Between layers, nodes in subsequent layers are connected if they have the same node ID.
由于没有布局函数(AFAIK)可以在三维上施加节点的平面性约束来计算三维位置,因此我们使用了一个小技巧:我们在所有图层上创建一个图形合成,在其中计算位置二维,然后将这些位置应用于所有层中的节点.可以计算出具有平面度约束的真正的力导向布局,但这将需要大量工作,并且由于您的示例仅使用了外壳布局(不会受到影响),因此我丝毫没有打扰.在许多情况下,差异将很小.
As there are no layout functions (AFAIK) that would compute node positions in three dimensions with the planarity constraint imposed on nodes within a layer, we use a small hack: we create a graph composition across all layers, compute the positions in two dimensions, and then apply these positions to nodes in all layers. One could compute a true force directed layout with the planarity constraints, but that would be a lot of work and since your example only used a shell layout (which would be unaffected), I haven't bothered. The differences would be small in many cases.
如果要更改可视化的各个方面(大小,宽度,颜色),请查看draw
方法.您可能需要的大多数更改都可以在此处进行.
If you want to change aspects of the visualisation (sizes, widths, colours), have a look at the draw
method. Most changes that you might require can probably be made there.
#!/usr/bin/env python
"""
Plot multi-graphs in 3D.
"""
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Line3DCollection
class LayeredNetworkGraph(object):
def __init__(self, graphs, node_labels=None, layout=nx.spring_layout, ax=None):
"""Given an ordered list of graphs [g1, g2, ..., gn] that represent
different layers in a multi-layer network, plot the network in
3D with the different layers separated along the z-axis.
Within a layer, the corresponding graph defines the connectivity.
Between layers, nodes in subsequent layers are connected if
they have the same node ID.
Arguments:
----------
graphs : list of networkx.Graph objects
List of graphs, one for each layer.
node_labels : dict node ID : str label or None (default None)
Dictionary mapping nodes to labels.
If None is provided, nodes are not labelled.
layout_func : function handle (default networkx.spring_layout)
Function used to compute the layout.
ax : mpl_toolkits.mplot3d.Axes3d instance or None (default None)
The axis to plot to. If None is given, a new figure and a new axis are created.
"""
# book-keeping
self.graphs = graphs
self.total_layers = len(graphs)
self.node_labels = node_labels
self.layout = layout
if ax:
self.ax = ax
else:
fig = plt.figure()
self.ax = fig.add_subplot(111, projection='3d')
# create internal representation of nodes and edges
self.get_nodes()
self.get_edges_within_layers()
self.get_edges_between_layers()
# compute layout and plot
self.get_node_positions()
self.draw()
def get_nodes(self):
"""Construct an internal representation of nodes with the format (node ID, layer)."""
self.nodes = []
for z, g in enumerate(self.graphs):
self.nodes.extend([(node, z) for node in g.nodes()])
def get_edges_within_layers(self):
"""Remap edges in the individual layers to the internal representations of the node IDs."""
self.edges_within_layers = []
for z, g in enumerate(self.graphs):
self.edges_within_layers.extend([((source, z), (target, z)) for source, target in g.edges()])
def get_edges_between_layers(self):
"""Determine edges between layers. Nodes in subsequent layers are
thought to be connected if they have the same ID."""
self.edges_between_layers = []
for z1, g in enumerate(self.graphs[:-1]):
z2 = z1 + 1
h = self.graphs[z2]
shared_nodes = set(g.nodes()) & set(h.nodes())
self.edges_between_layers.extend([((node, z1), (node, z2)) for node in shared_nodes])
def get_node_positions(self, *args, **kwargs):
"""Get the node positions in the layered layout."""
# What we would like to do, is apply the layout function to a combined, layered network.
# However, networkx layout functions are not implemented for the multi-dimensional case.
# Futhermore, even if there was such a layout function, there probably would be no straightforward way to
# specify the planarity requirement for nodes within a layer.
# Therefor, we compute the layout for the full network in 2D, and then apply the
# positions to the nodes in all planes.
# For a force-directed layout, this will approximately do the right thing.
# TODO: implement FR in 3D with layer constraints.
composition = self.graphs[0]
for h in self.graphs[1:]:
composition = nx.compose(composition, h)
pos = self.layout(composition, *args, **kwargs)
self.node_positions = dict()
for z, g in enumerate(self.graphs):
self.node_positions.update({(node, z) : (*pos[node], z) for node in g.nodes()})
def draw_nodes(self, nodes, *args, **kwargs):
x, y, z = zip(*[self.node_positions[node] for node in nodes])
self.ax.scatter(x, y, z, *args, **kwargs)
def draw_edges(self, edges, *args, **kwargs):
segments = [(self.node_positions[source], self.node_positions[target]) for source, target in edges]
line_collection = Line3DCollection(segments, *args, **kwargs)
self.ax.add_collection3d(line_collection)
def get_extent(self, pad=0.1):
xyz = np.array(list(self.node_positions.values()))
xmin, ymin, _ = np.min(xyz, axis=0)
xmax, ymax, _ = np.max(xyz, axis=0)
dx = xmax - xmin
dy = ymax - ymin
return (xmin - pad * dx, xmax + pad * dx), \
(ymin - pad * dy, ymax + pad * dy)
def draw_plane(self, z, *args, **kwargs):
(xmin, xmax), (ymin, ymax) = self.get_extent(pad=0.1)
u = np.linspace(xmin, xmax, 10)
v = np.linspace(ymin, ymax, 10)
U, V = np.meshgrid(u ,v)
W = z * np.ones_like(U)
self.ax.plot_surface(U, V, W, *args, **kwargs)
def draw_node_labels(self, node_labels, *args, **kwargs):
for node, z in self.nodes:
if node in node_labels:
ax.text(*self.node_positions[(node, z)], node_labels[node], *args, **kwargs)
def draw(self):
self.draw_edges(self.edges_within_layers, color='k', alpha=0.3, linestyle='-', zorder=2)
self.draw_edges(self.edges_between_layers, color='k', alpha=0.3, linestyle='--', zorder=2)
for z in range(self.total_layers):
self.draw_plane(z, alpha=0.2, zorder=1)
self.draw_nodes([node for node in self.nodes if node[1]==z], s=300, zorder=3)
if self.node_labels:
self.draw_node_labels(self.node_labels,
horizontalalignment='center',
verticalalignment='center',
zorder=100)
if __name__ == '__main__':
# define graphs
n = 5
g = nx.erdos_renyi_graph(4*n, p=0.1)
h = nx.erdos_renyi_graph(3*n, p=0.2)
i = nx.erdos_renyi_graph(2*n, p=0.4)
node_labels = {nn : str(nn) for nn in range(4*n)}
# initialise figure and plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
LayeredNetworkGraph([g, h, i], node_labels=node_labels, ax=ax, layout=nx.spring_layout)
ax.set_axis_off()
plt.show()
这篇关于networkx中的多层图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!