子图中的 networkx 正在绘制部分在轴框架之外的节点
Posted
技术标签:
【中文标题】子图中的 networkx 正在绘制部分在轴框架之外的节点【英文标题】:networkx in a subplot is drawing nodes partially outside of axes frame 【发布时间】:2021-10-09 09:28:37 【问题描述】:当我在子图中绘制 networkx 图时,一些节点在轴的框架中被部分切断。我已经尝试过使用所有不同类型的图形和布局,这总是一个问题。它总是切断我的节点。就好像 networkx 在比实际更大的轴上绘制图形。
这是一个最小的例子
plt.subplot(2, 1, 1)
plt.scatter(range(10), range(10))
plt.subplot(2, 1, 2)
G = nx.erdos_renyi_graph(20, p=0.1)
nx.draw_networkx(G)
plt.show()
这就是我从中得到的。如您所见,节点 0 和节点 7 不适合框架。
【问题讨论】:
您与哪个networkx
和matplotlib
合作?我在 2.2 networkx 和 3.0.2 matplotlib 上尝试了您的代码,但在 10 次尝试中都无法重现您的图形。在 networkx 2.4 和 matplotlib 3.1.3 上相同
@Sparky05 我正在使用 networkx 2.4 和 matplotlib 3.2.1
在我的第二个环境(nx 2.4)中升级 matplotlib 后,我现在可以重现您的错误。您的问题可能是由于版本 3.2.0 引入的 matplotlib 的不同 autoscaling 引起的。
【参考方案1】:
背景
您的问题似乎是由matplotlib
3.2.0 引入的新autoscaling 算法引起的。在链接中它指出,旧算法确实
对于 Axes.scatter,它会使限制足够大,不会在散点图中剪裁任何标记。
因此,新算法已停止执行此操作,从而产生可爱的节点。
如何解决您的问题
您可以简单地增加轴的长度:
import networkx as nx
import matplotlib.pylab as plt
figure = plt.subplot(2, 1, 1)
plt.scatter(range(10), range(10))
plt.subplot(2, 1, 2)
G = nx.erdos_renyi_graph(20, p=0.1)
nx.draw_networkx(G)
axis = plt.gca()
# maybe smaller factors work as well, but 1.1 works fine for this minimal example
axis.set_xlim([1.1*x for x in axis.get_xlim()])
axis.set_ylim([1.1*y for y in axis.get_ylim()])
plt.show()
【讨论】:
【参考方案2】:只需使用数字大小就可以解决问题。尝试通过子图的figsize
参数设置更大的图形大小:
f, axs = plt.subplots(2,1,figsize=(15,15))
axs[0].scatter(range(10), range(10))
G = nx.erdos_renyi_graph(20, p=0.1)
nx.draw_networkx(G, ax=axs[1], node_color='lightgreen')
您还可以查看 networkX 的布局,例如 spring_layout
,它允许将节点封装在由 scale
参数指定的给定框大小内。这是一个例子:
f, axs = plt.subplots(2,1,figsize=(15,15))
axs[0].scatter(range(10), range(10))
G = nx.erdos_renyi_graph(20, p=0.05)
pos = nx.spring_layout(G, k=0.7, scale=0.05)
nx.draw_networkx(G, pos=pos, ax=axs[1], node_color='lightgreen')
【讨论】:
【参考方案3】:由于其他答案需要用户在迭代过程中手动调整某些参数,所以我想添加自己的答案。它是自动的,但当前实现仅在所有节点大小相等时才有效。
节点大小以磅为单位,因此它们不会随图像缩放。尽管此答案以编程方式有效,但如果您以交互方式更改图形的窗口大小,则它不起作用。 fix_graph_scale
函数的前半部分根据未来的 x 和 y 比例计算节点半径。后半部分设置轴比例,使其包括所有节点位置加上节点大小的一半。
get_ax_size
函数来自 unutbu's answer,稍作修改。
import matplotlib.pyplot as plt
import networkx as nx
def get_ax_size(ax):
bbox = ax.get_window_extent().transformed(ax.figure.dpi_scale_trans.inverted())
width, height = bbox.width, bbox.height
width *= 72
height *= 72
return width, height
def fix_graph_scale(ax,pos,node_size = 300):
node_radius = (node_size / 3.14159265359)**0.5
min_x = min(i_pos[0] for i_pos in pos.values())
max_x = max(i_pos[0] for i_pos in pos.values())
min_y = min(i_pos[1] for i_pos in pos.values())
max_y = max(i_pos[1] for i_pos in pos.values())
ax_size_x, ax_size_y = get_ax_size(ax)
points_to_x_axis = (max_x - min_x)/(ax_size_x-node_radius*2)
points_to_y_axis = (max_y - min_y)/(ax_size_y-node_radius*2)
node_radius_in_x_axis = node_radius * points_to_x_axis
node_radius_in_y_axis = node_radius * points_to_y_axis
ax_min_x = min_x - node_radius_in_x_axis
ax_max_x = max_x + node_radius_in_x_axis
ax_min_y = min_y - node_radius_in_y_axis
ax_max_y = max_y + node_radius_in_y_axis
ax.set_xlim([ax_min_x, ax_max_x])
ax.set_ylim([ax_min_y, ax_max_y])
fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.scatter(range(10), range(10))
G = nx.erdos_renyi_graph(20, p=0.1)
pos = nx.drawing.spring_layout(G)
nx.draw_networkx(G,pos,ax=ax2)
default_node_size = 300
fix_graph_scale(ax2,pos,node_size = default_node_size)
plt.show()
Sample result
【讨论】:
以上是关于子图中的 networkx 正在绘制部分在轴框架之外的节点的主要内容,如果未能解决你的问题,请参考以下文章
python 使用networkx绘制带权无向图和带权有向图,以及标注特定路径
读书笔记3|使用Python,networkx对卡勒德胡赛尼三部曲之——《群山回唱》人物关系图谱绘制