分层层次布局#
注意:我们强烈建议考虑其他布局,如环形布局(pygraphistry)和igraph插件的dot引擎
[ ]:
from graphistry.layout.sugiyama import SugiyamaLayout
from graphistry.layout.graph import Graph, Vertex, Edge
import pandas as pd
import networkx as nx
import matplotlib
import matplotlib.pyplot as plt
[ ]:
def from_networkx(nxg):
"""
Converts a networkx graph to a sugiyama graph.
"""
vertices = []
data_to_v = {}
for x in nxg.nodes():
vertex = Vertex(x)
vertices.append(vertex)
data_to_v[x] = vertex
E = [Edge(data_to_v[xy[0]], data_to_v[xy[1]], data = xy) for xy in nxg.edges()]
g = Graph(vertices, E)
return g
def to_networkx(g):
"""
Converts a sugiyama graph to a networkx graph.
"""
from networkx import MultiDiGraph
nxg = MultiDiGraph()
for v in g.vertices():
nxg.add_node(v.data)
for e in g.edges():
# todo: this leads to issues when the data is more than an id
nxg.add_edge(e.v[0].data, e.v[1].data)
return nxg
def draw_graph(g, layout_direction = 0, source_column = "source", target_column = "target", root=None):
"""
Renders the given graph after applying the layered layout.
:param g: Graphistry Graph or NetworkX Graph.
"""
if isinstance(g, nx.Graph):
gg = from_networkx(g)
nxg = g
elif isinstance(g, Graph):
gg = g
nxg = to_networkx(g)
elif isinstance(g, pd.DataFrame):
gg = SugiyamaLayout.graph_from_pandas(g, source_column = source_column, target_column = target_column)
nxg = to_networkx(gg)
else:
raise ValueError
# apply layout
positions = SugiyamaLayout.arrange(gg, layout_direction = layout_direction, root=root)
nx.draw(nxg, pos = positions, with_labels = True, verticalalignment = 'bottom', arrowsize = 3, horizontalalignment = "left", font_size = 20)
plt.show()
def scatter_graph(g, root=None):
"""
Renders the given graph as a scatter plot after applying the layered layout.
:param g: Graphistry Graph or NetworkX Graph.
"""
if isinstance(g, nx.Graph):
gg = from_networkx(g)
nxg = g
elif isinstance(g, Graph):
gg = g
nxg = to_networkx(g)
# apply layout
coords = list(SugiyamaLayout.arrange(gg, root=root).values())
x = [c[0] for c in coords]
y = [c[1] for c in coords]
fig, ax = plt.subplots()
ax.scatter(x, y)
for i, v in enumerate(gg.vertices()):
ax.annotate(v.data, (x[i], y[i]))
plt.axis('off')
plt.show()
def arrange(g, layout_direction = 0, source_column = "source", target_column = "target", topological_coordinates = False):
"""
Returns the positions of the given graph after applying the layered layout.
:param g: Graphistry Graph, Pandas frame or NetworkX Graph.
"""
if isinstance(g, nx.Graph):
gg = from_networkx(g)
nxg = g
elif isinstance(g, Graph):
gg = g
nxg = to_networkx(g)
elif isinstance(g, pd.DataFrame):
gg = SugiyamaLayout.graph_from_pandas(g, source_column = source_column, target_column = target_column)
nxg = to_networkx(gg)
else:
raise ValueError
# apply layout
positions = SugiyamaLayout.arrange(gg, layout_direction = layout_direction, topological_coordinates = topological_coordinates)
return positions
显式简单图#
Graph 对象可用于创建显式图:
[ ]:
matplotlib.rc('figure', figsize = [8, 5])
g = Graph()
bosons = Vertex("Boson")
higgs = Vertex("Higgs")
pions = Vertex("Pions")
kaons = Vertex("Kaons")
hadrons = Vertex("Hadrons")
e1 = Edge(bosons, higgs)
e2 = Edge(bosons, kaons)
e3 = Edge(bosons, pions)
e4 = Edge(pions, hadrons)
e5 = Edge(kaons, hadrons)
g.add_edges([e1, e2, e3, e4, e5])
scatter_graph(g)
Pandas 图表#
[ ]:
g = nx.generators.balanced_tree(2, 3)
df = nx.to_pandas_edgelist(g, "source", "target")
df.head()
[ ]:
matplotlib.rc('figure', figsize = [5, 5])
draw_graph(df, 3)
你可以像这样设置根目录
[ ]:
matplotlib.rc('figure', figsize = [5, 5])
draw_graph(df, 3, root=[3,12])
树#
一棵真正的树将按预期排列:
[ ]:
matplotlib.rc('figure', figsize = [120, 30])
g = nx.generators.balanced_tree(5, 3)
draw_graph(g, 2)
现实世界的图#
Barabasi-Albert graphs 代表模仿生物和其他现实世界网络的无标度网络:
[ ]:
matplotlib.rc('figure', figsize = [120, 90])
g = nx.generators.barabasi_albert_graph(500, 3)
draw_graph(g)
布局方向#
0: 从上到下
1: 从右到左
2: 从下到上
3: 从左到右
[ ]:
matplotlib.rc('figure', figsize = [10, 5])
g = nx.generators.balanced_tree(3, 2)
draw_graph(g, layout_direction = 2)
拓扑坐标#
除了绝对坐标外,您还可以请求拓扑坐标,这些坐标对应于垂直轴的层索引和水平轴的单位区间内的值。将这些值与实际的总宽度和高度相乘,可以得到给定(宽度,高度)矩形内的坐标。请注意,分层是根据标准坐标系进行的,即向上和向右。当将布局方向设置为水平(layout_direction等于1或3)时,第一个坐标是层索引,第二个坐标将在单位区间内。
[ ]:
g = nx.from_edgelist([(1,2),(1,3),(3,4)])
positions = arrange(g, topological_coordinates=True, layout_direction=0)
print(positions)
拓扑坐标也可以直接与NetworX一起使用:
[ ]:
nx.draw(g, pos = positions, with_labels = True, verticalalignment = 'bottom', arrowsize = 3, horizontalalignment = "left", font_size = 20)
完全图#
分层布局试图最小化交叉,但在像完全图这样的极端例子中,无论算法尝试调整多少次,交叉仍然存在。
[ ]:
matplotlib.rc('figure', figsize = [120, 90])
g = nx.generators.complete_graph(10)
draw_graph(g,root=4)
仅限职位#
你可以通过arrange方法获取节点的位置而不进行可视化。你也可以只绘制点而不绘制边,如下所示:
[ ]:
matplotlib.rc('figure', figsize = [20, 30])
g = nx.generators.random_lobster(100, 0.3, 0.3)
scatter_graph(g)
瓦茨-斯托加茨#
Watts-Strogatz 模型是另一种展示所谓小世界现象的现实世界图模型:
[ ]:
matplotlib.rc('figure', figsize = [120, 70])
g = nx.generators.connected_watts_strogatz_graph(1000, 2, 0.3)
draw_graph(g)
[ ]: