Plot node size as legend using Networkx
Question:
While working with NetworkX, I managed to plot a graph that shows the node size corresponding to a node attribute. To produce a consistent plot, I want to show the node size in the legend.
Is there a pre-implemented way to add a legend with a defined number (e.g., four) of node sizes with the corresponding node attribute?
I imagine something similar to the appended legend.
Answers:
I am unsure of a built-in way to do this, but networkx
plotting algorithm uses scatter
to set node size so you can create a set of ghost nodes using scatter that are used in the legend. (I am making up the term ghost because you don’t actually see them. There may be an officially accepted term, I don’t know.)
For some reason I am could not get these to work with scatter
so I am using plot
instead. (Note that the size of values in scatter
follows area while plot
follows width as discussed here so the the size of the ghost values used in plot
are the square-root of the sizes generated by networkx.draw_networkx
.
from math import sqrt
import networkx as nx
import matplotlib.pyplot as plt
# Create graph
G = nx.Graph()
N = 10 # number of nodes
for n in range(1,N + 1):
G.add_node(n, size = n * 100, pos = [0, n]) # size of node based on its number
# Draw graph
node_sizes = nx.get_node_attributes(G, 'size')
nx.draw_networkx(G, node_color = 'b', node_size = [v for v in node_sizes.values()])
# Make legend
for n in [2, 4, 6, 8]:
plt.plot([], [], 'bo', markersize = sqrt(n*100), label = f"{n}")
plt.legend(labelspacing = 5, loc='center left', bbox_to_anchor=(1, 0.5), frameon = False)
I have a bit of a hacky, alternative way of doing this, using scatter legend_items
, basically making a scatter plot with your node sizes, copying the legend, then removing the scatter plot again. Probably neater way to to do this but it works.
import matplotlib.pyplot as plt
import networkx as nx
from scipy.stats import expon
# some random graph
G = nx.erdos_renyi_graph(10, 0.5)
# some attribute that will influence node size
attribute = [70*i for i in range(10)]
# helper method for getting legend
def get_legend(sizes, num=10):
fig, ax = plt.subplots()
sc = ax.scatter([0]*len(sizes), [0]*len(sizes), s=sizes, color='blue')
store = sc.legend_elements("sizes", num=num)
fig.clf()
return store
fig, ax = plt.subplots()
nx.draw_networkx(G, node_size=attribute, ax=ax)
ax.legend(*get_legend(attribute, num=10), title="Variable A")
While working with NetworkX, I managed to plot a graph that shows the node size corresponding to a node attribute. To produce a consistent plot, I want to show the node size in the legend.
Is there a pre-implemented way to add a legend with a defined number (e.g., four) of node sizes with the corresponding node attribute?
I imagine something similar to the appended legend.
I am unsure of a built-in way to do this, but networkx
plotting algorithm uses scatter
to set node size so you can create a set of ghost nodes using scatter that are used in the legend. (I am making up the term ghost because you don’t actually see them. There may be an officially accepted term, I don’t know.)
For some reason I am could not get these to work with scatter
so I am using plot
instead. (Note that the size of values in scatter
follows area while plot
follows width as discussed here so the the size of the ghost values used in plot
are the square-root of the sizes generated by networkx.draw_networkx
.
from math import sqrt
import networkx as nx
import matplotlib.pyplot as plt
# Create graph
G = nx.Graph()
N = 10 # number of nodes
for n in range(1,N + 1):
G.add_node(n, size = n * 100, pos = [0, n]) # size of node based on its number
# Draw graph
node_sizes = nx.get_node_attributes(G, 'size')
nx.draw_networkx(G, node_color = 'b', node_size = [v for v in node_sizes.values()])
# Make legend
for n in [2, 4, 6, 8]:
plt.plot([], [], 'bo', markersize = sqrt(n*100), label = f"{n}")
plt.legend(labelspacing = 5, loc='center left', bbox_to_anchor=(1, 0.5), frameon = False)
I have a bit of a hacky, alternative way of doing this, using scatter legend_items
, basically making a scatter plot with your node sizes, copying the legend, then removing the scatter plot again. Probably neater way to to do this but it works.
import matplotlib.pyplot as plt
import networkx as nx
from scipy.stats import expon
# some random graph
G = nx.erdos_renyi_graph(10, 0.5)
# some attribute that will influence node size
attribute = [70*i for i in range(10)]
# helper method for getting legend
def get_legend(sizes, num=10):
fig, ax = plt.subplots()
sc = ax.scatter([0]*len(sizes), [0]*len(sizes), s=sizes, color='blue')
store = sc.legend_elements("sizes", num=num)
fig.clf()
return store
fig, ax = plt.subplots()
nx.draw_networkx(G, node_size=attribute, ax=ax)
ax.legend(*get_legend(attribute, num=10), title="Variable A")