Skip to content

Commit

Permalink
Ensure _relabel_copy() respects node order of OrderedGraphs (#2488)
Browse files Browse the repository at this point in the history
* Ensure _relabel_copy() respects node order of OrderedGraphs, fixes #2482

* Comply with pep8
  • Loading branch information
jarrodmillman authored and dschult committed Jun 28, 2017
1 parent 55876b7 commit 7ae3d0f
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 112 deletions.
58 changes: 30 additions & 28 deletions networkx/relabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
# BSD license.
import networkx as nx
__author__ = """\n""".join(['Aric Hagberg <aric.hagberg@gmail.com>',
'Pieter Swart (swart@lanl.gov)',
'Dan Schult (dschult@colgate.edu)'])
'Pieter Swart (swart@lanl.gov)',
'Dan Schult (dschult@colgate.edu)'])
__all__ = ['convert_node_labels_to_integers', 'relabel_nodes']


def relabel_nodes(G, mapping, copy=True):
"""Relabel the nodes of the graph G.
Expand Down Expand Up @@ -76,21 +77,21 @@ def relabel_nodes(G, mapping, copy=True):
Only the nodes specified in the mapping will be relabeled.
The keyword setting copy=False modifies the graph in place.
Relabel_nodes avoids naming collisions by building a
Relabel_nodes avoids naming collisions by building a
directed graph from ``mapping`` which specifies the order of
relabelings. Naming collisions, such as a->b, b->c, are ordered
such that "b" gets renamed to "c" before "a" gets renamed "b".
In cases of circular mappings (e.g. a->b, b->a), modifying the
In cases of circular mappings (e.g. a->b, b->a), modifying the
graph is not possible in-place and an exception is raised.
In that case, use copy=True.
See Also
--------
convert_node_labels_to_integers
"""
# you can pass a function f(old_label)->new_label
# but we'll just make a dictionary here regardless
if not hasattr(mapping,"__getitem__"):
if not hasattr(mapping, "__getitem__"):
m = dict((n, mapping(n)) for n in G)
else:
m = mapping
Expand Down Expand Up @@ -131,38 +132,39 @@ def _relabel_inplace(G, mapping):
try:
G.add_node(new, **G.node[old])
except KeyError:
raise KeyError("Node %s is not in the graph"%old)
raise KeyError("Node %s is not in the graph" % old)
if multigraph:
new_edges = [(new, new if old == target else target, key, data)
for (_,target,key,data)
for (_, target, key, data)
in G.edges(old, data=True, keys=True)]
if directed:
new_edges += [(new if old == source else source, new, key, data)
for (source, _, key,data)
for (source, _, key, data)
in G.in_edges(old, data=True, keys=True)]
else:
new_edges = [(new, new if old == target else target, data)
for (_,target,data) in G.edges(old, data=True)]
for (_, target, data) in G.edges(old, data=True)]
if directed:
new_edges += [(new if old == source else source,new,data)
for (source,_,data) in G.in_edges(old, data=True)]
new_edges += [(new if old == source else source, new, data)
for (source, _, data) in G.in_edges(old, data=True)]
G.remove_node(old)
G.add_edges_from(new_edges)
return G


def _relabel_copy(G, mapping):
H = G.__class__()
H.add_nodes_from(mapping.get(n, n) for n in G)
H._node.update(dict((mapping.get(n, n), d.copy()) for n, d in G.node.items()))
if G.name:
H.name = "(%s)" % G.name
if G.is_multigraph():
H.add_edges_from( (mapping.get(n1, n1),mapping.get(n2, n2),k,d.copy())
for (n1,n2,k,d) in G.edges(keys=True, data=True))
H.add_edges_from((mapping.get(n1, n1), mapping.get(n2, n2), k, d.copy())
for (n1, n2, k, d) in G.edges(keys=True, data=True))
else:
H.add_edges_from( (mapping.get(n1, n1),mapping.get(n2, n2),d.copy())
for (n1, n2, d) in G.edges(data=True))
H.add_edges_from((mapping.get(n1, n1), mapping.get(n2, n2), d.copy())
for (n1, n2, d) in G.edges(data=True))

H.add_nodes_from(mapping.get(n, n) for n in G)
H._node.update(dict((mapping.get(n, n), d.copy()) for n,d in G.node.items()))
H.graph.update(G.graph.copy())

return H
Expand Down Expand Up @@ -200,27 +202,27 @@ def convert_node_labels_to_integers(G, first_label=0, ordering="default",
--------
relabel_nodes
"""
N = G.number_of_nodes()+first_label
N = G.number_of_nodes() + first_label
if ordering == "default":
mapping = dict(zip(G.nodes(), range(first_label, N)))
elif ordering == "sorted":
nlist = sorted(G.nodes())
mapping = dict(zip(nlist, range(first_label, N)))
elif ordering == "increasing degree":
dv_pairs = [(d,n) for (n,d) in G.degree()]
dv_pairs.sort() # in-place sort from lowest to highest degree
mapping = dict(zip([n for d,n in dv_pairs], range(first_label, N)))
dv_pairs = [(d, n) for (n, d) in G.degree()]
dv_pairs.sort() # in-place sort from lowest to highest degree
mapping = dict(zip([n for d, n in dv_pairs], range(first_label, N)))
elif ordering == "decreasing degree":
dv_pairs = [(d,n) for (n,d) in G.degree()]
dv_pairs.sort() # in-place sort from lowest to highest degree
dv_pairs = [(d, n) for (n, d) in G.degree()]
dv_pairs.sort() # in-place sort from lowest to highest degree
dv_pairs.reverse()
mapping = dict(zip([n for d,n in dv_pairs], range(first_label, N)))
mapping = dict(zip([n for d, n in dv_pairs], range(first_label, N)))
else:
raise nx.NetworkXError('Unknown node ordering: %s'%ordering)
raise nx.NetworkXError('Unknown node ordering: %s' % ordering)
H = relabel_nodes(G, mapping)
H.name = "("+G.name+")_with_int_labels"
H.name = "(" + G.name + ")_with_int_labels"
# create node attribute with the old label
if label_attribute is not None:
nx.set_node_attributes(H, label_attribute,
dict((v,k) for k,v in mapping.items()))
dict((v, k) for k, v in mapping.items()))
return H
175 changes: 91 additions & 84 deletions networkx/tests/test_relabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,153 +3,161 @@
from networkx import *
from networkx.convert import *
from networkx.algorithms.operators import *
from networkx.generators.classic import barbell_graph,cycle_graph
from networkx.generators.classic import barbell_graph, cycle_graph
from networkx.testing import *


class TestRelabel():
def test_convert_node_labels_to_integers(self):
# test that empty graph converts fine for all options
G=empty_graph()
H=convert_node_labels_to_integers(G,100)
G = empty_graph()
H = convert_node_labels_to_integers(G, 100)
assert_equal(H.name, '(empty_graph(0))_with_int_labels')
assert_equal(list(H.nodes()), [])
assert_equal(list(H.edges()), [])

for opt in ["default", "sorted", "increasing degree",
"decreasing degree"]:
G=empty_graph()
H=convert_node_labels_to_integers(G,100, ordering=opt)
for opt in ["default", "sorted", "increasing degree", "decreasing degree"]:
G = empty_graph()
H = convert_node_labels_to_integers(G, 100, ordering=opt)
assert_equal(H.name, '(empty_graph(0))_with_int_labels')
assert_equal(list(H.nodes()), [])
assert_equal(list(H.edges()), [])

G=empty_graph()
G.add_edges_from([('A','B'),('A','C'),('B','C'),('C','D')])
G.name="paw"
H=convert_node_labels_to_integers(G)
G = empty_graph()
G.add_edges_from([('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'D')])
G.name = "paw"
H = convert_node_labels_to_integers(G)
degH = (d for n, d in H.degree())
degG = (d for n, d in G.degree())
assert_equal(sorted(degH), sorted(degG))

H=convert_node_labels_to_integers(G,1000)
H = convert_node_labels_to_integers(G, 1000)
degH = (d for n, d in H.degree())
degG = (d for n, d in G.degree())
assert_equal(sorted(degH), sorted(degG))
assert_nodes_equal(H.nodes(), [1000, 1001, 1002, 1003])

H=convert_node_labels_to_integers(G,ordering="increasing degree")
H = convert_node_labels_to_integers(G, ordering="increasing degree")
degH = (d for n, d in H.degree())
degG = (d for n, d in G.degree())
assert_equal(sorted(degH), sorted(degG))
assert_equal(degree(H,0), 1)
assert_equal(degree(H,1), 2)
assert_equal(degree(H,2), 2)
assert_equal(degree(H,3), 3)
assert_equal(degree(H, 0), 1)
assert_equal(degree(H, 1), 2)
assert_equal(degree(H, 2), 2)
assert_equal(degree(H, 3), 3)

H=convert_node_labels_to_integers(G,ordering="decreasing degree")
H = convert_node_labels_to_integers(G, ordering="decreasing degree")
degH = (d for n, d in H.degree())
degG = (d for n, d in G.degree())
assert_equal(sorted(degH), sorted(degG))
assert_equal(degree(H,0), 3)
assert_equal(degree(H,1), 2)
assert_equal(degree(H,2), 2)
assert_equal(degree(H,3), 1)
assert_equal(degree(H, 0), 3)
assert_equal(degree(H, 1), 2)
assert_equal(degree(H, 2), 2)
assert_equal(degree(H, 3), 1)

H=convert_node_labels_to_integers(G,ordering="increasing degree",
label_attribute='label')
H = convert_node_labels_to_integers(G, ordering="increasing degree",
label_attribute='label')
degH = (d for n, d in H.degree())
degG = (d for n, d in G.degree())
assert_equal(sorted(degH), sorted(degG))
assert_equal(degree(H,0), 1)
assert_equal(degree(H,1), 2)
assert_equal(degree(H,2), 2)
assert_equal(degree(H,3), 3)
assert_equal(degree(H, 0), 1)
assert_equal(degree(H, 1), 2)
assert_equal(degree(H, 2), 2)
assert_equal(degree(H, 3), 3)

# check mapping
assert_equal(H.node[3]['label'],'C')
assert_equal(H.node[0]['label'],'D')
assert_true(H.node[1]['label']=='A' or H.node[2]['label']=='A')
assert_true(H.node[1]['label']=='B' or H.node[2]['label']=='B')
assert_equal(H.node[3]['label'], 'C')
assert_equal(H.node[0]['label'], 'D')
assert_true(H.node[1]['label'] == 'A' or H.node[2]['label'] == 'A')
assert_true(H.node[1]['label'] == 'B' or H.node[2]['label'] == 'B')

def test_convert_to_integers2(self):
G=empty_graph()
G.add_edges_from([('C','D'),('A','B'),('A','C'),('B','C')])
G.name="paw"
H=convert_node_labels_to_integers(G,ordering="sorted")
G = empty_graph()
G.add_edges_from([('C', 'D'), ('A', 'B'), ('A', 'C'), ('B', 'C')])
G.name = "paw"
H = convert_node_labels_to_integers(G, ordering="sorted")
degH = (d for n, d in H.degree())
degG = (d for n, d in G.degree())
assert_equal(sorted(degH), sorted(degG))

H=convert_node_labels_to_integers(G,ordering="sorted",
label_attribute='label')
assert_equal(H.node[0]['label'],'A')
assert_equal(H.node[1]['label'],'B')
assert_equal(H.node[2]['label'],'C')
assert_equal(H.node[3]['label'],'D')
H = convert_node_labels_to_integers(G, ordering="sorted",
label_attribute='label')
assert_equal(H.node[0]['label'], 'A')
assert_equal(H.node[1]['label'], 'B')
assert_equal(H.node[2]['label'], 'C')
assert_equal(H.node[3]['label'], 'D')

@raises(nx.NetworkXError)
def test_convert_to_integers_raise(self):
G = nx.Graph()
H=convert_node_labels_to_integers(G,ordering="increasing age")

H = convert_node_labels_to_integers(G, ordering="increasing age")

def test_relabel_nodes_copy(self):
G=empty_graph()
G.add_edges_from([('A','B'),('A','C'),('B','C'),('C','D')])
mapping={'A':'aardvark','B':'bear','C':'cat','D':'dog'}
H=relabel_nodes(G,mapping)
G = empty_graph()
G.add_edges_from([('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'D')])
mapping = {'A': 'aardvark', 'B': 'bear', 'C': 'cat', 'D': 'dog'}
H = relabel_nodes(G, mapping)
assert_nodes_equal(H.nodes(), ['aardvark', 'bear', 'cat', 'dog'])

def test_relabel_nodes_function(self):
G=empty_graph()
G.add_edges_from([('A','B'),('A','C'),('B','C'),('C','D')])
G = empty_graph()
G.add_edges_from([('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'D')])
# function mapping no longer encouraged but works

def mapping(n):
return ord(n)
H=relabel_nodes(G,mapping)
H = relabel_nodes(G, mapping)
assert_nodes_equal(H.nodes(), [65, 66, 67, 68])

def test_relabel_nodes_graph(self):
G=Graph([('A','B'),('A','C'),('B','C'),('C','D')])
mapping={'A':'aardvark','B':'bear','C':'cat','D':'dog'}
H=relabel_nodes(G,mapping)
G = Graph([('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'D')])
mapping = {'A': 'aardvark', 'B': 'bear', 'C': 'cat', 'D': 'dog'}
H = relabel_nodes(G, mapping)
assert_nodes_equal(H.nodes(), ['aardvark', 'bear', 'cat', 'dog'])

def test_relabel_nodes_orderedgraph(self):
G = OrderedGraph()
G.add_nodes_from([1, 2, 3])
G.add_edges_from([(1, 3), (2, 3)])
mapping = {1: 'a', 2: 'b', 3: 'c'}
H = relabel_nodes(G, mapping)
assert list(H.nodes) == ['a', 'b', 'c']

def test_relabel_nodes_digraph(self):
G=DiGraph([('A','B'),('A','C'),('B','C'),('C','D')])
mapping={'A':'aardvark','B':'bear','C':'cat','D':'dog'}
H=relabel_nodes(G,mapping,copy=False)
G = DiGraph([('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'D')])
mapping = {'A': 'aardvark', 'B': 'bear', 'C': 'cat', 'D': 'dog'}
H = relabel_nodes(G, mapping, copy=False)
assert_nodes_equal(H.nodes(), ['aardvark', 'bear', 'cat', 'dog'])

def test_relabel_nodes_multigraph(self):
G=MultiGraph([('a','b'),('a','b')])
mapping={'a':'aardvark','b':'bear'}
G=relabel_nodes(G,mapping,copy=False)
assert_nodes_equal(G.nodes(),['aardvark', 'bear'])
assert_edges_equal(G.edges(),[('aardvark', 'bear'), ('aardvark', 'bear')])
G = MultiGraph([('a', 'b'), ('a', 'b')])
mapping = {'a': 'aardvark', 'b': 'bear'}
G = relabel_nodes(G, mapping, copy=False)
assert_nodes_equal(G.nodes(), ['aardvark', 'bear'])
assert_edges_equal(G.edges(), [('aardvark', 'bear'), ('aardvark', 'bear')])

def test_relabel_nodes_multidigraph(self):
G=MultiDiGraph([('a','b'),('a','b')])
mapping={'a':'aardvark','b':'bear'}
G=relabel_nodes(G,mapping,copy=False)
assert_nodes_equal(G.nodes(),['aardvark', 'bear'])
assert_edges_equal(G.edges(),[('aardvark', 'bear'), ('aardvark', 'bear')])
G = MultiDiGraph([('a', 'b'), ('a', 'b')])
mapping = {'a': 'aardvark', 'b': 'bear'}
G = relabel_nodes(G, mapping, copy=False)
assert_nodes_equal(G.nodes(), ['aardvark', 'bear'])
assert_edges_equal(G.edges(), [('aardvark', 'bear'), ('aardvark', 'bear')])

def test_relabel_isolated_nodes_to_same(self):
G=Graph()
G = Graph()
G.add_nodes_from(range(4))
mapping={1:1}
H=relabel_nodes(G, mapping, copy=False)
mapping = {1: 1}
H = relabel_nodes(G, mapping, copy=False)
assert_nodes_equal(H.nodes(), list(range(4)))

@raises(KeyError)
def test_relabel_nodes_missing(self):
G=Graph([('A','B'),('A','C'),('B','C'),('C','D')])
mapping={0:'aardvark'}
G=relabel_nodes(G,mapping,copy=False)
G = Graph([('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'D')])
mapping = {0: 'aardvark'}
G = relabel_nodes(G, mapping, copy=False)

def test_relabel_copy_name(self):
G=Graph()
G = Graph()
H = relabel_nodes(G, {}, copy=True)
assert_equal(H.graph, G.graph)
H = relabel_nodes(G, {}, copy=False)
Expand All @@ -161,22 +169,21 @@ def test_relabel_copy_name(self):
assert_equal(H.graph, G.graph)

def test_relabel_toposort(self):
K5=nx.complete_graph(4)
G=nx.complete_graph(4)
G=nx.relabel_nodes(G,dict( [(i,i+1) for i in range(4)]),copy=False)
nx.is_isomorphic(K5,G)
G=nx.complete_graph(4)
G=nx.relabel_nodes(G,dict( [(i,i-1) for i in range(4)]),copy=False)
nx.is_isomorphic(K5,G)

K5 = nx.complete_graph(4)
G = nx.complete_graph(4)
G = nx.relabel_nodes(G, dict([(i, i + 1) for i in range(4)]), copy=False)
nx.is_isomorphic(K5, G)
G = nx.complete_graph(4)
G = nx.relabel_nodes(G, dict([(i, i - 1) for i in range(4)]), copy=False)
nx.is_isomorphic(K5, G)

def test_relabel_selfloop(self):
G = nx.DiGraph([(1, 1), (1, 2), (2, 3)])
G = nx.relabel_nodes(G, {1: 'One', 2: 'Two', 3: 'Three'}, copy=False)
assert_nodes_equal(G.nodes(),['One','Three','Two'])
assert_nodes_equal(G.nodes(), ['One', 'Three', 'Two'])
G = nx.MultiDiGraph([(1, 1), (1, 2), (2, 3)])
G = nx.relabel_nodes(G, {1: 'One', 2: 'Two', 3: 'Three'}, copy=False)
assert_nodes_equal(G.nodes(),['One','Three','Two'])
assert_nodes_equal(G.nodes(), ['One', 'Three', 'Two'])
G = nx.MultiDiGraph([(1, 1)])
G = nx.relabel_nodes(G, {1: 0}, copy=False)
assert_nodes_equal(G.nodes(), [0])

0 comments on commit 7ae3d0f

Please sign in to comment.