子图中的 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 不适合框架。

【问题讨论】:

您与哪个networkxmatplotlib 合作?我在 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绘制带权无向图和带权有向图,以及标注特定路径

从 networkx 绘制以底图位置为中心的图形

读书笔记3|使用Python,networkx对卡勒德胡赛尼三部曲之——《群山回唱》人物关系图谱绘制

networkx draw_network 在绘图周围绘制一个框架。我怎样才能防止这种情况?

有没有办法保证 NetworkX 的分层输出?

seaborn 没有在定义的子图中绘制