3

When I draw a networkx graph in a subplot, some of the nodes are partially cut off in the frame of the axes. I've tried this with all different types of graphs and layouts, it's always a problem. It always cuts off my nodes. It's as if networkx is drawing the graph on a bigger axes than is actually there.

Here is a minimal example

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()

This is what I get from that. As you can see, node 0 and node 7 do not fit in the frame.

Minimal example

chasmani
  • 2,362
  • 2
  • 23
  • 35
  • Which `networkx` and `matplotlib` you are working with? I tried your code on 2.2 networkx together with 3.0.2 matplotlib and could not reproduce your figure in any of the 10 tries. Same on networkx 2.4 together with matplotlib 3.1.3 – Sparky05 Apr 30 '20 at 09:57
  • @Sparky05 I'm using networkx 2.4 and matplotlib 3.2.1 – chasmani Apr 30 '20 at 10:12
  • After upgrading the matplotlib in my second environment (nx 2.4), I can now reproduce your error. Your issue is probably caused by the different [autoscaling](https://matplotlib.org/3.2.1/api/prev_api_changes/api_changes_3.2.0.html#autoscaling) of matplotlib introduced with version 3.2.0. – Sparky05 Apr 30 '20 at 11:02

3 Answers3

5

Background

Your issue seems to be caused by the new autoscaling algorithm introduced with matplotlib 3.2.0. In the link it states, that the old algorithm did

for Axes.scatter it would make the limits large enough to not clip any markers in the scatter.

Hence, the new algorithm has stopped to do this, which results in the cute nodes.

How to fix your problem

You can simply increase the length of your axis:

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()
Sparky05
  • 4,692
  • 1
  • 10
  • 27
2

Just playing a with the figure sizes should do the trick. Try setting a larger figure size through the subplots' figsize parameter:

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')

enter image description here


You can also look into networkX' layouts, such as spring_layout, which allow to encapsulate the nodes within a given box size, specified by a scale parameter. Here's an example:

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')

enter image description here

yatu
  • 86,083
  • 12
  • 84
  • 139
1

Since the other answers require the user to adjust some parameter manually in an iterative process, I wanted to add my own answer. It is automatic, but the current implementation only works if all node sizes are equal.

Node sizes are in points, therefore they don't scale with the image. Although this answer works programmatically, it doesn't work if you interactively change the window size of the figure. The first half of the fix_graph_scale function calculates the node radius in terms of the future x and y scale. The second half sets the axis scales such that they include all node positions plus half of the node size.

The get_ax_size function is from unutbu's answer with slight modifications.

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

kcoskun
  • 401
  • 3
  • 5