Des
Des

Reputation: 41

Networkx with errors from adding nodes and edges

This is a simple python program to generate a network graph. Everything was fine when I placed the data inside the program but when I decided to place the data in two input files, things started to get interesting. There are two input files: nodes (or vertices) and edges. When I read the nodes information from the input files called 'Step3-Vertices.txt', it did not give any error but additional information were added to the nodes which I did not supply. Here is a list of additional information : '[', '{', "'", '0', '2', ',', ' ', '6', '8', 'W', '}', '.', '1', '5', '3', '7', '4', 'O', 'X', 'D', ']', '\n'

I then read in the 2nd file called 'Step3-Edges.txt', this time I got a list of error messages which I could not comprehend.

ERROR MESSAGES - NETWORKX FAILED WHEN EDGES INFORMATION ARE ADDED FROM FILE :

Traceback (most recent call last):
File "step4_test1.py", line 30, in <module>
G.add_edges_from(data_edges)
File "/home/desmond/anaconda3/lib/python3.6/site-packages/networkx/classes/graph.py", line 934, in add_edges_from
"Edge tuple %s must be a 2-tuple or 3-tuple." % (e,))
networkx.exception.NetworkXError: Edge tuple [ must be a 2-tuple or 3-tuple.

Could someone help me please?

Here is my program:

""" THIS PROGRAM WORKS PROPERLY WHEN DATA ARE PASSED TO LOCAL VARIABLES CALLED "nodes" and "edges".  THE EXACT DATA ARE ALSO STORED IN TWO FILES: "nodes" in 'Step3-Vertices.txt' and "edges" in 'Step3-Edges.txt'.  PROBLEMS STARTED WHEN NODES AND EDGES ARE READ FROM BOTH FILES.  FIRST, RUN THIS PROGRAM AND IT SHOULD GENERATE A GRAPH.  THEN REPLACE THE "nodes" with "data_nodes" in "G.add_nodes_from" AND THIS WILL GENERATE UNEXPECTED ADDITIONAL NODES WHICH ARE NOT SUPPOSED TO BE THERE.  NEXT, REPLACE THE "edges" with "data_edges" in "G.add_nodes_from" AND ERROR MESSAGES ARE DISPLAYED."""  


import networkx as nx
import matplotlib.pyplot as plt



""" READ NODES INFORMATION FROM FILE """

with open('Step3-Vertices.txt', encoding='utf-8') as data_file:
    data_nodes = data_file.read()

print(data_nodes)

""" READ EDGESS INFORMATION FROM FILE """


with open('Step3-Edges.txt', encoding='utf-8') as data_file:
    data_edges = data_file.read()

print(data_edges)


G=nx.Graph()

"""  PASS NODES INFORMATION TO A VARIABLE CALLED 'nodes'   """

nodes = ['0000000002', '0000000101', '0000000111', '0000000200', '0000000502', '0000000600', '0000001000', '0000001001', '0000001069', '0000001253', '0000001462', '0000003013', '0000003200', '0000004100', '0000004305', '0000005100', '0000005460', '0000006600', '0000010021', '0000010101', '0000010200', '0000010314', '0000012000', '0000012151', '0000012600', '0000015201', '0000016100', '0000017002', '0000020002', '0000020050', '0000020100', '0000021001', '0000022044', '0000022100']


""" PASS EDGES INFORMATION TO A VARIABLE CALLED 'edges'   """

edges = [{'0000000002', '6080022W'}, {'80.015.012.210', '0000000002'}, {'80.015.012.210', '0000000502'}, {'0000012000', '0000000502'},{'0000000101', '012.105.123.127'}, {'0000000111', '2442032O'}, {'105.103.02.110', '0000000111'}, {'0604054X', '0000000200'}, {'100.001.008.002', '0000000200'}, {'0000000502', '1002567D'}, {'208.08.032.1', '0000000502'}]

"""THIS IS WHERE YOU ADD DATA TO THE NODES AND EDGES, BY DEFAULT, LOCAL VARIABLES ARE USED. TO ADD DATA FROM THE INPUT FILES - replace 'nodes' with 'data_nodes' and replace 'edges' with 'data_edges'   """




G.add_nodes_from(nodes)
G.add_edges_from(edges)

print("Nodes of graph: ")
print(G.nodes())


print("Edges of graph: ")
print(G.edges())

###  DRAW A GRAPH  ###

nx.draw(G)
plt.savefig("test1.png") # save as png
plt.show() # display

Upvotes: 3

Views: 9877

Answers (1)

Bonlenfum
Bonlenfum

Reputation: 20175

The format expected by add_edges_from is a list of tuples, in its most basic form, as a list of (u,v) pairs to connect.

Your files don't have the data in an appropriate form so networkx doesn't know what to do with them. If the text is exactly as you wrote in the "edges" variable, then below is one way to massage it into the right kind of list. You can do similar for the node processing, though this only needs a list of elements, not a list of tuples so is more straightforward.

with open("edgefile.txt") as data_file:
    data_edges = data_file.read()

# split on the comma, assuming this divides elements, remove the curly braces and quotes
elems = ([f.strip(" {}'") for f in data_edges.strip().split(',')])
# use zip to turn the flat list into a lst of pairs
edge_list = zip(elems[::2], elems[1::2])

# now we are in a form that nx.add_edges_from can handle
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edge_list)

You should read the docs on reading in graphs here: https://networkx.github.io/documentation/stable/reference/readwrite/index.html

which describes how to read from various standard graph formats.


EDIT following qu in comment:

The reason that you have many "unexpected" nodes in the graph is because nx.add_nodes_from takes an iterable type, and when you read the whole file into a text variable, iterating over that string takes one character at at time. This produces the single-character nodes like 0, ', and \n. So we can fix it by parsing the string into a list, and iterating over a list gives one element, like '0000000002'.

Here is an example:

# assume that the file describing nodes is read into this string:
node_txt = "'0000000002', '0000000101', '0000000111', '0000000200', '0000000502', '0000000600', '0000001000', '0000001001', '0000001069', '0000001253', '0000001462', '0000003013', '0000003200', '0000004100', '0000004305', '0000005100', '0000005460', '0000006600', '0000010021', '0000010101', '0000010200', '0000010314', '0000012000', '0000012151', '0000012600', '0000015201', '0000016100', '0000017002', '0000020002', '0000020050', '0000020100', '0000021001', '0000022044', '0000022100'\n"

G1 = nx.Graph()
G1.add_nodes_from(node_txt)
print(G1.nodes())
print(set(node_txt))
# output of these two commands shows that the node names are 1 char each:
>>> [' ', "'", '\n', ',', '1', '0', '3', '2', '5', '4', '7', '6', '9']
>>> set([' ', "'", '\n', ',', '1', '0', '3', '2', '5', '4', '7', '6', '9'])

# reference: what we really wanted    
node_list = ['0000000002', '0000000101', '0000000111', '0000000200', '0000000502', '0000000600', '0000001000', '0000001001', '0000001069', '0000001253', '0000001462', '0000003013', '0000003200', '0000004100', '0000004305', '0000005100', '0000005460', '0000006600', '0000010021', '0000010101', '0000010200', '0000010314', '0000012000', '0000012151', '0000012600', '0000015201', '0000016100', '0000017002', '0000020002', '0000020050', '0000020100', '0000021001', '0000022044', '0000022100']

G2  = nx.Graph()
G2.add_nodes_from(node_list)
print(G2.nodes())
print(set(node_list))

So how to transform node_txt into the form of node_list? We follow the same process as described above for the edges - this one is a bit simpler.

elems = [f.strip(" '") for f in node_txt.strip().split(',')]
print(elems == node_list)
# output: True -> so here we recovered the node names correctly from node_txt

Upvotes: 4

Related Questions