-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcluster.py
More file actions
186 lines (139 loc) · 5.47 KB
/
Copy pathcluster.py
File metadata and controls
186 lines (139 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# Copyright (c) 2007 Tom De Smedt.
# See LICENSE.txt for details.
from types import FunctionType, LambdaType
#--- LIST OPERATIONS ---------------------------------------------------------------------------------
def sorted(list, cmp=None, reversed=False):
""" Returns a sorted copy of the list.
"""
list = [x for x in list]
list.sort(cmp)
if reversed: list.reverse()
return list
def unique(list):
""" Returns a copy of the list without duplicates.
"""
unique = []; [unique.append(x) for x in list if x not in unique]
return unique
#--- SET THEORY --------------------------------------------------------------------------------------
def flatten(node, distance=1):
""" Recursively lists the node and its links.
Distance of 0 will return the given [node].
Distance of 1 will return a list of the node and all its links.
Distance of 2 will also include the linked nodes' links, etc.
"""
# When you pass a graph it returns all the node id's in it.
if hasattr(node, "nodes") and hasattr(node, "edges"):
return [n.id for n in node.nodes]
all = [node]
if distance >= 1:
for n in node.links:
all += n.flatten(distance-1)
return unique(all)
def intersection(a, b):
""" Returns the intersection of lists.
a & b -> elements that appear in a as well as in b.
"""
return [x for x in b if x in a]
def union(a, b):
""" Returns the union of lists.
a | b -> all elements from a and all the elements from b.
"""
return a + [x for x in b if x not in a]
def difference(a, b):
""" Returns the difference of lists.
a - b -> elements that appear in a but not in b.
"""
return [x for x in a if x not in b]
#--- SUBGRAPH ----------------------------------------------------------------------------------------
def subgraph(graph, id, distance=1):
""" Creates the subgraph of the flattened node with given id (or list of id's).
Finds all the edges between the nodes that make up the subgraph.
"""
g = graph.copy(empty=True)
if isinstance(id, (FunctionType, LambdaType)):
# id can also be a lambda or function that returns True or False
# for each node in the graph. We take the id's of nodes that pass.
id = [node.id for node in filter(id, graph.nodes)]
if not isinstance(id, (list, tuple)):
id = [id]
for id in id:
for n in flatten(graph[id], distance):
g.add_node(n.id, n.r, n.style, n.category, n.label, (n==graph.root), n.__dict__)
for e in graph.edges:
if e.node1.id in g and \
e.node2.id in g:
g.add_edge(e.node1.id, e.node2.id, e.weight, e.length, e.label, e.__dict__)
# Should we look for shortest paths between nodes here?
return g
#--- CLIQUE ----------------------------------------------------------------------------------------
def is_clique(graph):
""" A clique is a set of nodes in which each node is connected to all other nodes.
"""
#for n1 in graph.nodes:
# for n2 in graph.nodes:
# if n1 != n2 and graph.edge(n1.id, n2.id) == None:
# return False
if graph.density < 1.0:
return False
return True
def clique(graph, id):
""" Returns the largest possible clique for the node with given id.
"""
clique = [id]
for n in graph.nodes:
friend = True
for id in clique:
if n.id == id or graph.edge(n.id, id) == None:
friend = False
break
if friend:
clique.append(n.id)
return clique
def cliques(graph, threshold=3):
""" Returns all the cliques in the graph of at least the given size.
"""
cliques = []
for n in graph.nodes:
c = clique(graph, n.id)
if len(c) >= threshold:
c.sort()
if c not in cliques:
cliques.append(c)
return cliques
#--- UNCONNECTED SUBGRAPHS -------------------------------------------------------------------------
def partition(graph):
""" Splits unconnected subgraphs.
For each node in the graph, make a list of its id and all directly connected id's.
If one of the nodes in this list intersects with a subgraph,
they are all part of that subgraph.
Otherwise, this list is part of a new subgraph.
Return a list of subgraphs sorted by size (biggest-first).
"""
g = []
for n in graph.nodes:
c = [n.id for n in flatten(n)]
f = False
for i in range(len(g)):
if len(intersection(g[i], c)) > 0:
g[i] = union(g[i], c)
f = True
break
if not f:
g.append(c)
# If 1 is directly connected to 2 and 3,
# and 4 is directly connected to 5 and 6, these are separate subgraphs.
# If we later find that 7 is directly connected to 3 and 6,
# it will be attached to [1, 2, 3] yielding
# [1, 2, 3, 6, 7] and [4, 5, 6].
# These two subgraphs are connected and need to be merged.
merged = []
for i in range(len(g)):
merged.append(g[i])
for j in range(i+1, len(g)):
if len(intersection(g[i], g[j])) > 0:
merged[-1].extend(g[j])
g[j] = []
g = merged
g = [graph.sub(g, distance=0) for g in g]
g.sort(lambda a, b: len(b) - len(a))
return g