如何在Python中使用networkx和matplotlib在同一图中绘制不同形状的节点和bbox的面色?
How to plot nodes of different shapes and facecolor of bbox in the same graph using networkx and matplotlib in Python?
我想使用 networkx 绘制一个简单的图形。有两个带有标签 Python 和 Programming 的节点。代码和图表如下:
import networkx as nx
G = nx.DiGraph()
G.add_node(0)
G.add_node(1)
G.add_edge(0,1)
nx.draw_networkx(G,
pos = {0:(0, 0), 1: (1, 0)},
node_size = [1000, 2000],
node_color = ["red","green"],
node_shape = "s",
)
我可以在两个节点中分别得到红色和绿色两种不同的颜色,并在每个节点中设置不同的标签。但是,我也想获得两种不同形状的节点。我想要原样的第一个节点和菱形的第二个节点,可以使用 node_shape
中的 d
获得。我尝试以 ["s","d"]
的形式传递列表,并在 node_shape
.
中以 {0:"s", 1:"d"}
的形式传递字典
但是,两者return都出错了。我怎样才能在这里得到不同形状的节点?是否可以使用任何其他库,如 graphviz,如何?
在另一步中,我希望将标签包含在节点中。所以我将 node_size 设置为 0 并使用 bbox,如下所示。
nx.draw_networkx(G,
pos = {0:(0, 0), 1: (1, 0)},
node_size = 0,
arrows = True,
labels = {0:"Python",1:"Programming"},
bbox = dict(facecolor = "skyblue")
)
我可以将标签包含在 bbox 中,但是箭头没有隐藏,并且两个节点中 bbox 的面色相同。在这种情况下,如何使面部颜色不同?解决这个问题的替代方法是什么?
Networkx 不支持在一个函数调用中使用多个节点形状,因为节点是使用 matplotlib 的 scatter
绘制的,不支持在同一个函数调用中绘制不同的标记。但是,您可以多次调用 nx.draw_networkx_nodes
,每个调用具有不同的节点和形状子集。
在节点中包含标签是一件棘手的事情。 matplotlib 中的文本框尺寸不能为 pre-calculated,因为它支持多个渲染器。因此,您必须绘制一些虚拟文本对象,确定文本框的尺寸,然后逆向调整为所需的尺寸。 networkx 不包含此类功能。
如果您愿意使用其他库,那么您所需的大部分功能都可以在 netgraph
中轻松实现,这是我编写和维护的库。
支持在一个函数调用中绘制不同的节点形状。箭头应保持可见。更改节点标签背景颜色相当容易。
关于字体大小,在netgraph中,如果没有明确设置节点标签的字体大小,它会计算出最大字体大小,使所有文本框都适合相应的节点艺术家。正如您在下面的示例中看到的那样,该计算存在一个小问题,因为当前的实现假设一个圆形节点形状,因此第一个节点的文本框不太适合方形节点。我会尽快解决这个问题;与此同时,您将不得不使用更多的圆形节点形状,或者在单独的调用中调整字体大小,如下所示。
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
from netgraph import Graph # pip install netgraph
graph_data = nx.DiGraph([(0, 1)]) # edge lists or igraph Graph objects are also supported
g = Graph(graph_data,
node_layout = {0: (0, 0), 1: (1, 0)},
node_size = {0 : 10, 1 : 20},
node_color = {0 : "red", 1 : "green"},
node_labels = {0:"Python", 1:"Programming"},
node_label_fontdict = {'backgroundcolor' : 'lightgray'},
node_shape = {0 : "s", 1 : "d"},
arrows=True,
)
# Netgraph currently does not support multiple values for label backgroundcolors.
# However, all artists are exposed in simple to query dictionaries.
# As node label artists are matplotlib text objects,
# we can vary the node label background colors by using matplotlib.text.Text methods:
g.node_label_artists[0].set_backgroundcolor('salmon')
g.node_label_artists[1].set_backgroundcolor('lightgreen')
# Netgraph assumes circular node shapes when computing fontsizes.
# We hence have to manually adjust the node label fontsizes
# by the ratio of the diagonal to the width in a square.
for node, label in g.node_label_artists.items():
fontsize = label.get_fontsize()
label.set_fontsize(fontsize * 1./np.sqrt(2))
plt.show()
我找到了一种使用 graphviz 包的方法。
NetworkX 包的局限性在于它不允许节点具有不同的形状,尽管可以以列表的形式设置不同的大小或颜色。另外,如果使用边界框(bbox),那么它的属性在节点之间是统一的。
但是,graphviz 在这种情况下提供了更多的灵活性。每个节点的形状、大小和颜色都可以单独调整。
我是这样做的:
import graphviz
G = graphviz.Digraph()
G.node("Python", style = 'filled', color = "red", shape = "box")
G.node("Programming", style = "filled", color = "green", shape = "ellipse")
G.edge("Python","Programming")
G.view()
结果,我得到了如图所示的情节:
也可以进一步自定义图形,例如节点的位置、边的颜色等
我正在测试 Python 库,这些库允许以更直观和用户友好的方式绘制图表。我考虑了标记节点的正确连接、不同样式的标签、可以用鼠标拖动的元素以及其他类型的交互性。 netgraph
在大多数这些功能中看起来很有前途,但不幸的是它不支持矩形节点和边缘末端位置的动态偏移,这对于能够以所需方式显示有向边缘至关重要。事实上,节点和边在 netgraph
中是 matplotlib path objects and labels are matplotlib text objects,这意味着即使您重写它的任何方法,矩形文本标签和文本包围的正确连接也会变得复杂。
另一方面,如果您使用 matplotlib annotations 连接节点标签,这些问题可以在 networkx
中解决。在这种情况下,您只需要 networkx.Graph
和注释其节点的自定义艺术家:
import matplotlib.pyplot as plt
import networkx as nx
G = nx.DiGraph()
G.add_edges_from([(0, 1)])
G.add_nodes_from([0, 1])
pos = {0:(0.1, 0.9), 1: (0.1, 0.1)}
fig, ax = plt.subplots()
annotations = {0: ax.annotate('Python', xy=pos[0], xycoords='data', size=20,
ha="center", va="center", bbox=dict(facecolor = "blue")),
1:ax.annotate('programming', xy=pos[1], xycoords='data',
ha="center", va="center", bbox=dict(facecolor = "red"))}
for A, B in G.edges:
ax.annotate("", xy=pos[B], xycoords='data', xytext=pos[A], textcoords='data',
arrowprops=dict(arrowstyle="->", color="0.5", # shrinkA=85, shrinkB=85,
patchA=annotations[A],
patchB=annotations[B],
connectionstyle='arc3'))
plt.axis('off')
plt.show()
另一个灵活性
此外,matplotlib
允许更多花哨的节点标记样式:
import matplotlib.pyplot as plt
from matplotlib.offsetbox import AnnotationBbox, TextArea, VPacker
import networkx as nx
G = nx.DiGraph()
G.add_edges_from([(0,1), (1,2), (0, 2)])
G.add_nodes_from([0, 1, 2])
pos = {0:(0.1, 0.9), 1: (0.1, 0.1), 2: (0.9, 0.5)}
fig, ax = plt.subplots()
textobjs = [TextArea('IS', textprops=dict(color="blue", size=18, ha='left',va='baseline')),
TextArea('FUN!', textprops=dict(color="red", size=18, ha='left',va='baseline'))]
ybox = VPacker(children=textobjs, pad=0, sep=0)
anbox = AnnotationBbox(ybox, pos[2], frameon=True, box_alignment=(0,.5),
bboxprops=dict(facecolor='yellow', boxstyle='round'))
annotations = {0: ax.annotate('Python', xy=pos[0], xycoords='data',
ha="center", va="center", bbox=dict(facecolor = "blue")),
1:ax.annotate('programming', xy=pos[1], xycoords='data',
ha="center", va="center", bbox=dict(facecolor = "red")),
2:ax.add_artist(anbox)}
annotations[2].draggable()
for A, B in G.edges:
ax.annotate("", xy=pos[B], xycoords='data', xytext=pos[A], textcoords='data',
arrowprops=dict(arrowstyle="->", color="0.5", # shrinkA=85, shrinkB=85,
patchA=annotations[A],
patchB=annotations[B],
connectionstyle='arc3'))
plt.axis('off')
plt.show()
我想使用 networkx 绘制一个简单的图形。有两个带有标签 Python 和 Programming 的节点。代码和图表如下:
import networkx as nx
G = nx.DiGraph()
G.add_node(0)
G.add_node(1)
G.add_edge(0,1)
nx.draw_networkx(G,
pos = {0:(0, 0), 1: (1, 0)},
node_size = [1000, 2000],
node_color = ["red","green"],
node_shape = "s",
)
我可以在两个节点中分别得到红色和绿色两种不同的颜色,并在每个节点中设置不同的标签。但是,我也想获得两种不同形状的节点。我想要原样的第一个节点和菱形的第二个节点,可以使用 node_shape
中的 d
获得。我尝试以 ["s","d"]
的形式传递列表,并在 node_shape
.
{0:"s", 1:"d"}
的形式传递字典
但是,两者return都出错了。我怎样才能在这里得到不同形状的节点?是否可以使用任何其他库,如 graphviz,如何?
在另一步中,我希望将标签包含在节点中。所以我将 node_size 设置为 0 并使用 bbox,如下所示。
nx.draw_networkx(G,
pos = {0:(0, 0), 1: (1, 0)},
node_size = 0,
arrows = True,
labels = {0:"Python",1:"Programming"},
bbox = dict(facecolor = "skyblue")
)
我可以将标签包含在 bbox 中,但是箭头没有隐藏,并且两个节点中 bbox 的面色相同。在这种情况下,如何使面部颜色不同?解决这个问题的替代方法是什么?
Networkx 不支持在一个函数调用中使用多个节点形状,因为节点是使用 matplotlib 的 scatter
绘制的,不支持在同一个函数调用中绘制不同的标记。但是,您可以多次调用 nx.draw_networkx_nodes
,每个调用具有不同的节点和形状子集。
在节点中包含标签是一件棘手的事情。 matplotlib 中的文本框尺寸不能为 pre-calculated,因为它支持多个渲染器。因此,您必须绘制一些虚拟文本对象,确定文本框的尺寸,然后逆向调整为所需的尺寸。 networkx 不包含此类功能。
如果您愿意使用其他库,那么您所需的大部分功能都可以在 netgraph
中轻松实现,这是我编写和维护的库。
支持在一个函数调用中绘制不同的节点形状。箭头应保持可见。更改节点标签背景颜色相当容易。
关于字体大小,在netgraph中,如果没有明确设置节点标签的字体大小,它会计算出最大字体大小,使所有文本框都适合相应的节点艺术家。正如您在下面的示例中看到的那样,该计算存在一个小问题,因为当前的实现假设一个圆形节点形状,因此第一个节点的文本框不太适合方形节点。我会尽快解决这个问题;与此同时,您将不得不使用更多的圆形节点形状,或者在单独的调用中调整字体大小,如下所示。
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
from netgraph import Graph # pip install netgraph
graph_data = nx.DiGraph([(0, 1)]) # edge lists or igraph Graph objects are also supported
g = Graph(graph_data,
node_layout = {0: (0, 0), 1: (1, 0)},
node_size = {0 : 10, 1 : 20},
node_color = {0 : "red", 1 : "green"},
node_labels = {0:"Python", 1:"Programming"},
node_label_fontdict = {'backgroundcolor' : 'lightgray'},
node_shape = {0 : "s", 1 : "d"},
arrows=True,
)
# Netgraph currently does not support multiple values for label backgroundcolors.
# However, all artists are exposed in simple to query dictionaries.
# As node label artists are matplotlib text objects,
# we can vary the node label background colors by using matplotlib.text.Text methods:
g.node_label_artists[0].set_backgroundcolor('salmon')
g.node_label_artists[1].set_backgroundcolor('lightgreen')
# Netgraph assumes circular node shapes when computing fontsizes.
# We hence have to manually adjust the node label fontsizes
# by the ratio of the diagonal to the width in a square.
for node, label in g.node_label_artists.items():
fontsize = label.get_fontsize()
label.set_fontsize(fontsize * 1./np.sqrt(2))
plt.show()
我找到了一种使用 graphviz 包的方法。 NetworkX 包的局限性在于它不允许节点具有不同的形状,尽管可以以列表的形式设置不同的大小或颜色。另外,如果使用边界框(bbox),那么它的属性在节点之间是统一的。
但是,graphviz 在这种情况下提供了更多的灵活性。每个节点的形状、大小和颜色都可以单独调整。 我是这样做的:
import graphviz
G = graphviz.Digraph()
G.node("Python", style = 'filled', color = "red", shape = "box")
G.node("Programming", style = "filled", color = "green", shape = "ellipse")
G.edge("Python","Programming")
G.view()
结果,我得到了如图所示的情节:
我正在测试 Python 库,这些库允许以更直观和用户友好的方式绘制图表。我考虑了标记节点的正确连接、不同样式的标签、可以用鼠标拖动的元素以及其他类型的交互性。 netgraph
在大多数这些功能中看起来很有前途,但不幸的是它不支持矩形节点和边缘末端位置的动态偏移,这对于能够以所需方式显示有向边缘至关重要。事实上,节点和边在 netgraph
中是 matplotlib path objects and labels are matplotlib text objects,这意味着即使您重写它的任何方法,矩形文本标签和文本包围的正确连接也会变得复杂。
另一方面,如果您使用 matplotlib annotations 连接节点标签,这些问题可以在 networkx
中解决。在这种情况下,您只需要 networkx.Graph
和注释其节点的自定义艺术家:
import matplotlib.pyplot as plt
import networkx as nx
G = nx.DiGraph()
G.add_edges_from([(0, 1)])
G.add_nodes_from([0, 1])
pos = {0:(0.1, 0.9), 1: (0.1, 0.1)}
fig, ax = plt.subplots()
annotations = {0: ax.annotate('Python', xy=pos[0], xycoords='data', size=20,
ha="center", va="center", bbox=dict(facecolor = "blue")),
1:ax.annotate('programming', xy=pos[1], xycoords='data',
ha="center", va="center", bbox=dict(facecolor = "red"))}
for A, B in G.edges:
ax.annotate("", xy=pos[B], xycoords='data', xytext=pos[A], textcoords='data',
arrowprops=dict(arrowstyle="->", color="0.5", # shrinkA=85, shrinkB=85,
patchA=annotations[A],
patchB=annotations[B],
connectionstyle='arc3'))
plt.axis('off')
plt.show()
另一个灵活性
此外,matplotlib
允许更多花哨的节点标记样式:
import matplotlib.pyplot as plt
from matplotlib.offsetbox import AnnotationBbox, TextArea, VPacker
import networkx as nx
G = nx.DiGraph()
G.add_edges_from([(0,1), (1,2), (0, 2)])
G.add_nodes_from([0, 1, 2])
pos = {0:(0.1, 0.9), 1: (0.1, 0.1), 2: (0.9, 0.5)}
fig, ax = plt.subplots()
textobjs = [TextArea('IS', textprops=dict(color="blue", size=18, ha='left',va='baseline')),
TextArea('FUN!', textprops=dict(color="red", size=18, ha='left',va='baseline'))]
ybox = VPacker(children=textobjs, pad=0, sep=0)
anbox = AnnotationBbox(ybox, pos[2], frameon=True, box_alignment=(0,.5),
bboxprops=dict(facecolor='yellow', boxstyle='round'))
annotations = {0: ax.annotate('Python', xy=pos[0], xycoords='data',
ha="center", va="center", bbox=dict(facecolor = "blue")),
1:ax.annotate('programming', xy=pos[1], xycoords='data',
ha="center", va="center", bbox=dict(facecolor = "red")),
2:ax.add_artist(anbox)}
annotations[2].draggable()
for A, B in G.edges:
ax.annotate("", xy=pos[B], xycoords='data', xytext=pos[A], textcoords='data',
arrowprops=dict(arrowstyle="->", color="0.5", # shrinkA=85, shrinkB=85,
patchA=annotations[A],
patchB=annotations[B],
connectionstyle='arc3'))
plt.axis('off')
plt.show()