Repository: networkx/networkx

Issue:
Error when attempting to write a GraphML file after merging two graphs from imported GML files.
Hi,

(i posted this on StackOverflow earlier today, but thought it advisable to raise it here too in case it is a known issue - https://stackoverflow.com/questions/51612124/networkx-key-error-when-writing-gml-file )

I am getting the following error message when attempting to write GML file after merging two graphs using compose():

`NetworkXError: 'user_id' is not a valid key`
The background is that I import two GML files using:

```
g = nx.read_gml(file_path + "test_graph_1.gml")
h = nx.read_gml(file_path + "test_graph_2.gml")
```
Each node (in both GML files) file is structured thus:

```
node [
id 9
user_id "1663413990"
file "wingsscotland.dat"
label "brian_bilston"
image "/Users/ian/development/gtf/gtf/img/1663413990.jpg"
type "friends"
statuses 21085
friends 737
followers 53425
listed 550
ffr 72.4898
lfr 0.1029
shape "triangle-up"
]
```
After importing each file, I can check all the node attributes, see that nodes are unique within each graph.

I also see that NetworkX by default discards the 'id' field, und uses the 'label' as the identifier of the node. It retains the user_id attribute (which happens to be a Twitter user_id and suits my purposes well).

Running

`list(f.nodes(data=True))`
I can see that the data for the node above is:

```
('brian_bilston',
{'ffr': 72.4898,
'file': 'wingsscotland.dat',
'followers': 53425,
'friends': 737,
'image': '/Users/ian/development/gtf/gtf/img/1663413990.jpg',
'lfr': 0.1029,
'listed': 550,
'shape': 'triangle-up',
'statuses': 21085,
'type': 'friends',
'user_id': '1663413990'})
```
There is (in this test case) one common node shared by Graph g and Graph h, - the one shown above. All others are unique by user_id and label.

I then merge the two graphs using:

`f = nx.compose(g,h)`
This works ok.

I then go to write out a new GML from the graph, f, using:

`nx.write_gml(f, file_path + "one_plus_two.gml")`
At this point I get the error, above:

`  NetworkXError: 'user_id' is not a valid key
`
I have checked the uniqueness of all user_id's (in case I had duplicated one):

```
uid = nx.get_node_attributes(f,'user_id')
print(uid)
```
Which outputs:

```
{'brian_bilston': '1663413990', 
'ICMResearch': '100', 
'justcswilliams': '200', 
'MissBabington': '300', 
'ProBirdRights': '400', 
'FredSmith': '247775851', 
'JasWatt': '160952087', 
'Angela_Lewis': '2316946782', 
'Fuzzpig54': '130136162', 
'SonnyRussel': '828881340', 
'JohnBird': '448476934', 
'AngusMcAngus': '19785044'}
```
(formatted for readability).

So, all user_id's are unique, as far as I can tell.

So, if it is not a question of uniqueness of keys, what is the error telling me?

I've exhausted my thinking on this!

Any pointers, please, would be very much appreciated!


Discussion:
Yes -- this is a known issues:  see #2131 
The GML spec doesn't allow underscores in attribute names.
We allow reading .gml files that don't correspond to the official GML spec. But we write only items that follow the spec.  You should convert your attribute names to not include the underscore.

    for n in G:
        G.node[n]['userid'] = G.node[n]['user_id']
        del G[n]['user_id']

We should also add to the documentation a note about this. 
Wonderful @dschult I really appreciate your quick answer. 

I will post a response to the StackOverflow question with a link back here so that anyone else can find it from there. 

Much appreciated

Ian

Resolving patch:
diff --git a/networkx/readwrite/gml.py b/networkx/readwrite/gml.py
--- a/networkx/readwrite/gml.py
+++ b/networkx/readwrite/gml.py
@@ -195,7 +195,7 @@ def read_gml(path, label='label', destringizer=None):
     For additional documentation on the GML file format, please see the
     `GML website <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
 
-    See the module docstring :mod:`networkx.readwrite.gml` for additional details.
+    See the module docstring :mod:`networkx.readwrite.gml` for more details.
 
     Examples
     --------
@@ -265,7 +265,7 @@ def parse_gml(lines, label='label', destringizer=None):
     For additional documentation on the GML file format, please see the
     `GML website <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
 
-    See the module docstring :mod:`networkx.readwrite.gml` for additional details.
+    See the module docstring :mod:`networkx.readwrite.gml` for more details.
     """
     def decode_line(line):
         if isinstance(line, bytes):
@@ -611,7 +611,7 @@ def generate_gml(G, stringizer=None):
     For additional documentation on the GML file format, please see the
     `GML website <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
 
-    See the module docstring :mod:`networkx.readwrite.gml` for additional details.
+    See the module docstring :mod:`networkx.readwrite.gml` for more details.
 
     Examples
     --------
@@ -793,10 +793,13 @@ def write_gml(G, path, stringizer=None):
     specification.  For other data types, you need to explicitly supply a
     `stringizer`/`destringizer`.
 
+    Note that while we allow non-standard GML to be read from a file, we make
+    sure to write GML format. In particular, underscores are not allowed in
+    attribute names.
     For additional documentation on the GML file format, please see the
     `GML website <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
 
-    See the module docstring :mod:`networkx.readwrite.gml` for additional details.
+    See the module docstring :mod:`networkx.readwrite.gml` for more details.
 
     Examples
     --------


Test patch: