如何使用 matplotlib 和 networkx 开发矢量化可导航图
How to develop a vectorized navigable graph using matplotlib and networkx
我想生成一个很长的数字层次结构图表,但生成的图表是像素化的,无法缩放以显示其详细信息。我正在使用 networkx 和 matplotlib 生成此图表。
这是我要生成层次结构的数组。
tree = [(1, 2), (2, 4), (4, 8), (8, 16), (16, 5), (5, 10), (10, 3), (3, 6), (10, 20), (20, 40), (40, 13), (13, 26), (26, 52), (52, 17), (17, 34), (34, 11), (11, 22), (22, 7), (7, 14), (14, 28), (28, 9), (6, 12), (40, 80), (80, 160), (160, 53), (53, 106), (106, 35), (35, 70), (70, 23), (23, 46), (46, 15), (9, 18), (22, 44), (44, 88), (88, 29), (29, 58), (58, 19), (16, 32), (32, 64), (64, 21), (12, 24), (19, 38), (38, 76), (76, 25), (46, 92), (92, 184), (184, 61), (61, 122), (122, 244), (244, 488), (488, 976), (976, 325), (325, 650), (650, 1300), (1300, 433), (433, 866), (866, 1732), (1732, 577), (577, 1154), (1154, 2308), (2308, 4616), (4616, 9232), (9232, 3077), (3077, 6154), (6154, 2051), (2051, 4102), (4102, 1367), (1367, 2734), (2734, 911), (911, 1822), (1822, 3644), (3644, 7288), (7288, 2429), (2429, 4858), (4858, 1619), (1619, 3238), (3238, 1079), (1079, 2158), (2158, 719), (719, 1438), (1438, 479), (479, 958), (958, 319), (319, 638), (638, 1276), (1276, 425), (425, 850), (850, 283), (283, 566), (566, 1132), (1132, 377), (377, 754), (754, 251), (251, 502), (502, 167), (167, 334), (334, 668), (668, 1336), (1336, 445), (445, 890), (890, 1780), (1780, 593), (593, 1186), (1186, 395), (395, 790), (790, 263), (263, 526), (526, 175), (175, 350), (350, 700), (700, 233), (233, 466), (466, 155), (155, 310), (310, 103), (103, 206), (206, 412), (412, 137), (137, 274), (274, 91), (91, 182), (182, 364), (364, 121), (121, 242), (242, 484), (484, 161), (161, 322), (322, 107), (107, 214), (214, 71), (71, 142), (142, 47), (47, 94), (94, 31), (31, 62), (62, 124), (124, 41), (41, 82), (82, 27), (15, 30), (25, 50), (50, 100), (100, 33)]
图生成如下:
G=nx.Graph()
G.add_edges_from(tree)
pos = tg.hierarchy_pos(G,1)
nx.draw(G, pos=pos, with_labels=True)
生成的层次结构如下所示:enter image description here
如图所示,该图是像素化图像,无法缩放以显示细节。如何生成此图的矢量化版本。
hierarchy_pos函数取自
实际上有两个不同的问题:
- nodes/labels 的位置重叠,因为图在默认 node/text 尺寸下不够大。
- 图像需要以矢量化格式保存。
下面的代码片段调整了绘图大小并保存了矢量化图像,但是包含 hierarchy_pos
定义的完整代码被复制到 post 的底部,用于 post真实。
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(20, 50)) # increase figsize
nx.draw(G, pos=pos, with_labels=True)
fig.savefig('filename.eps', format='eps') #save vectorized image
这里是 .jpg 只是为了显示布局差异。 Imgur(Whosebug 的图像托管提供商)不支持矢量化图像,所以我无法在此处上传。
完整的可复制粘贴代码:
import networkx as nx
import random
import matplotlib.pyplot as plt
def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
'''
From Joel's answer at
Licensed under Creative Commons Attribution-Share Alike
If the graph is a tree this will return the positions to plot this in a
hierarchical layout.
G: the graph (must be a tree)
root: the root node of current branch
- if the tree is directed and this is not given,
the root will be found and used
- if the tree is directed and this is given, then
the positions will be just for the descendants of this node.
- if the tree is undirected and not given,
then a random choice will be used.
width: horizontal space allocated for this branch - avoids overlap with other branches
vert_gap: gap between levels of hierarchy
vert_loc: vertical location of root
xcenter: horizontal location of root
'''
if not nx.is_tree(G):
raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
if root is None:
if isinstance(G, nx.DiGraph):
root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11
else:
root = random.choice(list(G.nodes))
def _hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, pos = None, parent = None):
'''
see hierarchy_pos docstring for most arguments
pos: a dict saying where all nodes go if they have been assigned
parent: parent of this branch. - only affects it if non-directed
'''
if pos is None:
pos = {root:(xcenter,vert_loc)}
else:
pos[root] = (xcenter, vert_loc)
children = list(G.neighbors(root))
if not isinstance(G, nx.DiGraph) and parent is not None:
children.remove(parent)
if len(children)!=0:
dx = width/len(children)
nextx = xcenter - width/2 - dx/2
for child in children:
nextx += dx
pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap,
vert_loc = vert_loc-vert_gap, xcenter=nextx,
pos=pos, parent = root)
return pos
return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
tree = [(1, 2), (2, 4), (4, 8), (8, 16), (16, 5), (5, 10), (10, 3), (3, 6), (10, 20), (20, 40), (40, 13), (13, 26), (26, 52), (52, 17), (17, 34), (34, 11), (11, 22), (22, 7), (7, 14), (14, 28), (28, 9), (6, 12), (40, 80), (80, 160), (160, 53), (53, 106), (106, 35), (35, 70), (70, 23), (23, 46), (46, 15), (9, 18), (22, 44), (44, 88), (88, 29), (29, 58), (58, 19), (16, 32), (32, 64), (64, 21), (12, 24), (19, 38), (38, 76), (76, 25), (46, 92), (92, 184), (184, 61), (61, 122), (122, 244), (244, 488), (488, 976), (976, 325), (325, 650), (650, 1300), (1300, 433), (433, 866), (866, 1732), (1732, 577), (577, 1154), (1154, 2308), (2308, 4616), (4616, 9232), (9232, 3077), (3077, 6154), (6154, 2051), (2051, 4102), (4102, 1367), (1367, 2734), (2734, 911), (911, 1822), (1822, 3644), (3644, 7288), (7288, 2429), (2429, 4858), (4858, 1619), (1619, 3238), (3238, 1079), (1079, 2158), (2158, 719), (719, 1438), (1438, 479), (479, 958), (958, 319), (319, 638), (638, 1276), (1276, 425), (425, 850), (850, 283), (283, 566), (566, 1132), (1132, 377), (377, 754), (754, 251), (251, 502), (502, 167), (167, 334), (334, 668), (668, 1336), (1336, 445), (445, 890), (890, 1780), (1780, 593), (593, 1186), (1186, 395), (395, 790), (790, 263), (263, 526), (526, 175), (175, 350), (350, 700), (700, 233), (233, 466), (466, 155), (155, 310), (310, 103), (103, 206), (206, 412), (412, 137), (137, 274), (274, 91), (91, 182), (182, 364), (364, 121), (121, 242), (242, 484), (484, 161), (161, 322), (322, 107), (107, 214), (214, 71), (71, 142), (142, 47), (47, 94), (94, 31), (31, 62), (62, 124), (124, 41), (41, 82), (82, 27), (15, 30), (25, 50), (50, 100), (100, 33)]
G=nx.Graph()
G.add_edges_from(tree)
pos = hierarchy_pos(G,1)
fig = plt.figure(figsize=(20, 50)) # increase figsize
nx.draw(G, pos=pos, with_labels=True)
fig.savefig('filename.eps', format='eps') #save vectorized image
我想生成一个很长的数字层次结构图表,但生成的图表是像素化的,无法缩放以显示其详细信息。我正在使用 networkx 和 matplotlib 生成此图表。 这是我要生成层次结构的数组。
tree = [(1, 2), (2, 4), (4, 8), (8, 16), (16, 5), (5, 10), (10, 3), (3, 6), (10, 20), (20, 40), (40, 13), (13, 26), (26, 52), (52, 17), (17, 34), (34, 11), (11, 22), (22, 7), (7, 14), (14, 28), (28, 9), (6, 12), (40, 80), (80, 160), (160, 53), (53, 106), (106, 35), (35, 70), (70, 23), (23, 46), (46, 15), (9, 18), (22, 44), (44, 88), (88, 29), (29, 58), (58, 19), (16, 32), (32, 64), (64, 21), (12, 24), (19, 38), (38, 76), (76, 25), (46, 92), (92, 184), (184, 61), (61, 122), (122, 244), (244, 488), (488, 976), (976, 325), (325, 650), (650, 1300), (1300, 433), (433, 866), (866, 1732), (1732, 577), (577, 1154), (1154, 2308), (2308, 4616), (4616, 9232), (9232, 3077), (3077, 6154), (6154, 2051), (2051, 4102), (4102, 1367), (1367, 2734), (2734, 911), (911, 1822), (1822, 3644), (3644, 7288), (7288, 2429), (2429, 4858), (4858, 1619), (1619, 3238), (3238, 1079), (1079, 2158), (2158, 719), (719, 1438), (1438, 479), (479, 958), (958, 319), (319, 638), (638, 1276), (1276, 425), (425, 850), (850, 283), (283, 566), (566, 1132), (1132, 377), (377, 754), (754, 251), (251, 502), (502, 167), (167, 334), (334, 668), (668, 1336), (1336, 445), (445, 890), (890, 1780), (1780, 593), (593, 1186), (1186, 395), (395, 790), (790, 263), (263, 526), (526, 175), (175, 350), (350, 700), (700, 233), (233, 466), (466, 155), (155, 310), (310, 103), (103, 206), (206, 412), (412, 137), (137, 274), (274, 91), (91, 182), (182, 364), (364, 121), (121, 242), (242, 484), (484, 161), (161, 322), (322, 107), (107, 214), (214, 71), (71, 142), (142, 47), (47, 94), (94, 31), (31, 62), (62, 124), (124, 41), (41, 82), (82, 27), (15, 30), (25, 50), (50, 100), (100, 33)]
图生成如下:
G=nx.Graph()
G.add_edges_from(tree)
pos = tg.hierarchy_pos(G,1)
nx.draw(G, pos=pos, with_labels=True)
生成的层次结构如下所示:enter image description here 如图所示,该图是像素化图像,无法缩放以显示细节。如何生成此图的矢量化版本。
hierarchy_pos函数取自
实际上有两个不同的问题:
- nodes/labels 的位置重叠,因为图在默认 node/text 尺寸下不够大。
- 图像需要以矢量化格式保存。
下面的代码片段调整了绘图大小并保存了矢量化图像,但是包含 hierarchy_pos
定义的完整代码被复制到 post 的底部,用于 post真实。
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(20, 50)) # increase figsize
nx.draw(G, pos=pos, with_labels=True)
fig.savefig('filename.eps', format='eps') #save vectorized image
这里是 .jpg 只是为了显示布局差异。 Imgur(Whosebug 的图像托管提供商)不支持矢量化图像,所以我无法在此处上传。
完整的可复制粘贴代码:
import networkx as nx
import random
import matplotlib.pyplot as plt
def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
'''
From Joel's answer at
Licensed under Creative Commons Attribution-Share Alike
If the graph is a tree this will return the positions to plot this in a
hierarchical layout.
G: the graph (must be a tree)
root: the root node of current branch
- if the tree is directed and this is not given,
the root will be found and used
- if the tree is directed and this is given, then
the positions will be just for the descendants of this node.
- if the tree is undirected and not given,
then a random choice will be used.
width: horizontal space allocated for this branch - avoids overlap with other branches
vert_gap: gap between levels of hierarchy
vert_loc: vertical location of root
xcenter: horizontal location of root
'''
if not nx.is_tree(G):
raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
if root is None:
if isinstance(G, nx.DiGraph):
root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11
else:
root = random.choice(list(G.nodes))
def _hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, pos = None, parent = None):
'''
see hierarchy_pos docstring for most arguments
pos: a dict saying where all nodes go if they have been assigned
parent: parent of this branch. - only affects it if non-directed
'''
if pos is None:
pos = {root:(xcenter,vert_loc)}
else:
pos[root] = (xcenter, vert_loc)
children = list(G.neighbors(root))
if not isinstance(G, nx.DiGraph) and parent is not None:
children.remove(parent)
if len(children)!=0:
dx = width/len(children)
nextx = xcenter - width/2 - dx/2
for child in children:
nextx += dx
pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap,
vert_loc = vert_loc-vert_gap, xcenter=nextx,
pos=pos, parent = root)
return pos
return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
tree = [(1, 2), (2, 4), (4, 8), (8, 16), (16, 5), (5, 10), (10, 3), (3, 6), (10, 20), (20, 40), (40, 13), (13, 26), (26, 52), (52, 17), (17, 34), (34, 11), (11, 22), (22, 7), (7, 14), (14, 28), (28, 9), (6, 12), (40, 80), (80, 160), (160, 53), (53, 106), (106, 35), (35, 70), (70, 23), (23, 46), (46, 15), (9, 18), (22, 44), (44, 88), (88, 29), (29, 58), (58, 19), (16, 32), (32, 64), (64, 21), (12, 24), (19, 38), (38, 76), (76, 25), (46, 92), (92, 184), (184, 61), (61, 122), (122, 244), (244, 488), (488, 976), (976, 325), (325, 650), (650, 1300), (1300, 433), (433, 866), (866, 1732), (1732, 577), (577, 1154), (1154, 2308), (2308, 4616), (4616, 9232), (9232, 3077), (3077, 6154), (6154, 2051), (2051, 4102), (4102, 1367), (1367, 2734), (2734, 911), (911, 1822), (1822, 3644), (3644, 7288), (7288, 2429), (2429, 4858), (4858, 1619), (1619, 3238), (3238, 1079), (1079, 2158), (2158, 719), (719, 1438), (1438, 479), (479, 958), (958, 319), (319, 638), (638, 1276), (1276, 425), (425, 850), (850, 283), (283, 566), (566, 1132), (1132, 377), (377, 754), (754, 251), (251, 502), (502, 167), (167, 334), (334, 668), (668, 1336), (1336, 445), (445, 890), (890, 1780), (1780, 593), (593, 1186), (1186, 395), (395, 790), (790, 263), (263, 526), (526, 175), (175, 350), (350, 700), (700, 233), (233, 466), (466, 155), (155, 310), (310, 103), (103, 206), (206, 412), (412, 137), (137, 274), (274, 91), (91, 182), (182, 364), (364, 121), (121, 242), (242, 484), (484, 161), (161, 322), (322, 107), (107, 214), (214, 71), (71, 142), (142, 47), (47, 94), (94, 31), (31, 62), (62, 124), (124, 41), (41, 82), (82, 27), (15, 30), (25, 50), (50, 100), (100, 33)]
G=nx.Graph()
G.add_edges_from(tree)
pos = hierarchy_pos(G,1)
fig = plt.figure(figsize=(20, 50)) # increase figsize
nx.draw(G, pos=pos, with_labels=True)
fig.savefig('filename.eps', format='eps') #save vectorized image