I cannot find any clear explanation as to how to create an adjacency matrix in Python, with weights taken into consideration. I assume it should be relatively simple to create.<
As mentioned previously, the standard way to deal with matrices in Python is to use NumPy. Here's a function that simply reads the adjacency matrix off of the adjacency list. (The implicit ordering of the nodes is made explicit by the parameter nodes
.)
import numpy
def weighted_adjmatrix(adjlist, nodes):
'''Returns a (weighted) adjacency matrix as a NumPy array.'''
matrix = []
for node in nodes:
weights = {endnode:int(weight)
for w in adjlist.get(node, {})
for endnode, weight in w.items()}
matrix.append([weights.get(endnode, 0) for endnode in nodes])
matrix = numpy.array(matrix)
return matrix + matrix.transpose()
In this case, weighted_adjmatrix(graph, nodes=list('123456'))
gives the NumPy array
array([[ 0, 15, 0, 7, 10, 0],
[15, 0, 9, 11, 0, 9],
[ 0, 9, 0, 0, 12, 7],
[ 7, 11, 0, 0, 8, 14],
[10, 0, 12, 8, 0, 8],
[ 0, 9, 7, 14, 8, 0]])
If a regular list is desired, the method tolist()
can be called.
I like tupled keys for 2d structures like this in python.
{(1, 1): 0, (3, 2): 9... }
I think it's conceptually clearest since it drops the intermediary data structure in the above solution. Nonetheless, that intermediary data structure -- the inner list or row / column-- can be useful if you intend to access your structure either row or column wise.
for x, row in enumerated(matrix, 1):
# process whole row
for y in enumerate(row, 1):
# process cell...
If cell-wise data access is your game though, it's hard to beat the following for expressive simplicity:
for (x, y), value in matrix.iteritems():
# act on cell
Sort it if you want.
# (1, 1), (1, 2)...
for (x, y), value in sorted(matrix.iteritems()):
# act on cell
I think the most common and simplest concept to store an adjacency matrix is to use a 2D array, which in python corresponds to nested lists
mat = [[0, 15, 0, 7, 10, 0], [15, 0, ...], [...], [...]]
m[0][1] # = 15 (weight of 1-2)
If the values are read only, you can use nested tuples, instead :)
Of course you can go as crazy as you want with that and use dictionaries or write a class and redefine __getattr__
to be more efficient on access times and storage as the matrix is symmetrical.
This converts your "adjacency list" (really a dict, not a list) into a genuine matrix:
import networkx as nx
graph = {'1': [{'2':'15'}, {'4':'7'}, {'5':'10'}],
'2': [{'3':'9'}, {'4':'11'}, {'6':'9'}],
'3': [{'5':'12'}, {'6':'7'}],
'4': [{'5':'8'}, {'6':'14'}],
'5': [{'6':'8'}]}
new_graph = nx.Graph()
for source, targets in graph.iteritems():
for inner_dict in targets:
assert len(inner_dict) == 1
new_graph.add_edge(int(source) - 1, int(inner_dict.keys()[0]) - 1,
weight=inner_dict.values()[0])
adjacency_matrix = nx.adjacency_matrix(new_graph)
(The format of your graph
is not particularly convenient for use in networkx
.) networkx
supports all kinds of operations on graphs and their adjacency matrices, so having the graph in this format should be very helpful for you. Note also that I've shifted your graph to use Python indices (i.e., starting at 0).
In [21]: adjacency_matrix
Out[21]:
matrix([[ 0., 15., 0., 7., 10., 0.],
[ 15., 0., 9., 11., 0., 9.],
[ 0., 9., 0., 0., 12., 7.],
[ 7., 11., 0., 0., 8., 14.],
[ 10., 0., 12., 8., 0., 8.],
[ 0., 9., 7., 14., 8., 0.]])