Python, NetworkX hexagonal lattice problem - how to create a perfect lattice? - python

So I am trying to generate a hexagonal lattice using NetworkX in Python. After using code:
G = nx.hexagonal_lattice_graph(m=2, n=2, periodic=False, with_positions=True, create_using=None)
plt.subplot(111)
nx.draw(G, with_labels=True, font_weight='bold')
plt.show()
I am getting a hexagonal lattice which looks like this:
lattice
As you can see, this lattice is formed from irregular hexagons and everytime the code is ran the shape changes. Is there a way to generate a perfect hexagonal lattice using NetworkX, i.e this, but with only X number of hexagons?
Thanks!

You need to use the with_postion attribute in the hexagonal_lattic_graph function and set it to True. This will store the positions of the nodes in an attribute called pos inside the Graph G itself. You can read more about from the documentation here:
with_positions (bool (default: True)) – Store the coordinates of each node in the graph node attribute ‘pos’. The coordinates provide a lattice with vertical columns of hexagons offset to interleave and cover the plane. Periodic positions shift the nodes vertically in a nonlinear way so the edges don’t overlap so much.
So, you just need to extract the positions from the graph itself, like this:
pos = nx.get_node_attributes(G, 'pos')
Then, pass this with pos while drawing your graph
import networkx as nx
import matplotlib.pyplot as plt
# create the graph and set with_positions=True
G = nx.hexagonal_lattice_graph(m=2, n=2, periodic=False, with_positions=True, create_using=None)
plt.subplot(111)
# Extract the positions
pos = nx.get_node_attributes(G, 'pos')
# Pass the positions while drawing
nx.draw(G, pos=pos, with_labels=True, font_weight='bold')
plt.show()

Related

Visualizing a little, complete and weighted network [duplicate]

I have the problem that I have a weighted adjacency matrix C of a directed graph, so C(j,i)=0, whenever there is no edge from j to i and if C(j,i)>0, then C(j,i) is the weight of the edge;
Now I want to plot the Directed Graph. There are many solutions when you manually add edges, see e.g. here:
Add edge-weights to plot output in networkx
But I want to plot edges and edge weights based on my matrix C; I started the following way:
def DrawGraph(C):
import networkx as nx
import matplotlib.pyplot as plt
G = nx.DiGraph(C)
plt.figure(figsize=(8,8))
nx.draw(G, with_labels=True)
This plots a graph and there are labels on the vertices, but there are no edge weights - also I cannot adapt the technic from the upper link to make it work - so what could I do?
And how would I change node size and color?
There are various ways to do this using networkx - here is a solution which should fit your requirements:
Code:
# Set up weighted adjacency matrix
A = np.array([[0, 0, 0],
[2, 0, 3],
[5, 0, 0]])
# Create DiGraph from A
G = nx.from_numpy_matrix(A, create_using=nx.DiGraph)
# Use spring_layout to handle positioning of graph
layout = nx.spring_layout(G)
# Use a list for node_sizes
sizes = [1000,400,200]
# Use a list for node colours
color_map = ['g', 'b', 'r']
# Draw the graph using the layout - with_labels=True if you want node labels.
nx.draw(G, layout, with_labels=True, node_size=sizes, node_color=color_map)
# Get weights of each edge and assign to labels
labels = nx.get_edge_attributes(G, "weight")
# Draw edge labels using layout and list of labels
nx.draw_networkx_edge_labels(G, pos=layout, edge_labels=labels)
# Show plot
plt.show()
Result:

Plot networkx with assigned coordinates

I am trying to run the following code to plot networkx graph but it gives me type error: list indices must be integers or slices, not str. May you help me with that?
pos = [[6.886709112999999, 50.50618938]]
nodes = ['866','144']
edges = [['866', '144']]
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
nx.draw_networkx(G, pos)
plt.show()
If you want to specify the positions for draw_networkx you should supply a dictionary of positions (x,y):
pos (dictionary, optional) – A dictionary with nodes as keys and positions as values. If not specified a spring layout positioning will be computed. See networkx.drawing.layout for functions that compute node positions.
You can take a look here (how to plot a networkx graph using the (x,y) coordinates of the points list?) for a recent example.
Or with your small example:
import networkx as nx
import matplotlib.pylab as plt
pos = {'866':[6.886709112999999, 0], '144': [50.50618938, 0]}
nodes = ['866','144']
edges = [['866', '144']]
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
nx.draw_networkx(G, pos)
plt.show()
The type error you observed, is probably caused by networks trying to call your pos list with the nodes as index, e.g. pos['866'].

Coloring, weighting and drawing a MultiGraph in networkx?

This answer demonstrates how to draw a graph with custom colors and edge thickness using the following code:
import networkx as nx
G = nx.Graph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(2,3,color='b',weight=4)
G.add_edge(3,4,color='g',weight=6)
pos = nx.circular_layout(G)
edges = G.edges()
colors = [G[u][v]['color'] for u,v in edges]
weights = [G[u][v]['weight'] for u,v in edges]
nx.draw(G, pos, edges=edges, edge_color=colors, width=weights)
Suppose however, that I want to graph a multi graph like:
G = nx.MultiGraph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(1,2,color='b',weight=3)
G.add_edge(2,3,color='r',weight=4)
G.add_edge(2,3,color='b',weight=6)
Calling something like draw should result in a total of three points. Point 1 and 2 should have both a red and blue line between them, similarly 2 and 3 should have both a red and blue line between them as well.
This fails to work for multigraphs because the multiple edges requires a different storage technique. Is there a relatively easy way around this?
Also, I believe this question and answer does not apply. The questioner uses the MultiGraph object, however, the actual graph is not a multigraph. The solution, is to chose the first (and, in his case, only) edge. In this case, however, both edges are needed in the drawing phase.
Is there a way to graph the multiple edges with different colors and weights in networkx?
You just need to access the edges of a multigraph in a different way
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
G = nx.MultiGraph()
G.add_edge(1,2,color='r',weight=8)
G.add_edge(1,2,color='b',weight=3)
G.add_edge(2,3,color='r',weight=4)
G.add_edge(2,3,color='c',weight=6)
pos = nx.circular_layout(G)
edges = G.edges()
colors = []
weight = []
for (u,v,attrib_dict) in list(G.edges.data()):
colors.append(attrib_dict['color'])
weight.append(attrib_dict['weight'])
nx.draw(G, pos, edges=edges, edge_color=colors, width=weight, )
plt.show()
Here is another combination of edges
G = nx.MultiGraph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(1,2,color='b',weight=3)
G.add_edge(2,3,color='r',weight=4)
G.add_edge(2,3,color='b',weight=6)
You can read more about accessing multigraph edges here.

Show a background grid (a.k.a. cells) when drawing a graph with networkx

I'm using networkx to draw a graph of a network with nodes at given positions on a field. The field is divided into a set amount of cells and the node positions correspond to a cell. When drawing the graph, I would like to show a grid in the background so the cells and distances in the graph are visible. Does networkx offer an option for that? I could not find one. I tried setting the grid via pyplot:
plt.xlim(0,14)
plt.ylim(0,10)
plt.xticks([x for x in xrange(0,14)])
plt.yticks([x for x in xrange(0,10)])
plt.grid(True)
which works by itself (see here) but when also calling
nx.draw(G, pos)
it shows the graph without the grid (see here). I'm guessing the grid is still in the background but networkx draws completely over it.
So is there any way to show the grid with plt or maybe a networkx command that I didn't find which allows something similar?
edit: Here is the complete code used. See the difference when (un)commenting nx.draw(G,pos)
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def quit_figure(event):
if event.key == 'q':
plt.close(event.canvas.figure)
nodes = [[1,1],[3,1],[6,1],[3,2],[13,1],[3,5]]
distance_array = [
[[],[],[1,3],[],[5],[],[],[],[],[],[],[],[],[],[]],
[[],[3],[0],[2],[],[],[],[],[],[],[],[],[],[],[]],
[[],[],[],[1,3],[5],[],[],[4],[],[],[],[],[],[]],
[[],[1],[0],[2,5],[],[],[],[],[],[],[10],[],[],[],[]],
[[],[],[],[],[],[],[],[2],[],[],[3,5],[],[],[],[]],
[[],[],[],[3],[0,2],[],[],[],[],[],[4],[],[],[],[],[]],
[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]],
[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]],
[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]]
current_transmission_range = 0
transmission_range = np.zeros((len(nodes)), dtype=int)
pos = {x:[nodes[x][0],nodes[x][1]] for x in xrange(len(nodes))}
graph_nodes = [x for x in xrange(len(nodes))]
current_transmission_range = 0
cid = plt.gcf().canvas.mpl_connect('key_press_event', quit_figure)
plt.xlim(0,14)
plt.ylim(0,10)
plt.xticks([x+0.5 for x in xrange(0,14)], [' '+str(x) for x in xrange(0,13)])
plt.yticks([x+0.5 for x in xrange(0,10)], ['\n\n'+str(x)+'\n\n\n\n' for x in xrange(0,9)])
plt.grid(True)
G = nx.Graph()
for node in graph_nodes:
G.add_node(node)
for node in graph_nodes:
for j in xrange(transmission_range[node]+1):
for k in distance_array[node][j]:
if(j <= current_transmission_range):
G.add_edge(node, k,weight=j)
edge_weight=dict([((u,v,),int(d['weight'])) for u,v,d in G.edges(data=True)])
nx.draw_networkx_edge_labels(G, pos,edge_labels=edge_weight)
# draw graph
# nx.draw(G, pos)
plt.show()
Use plt.grid('on')
import networkx as nx
import matplotlib.pyplot as plt
G = nx.path_graph(4)
nx.draw_networkx(G)
plt.grid('on')
plt.show()
If you want to use nx.draw(G) instead of nx.draw_networkx(G) you must also turn on the axis with plt.axis('on').
NetworkX's draw explicitly turns off axis.
You can avoid using draw and call the drawing subfunctions instead:
#...
nx.draw_networkx_nodes(G,pos)
nx.draw_networkx_edges(G,pos)
plt.show()
Or use draw_networkx, as Aric suggested.
That approach would be recommended if you're looking for long-term control over the drawing (as the draw implementation may change).
Of course, if it doesn't worry you, you can simply do plt.axes().set_axis_on() right after calling draw

How can I specify an exact output size for my networkx graph?

The above is the output of my current graph. However, I have yet to manage what I am trying to achieve. I need to output my graph in a larger size so that each node/edge can be viewed with ease.
I've tried nx.draw(G, node_size=size), but that only increases the size of the nodes, not the distance between nodes and edges.
You could try either smaller nodes/fonts or larger canvas. Here is a way to do both:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.cycle_graph(80)
pos = nx.circular_layout(G)
# default
plt.figure(1)
nx.draw(G,pos)
# smaller nodes and fonts
plt.figure(2)
nx.draw(G,pos,node_size=60,font_size=8)
# larger figure size
plt.figure(3,figsize=(12,12))
nx.draw(G,pos)
plt.show()
Since it seems that your network layout is too "messy", you might want to try different graph layout algorithms and see which one suits you best.
nx.draw(G)
nx.draw_random(G)
nx.draw_circular(G)
nx.draw_spectral(G)
nx.draw_spring(G)
Also, if you have too many nodes (let's say some thousands) visualizing your graph can be a problem.
you can increase the size of the plot as well as set the dpi.
If dpi is lowered that nodes would spread out more.
G = nx.Graph()
# Add edges
fig = plt.figure(1, figsize=(200, 80), dpi=60)
nx.draw(G, with_labels=True, font_weight='normal')

Categories

Resources