NetAnalyzer 0.1.5 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/NetAnalyzer.gemspec +16 -6
- data/README.md +16 -2
- data/Rakefile +13 -9
- data/bin/NetAnalyzer.rb +176 -33
- data/bin/randomize_clustering.rb +121 -0
- data/bin/randomize_network.rb +89 -0
- data/bin/ranker_gene.rb +121 -0
- data/bin/text2binary_matrix.rb +308 -0
- data/lib/NetAnalyzer/adv_mat_calc.rb +117 -0
- data/lib/NetAnalyzer/net_parser.rb +50 -0
- data/lib/NetAnalyzer/net_plotter.rb +145 -0
- data/lib/NetAnalyzer/network.rb +723 -249
- data/lib/NetAnalyzer/nodes.rb +15 -0
- data/lib/NetAnalyzer/performancer.rb +98 -0
- data/lib/NetAnalyzer/ranker.rb +250 -0
- data/lib/NetAnalyzer/templates/ElGrapho.min.js +28 -0
- data/lib/NetAnalyzer/templates/cytoscape.erb +65 -0
- data/lib/NetAnalyzer/templates/cytoscape.min.js +32 -0
- data/lib/NetAnalyzer/templates/el_grapho.erb +89 -0
- data/lib/NetAnalyzer/templates/pako.min.js +1 -0
- data/lib/NetAnalyzer/templates/sigma.erb +132 -0
- data/lib/NetAnalyzer/version.rb +1 -1
- data/lib/NetAnalyzer.rb +7 -0
- metadata +187 -29
data/lib/NetAnalyzer/network.rb
CHANGED
|
@@ -1,34 +1,94 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require 'nmatrix'
|
|
3
|
-
require '
|
|
4
|
-
require '
|
|
1
|
+
require 'rubystats'
|
|
2
|
+
#require 'nmatrix'
|
|
3
|
+
#require 'nmatrix/lapacke'
|
|
4
|
+
require 'numo/narray'
|
|
5
|
+
require 'numo/linalg'
|
|
6
|
+
require 'npy'
|
|
7
|
+
require 'parallel'
|
|
8
|
+
|
|
9
|
+
#require 'pp'
|
|
5
10
|
require 'benchmark'
|
|
11
|
+
#require 'nmatrix_expansion'
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+
require 'semtools'
|
|
14
|
+
require 'expcalc'
|
|
8
15
|
|
|
9
|
-
|
|
16
|
+
|
|
17
|
+
class Network
|
|
18
|
+
|
|
19
|
+
attr_accessor :adjacency_matrices, :association_values, :control_connections, :kernels, :reference_nodes, :group_nodes, :threads, :nodes, :edges, :compute_pairs, :compute_autorelations
|
|
10
20
|
|
|
11
21
|
## BASIC METHODS
|
|
12
22
|
############################################################
|
|
13
|
-
def initialize(layers)
|
|
14
|
-
@
|
|
23
|
+
def initialize(layers) # DONE
|
|
24
|
+
@threads = 0
|
|
25
|
+
@nodes = {}
|
|
15
26
|
@edges = {}
|
|
27
|
+
@reference_nodes = []
|
|
28
|
+
@group_nodes = {}
|
|
16
29
|
@adjacency_matrices = {}
|
|
30
|
+
@kernels = {}
|
|
17
31
|
@layers = layers
|
|
18
32
|
@association_values = {}
|
|
19
33
|
@control_connections = {}
|
|
34
|
+
@compute_pairs = :conn
|
|
35
|
+
@compute_autorelations = true
|
|
36
|
+
@loaded_obos = []
|
|
37
|
+
@ontologies = []
|
|
38
|
+
@layer_ontologies = {}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def clone # DONE
|
|
42
|
+
network_clone = Network.new(@layers.clone)
|
|
43
|
+
network_clone.threads = @threads.clone
|
|
44
|
+
network_clone.nodes = @nodes.clone
|
|
45
|
+
network_clone.edges = @edges.clone
|
|
46
|
+
network_clone.reference_nodes = @reference_nodes.clone
|
|
47
|
+
network_clone.group_nodes = @group_nodes.clone
|
|
48
|
+
network_clone.adjacency_matrices = @adjacency_matrices.clone
|
|
49
|
+
network_clone.kernels = @kernels.clone
|
|
50
|
+
network_clone.association_values = @association_values.clone
|
|
51
|
+
network_clone.control_connections = @control_connections.clone
|
|
52
|
+
network_clone.set_compute_pairs(@compute_pairs.clone, @compute_autorelations.clone)
|
|
53
|
+
#network_clone.loaded_obos = @loaded_obos.clone
|
|
54
|
+
#network_clone.ontologies = @ontologies.clone
|
|
55
|
+
#network_clone.layer_ontologies = @layer_ontologies.clone
|
|
56
|
+
|
|
57
|
+
return network_clone
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def ==(other) # DONE
|
|
61
|
+
are_equal = true
|
|
62
|
+
if self.threads != other.threads ||
|
|
63
|
+
self.nodes != other.nodes ||
|
|
64
|
+
self.edges != other.edges ||
|
|
65
|
+
self.reference_nodes != other.reference_nodes ||
|
|
66
|
+
self.group_nodes != other.group_nodes ||
|
|
67
|
+
self.adjacency_matrices != other.adjacency_matrices ||
|
|
68
|
+
self.association_values != other.association_values ||
|
|
69
|
+
self.control_connections != other.control_connections ||
|
|
70
|
+
self.compute_pairs != other.compute_pairs ||
|
|
71
|
+
self.compute_autorelations != other.compute_autorelations
|
|
72
|
+
are_equal = false
|
|
73
|
+
end
|
|
74
|
+
return are_equal
|
|
20
75
|
end
|
|
21
76
|
|
|
22
|
-
def
|
|
77
|
+
def set_compute_pairs(use_pairs, get_autorelations) #DONE
|
|
78
|
+
@compute_pairs = use_pairs
|
|
79
|
+
@compute_autorelations = get_autorelations
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def add_node(nodeID, nodeType = 0) # DONE
|
|
23
83
|
@nodes[nodeID] = Node.new(nodeID, nodeType)
|
|
24
84
|
end
|
|
25
85
|
|
|
26
|
-
def add_edge(nodeID1, nodeID2)
|
|
27
|
-
|
|
28
|
-
|
|
86
|
+
def add_edge(nodeID1, nodeID2) # DONE
|
|
87
|
+
add_edge2hash(nodeID1, nodeID2)
|
|
88
|
+
add_edge2hash(nodeID2, nodeID1)
|
|
29
89
|
end
|
|
30
90
|
|
|
31
|
-
def
|
|
91
|
+
def add_edge2hash(nodeA, nodeB) # NOT
|
|
32
92
|
query = @edges[nodeA]
|
|
33
93
|
if query.nil?
|
|
34
94
|
@edges[nodeA] = [nodeB]
|
|
@@ -37,70 +97,186 @@ class Network
|
|
|
37
97
|
end
|
|
38
98
|
end
|
|
39
99
|
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
100
|
+
def set_layer(layer_definitions, node_name) # DONE
|
|
101
|
+
layer = nil
|
|
102
|
+
if layer_definitions.length > 1
|
|
103
|
+
layer_definitions.each do |layer_name, regexp|
|
|
104
|
+
if node_name =~ regexp
|
|
105
|
+
layer = layer_name
|
|
106
|
+
break
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
raise("The node '#{node_name}' not match with any layer regex") if layer.nil?
|
|
110
|
+
else
|
|
111
|
+
layer = layer_definitions.first.first
|
|
49
112
|
end
|
|
113
|
+
@layers << layer if !@layers.include?(layer)
|
|
114
|
+
return layer
|
|
50
115
|
end
|
|
51
116
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
|
|
117
|
+
def generate_adjacency_matrix(layerA, layerB) # DONE
|
|
118
|
+
layerAidNodes = @nodes.select{|id, node| node.type == layerA}.keys
|
|
119
|
+
layerBidNodes = @nodes.select{|id, node| node.type == layerB}.keys
|
|
120
|
+
matrix = Numo::DFloat.zeros(layerAidNodes.length, layerBidNodes.length)
|
|
121
|
+
layerAidNodes.each_with_index do |nodeA, i|
|
|
122
|
+
layerBidNodes.each_with_index do |nodeB, j|
|
|
123
|
+
if @edges[nodeB].include?(nodeA)
|
|
124
|
+
matrix[i, j] = 1
|
|
125
|
+
else
|
|
126
|
+
matrix[i, j] = 0
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
all_info_matrix = [matrix, layerAidNodes, layerBidNodes]
|
|
131
|
+
|
|
132
|
+
if layerA == layerB
|
|
133
|
+
@adjacency_matrices[[layerA]] = all_info_matrix
|
|
134
|
+
else
|
|
135
|
+
@adjacency_matrices[[layerA, layerB]] = all_info_matrix
|
|
136
|
+
end
|
|
137
|
+
return all_info_matrix
|
|
55
138
|
end
|
|
56
139
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
140
|
+
|
|
141
|
+
def delete_nodes(node_list, mode='d') #DONE
|
|
142
|
+
if mode == 'd'
|
|
143
|
+
@nodes.reject!{|n| node_list.include?(n)}
|
|
144
|
+
@edges.reject!{|n, connections| node_list.include?(n)}
|
|
145
|
+
@edges.each do |n, connections|
|
|
146
|
+
connections.reject!{|c| node_list.include?(c)}
|
|
147
|
+
end
|
|
148
|
+
elsif mode == 'r'
|
|
149
|
+
@nodes.select!{|n| node_list.include?(n)}
|
|
150
|
+
@edges.select!{|n, connections| node_list.include?(n)}
|
|
151
|
+
@edges.each do |n, connections|
|
|
152
|
+
connections.select!{|c| node_list.include?(c)}
|
|
63
153
|
end
|
|
64
154
|
end
|
|
65
|
-
|
|
66
|
-
roboWrite.close
|
|
67
|
-
cmd = "#{layout} -Tpng #{output_filename} -o #{output_filename}.png"
|
|
68
|
-
system(cmd)
|
|
155
|
+
@edges.reject!{|n, connections| connections.empty?}
|
|
69
156
|
end
|
|
70
157
|
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
158
|
+
def get_connected_nodes(node_id, from_layer) # DONE
|
|
159
|
+
return @edges[node_id].map{|id| @nodes[id]}.select{|node| node.type == from_layer}.map{|node| node.id}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def get_bipartite_subgraph(from_layer_node_ids, from_layer, to_layer) # DONE
|
|
163
|
+
bipartite_subgraph = {}
|
|
164
|
+
from_layer_node_ids.each do |from_layer_node_id|
|
|
165
|
+
connected_nodes = @edges[from_layer_node_id]
|
|
166
|
+
connected_nodes.each do |connected_node|
|
|
167
|
+
if @nodes[connected_node].type == to_layer
|
|
168
|
+
query = bipartite_subgraph[connected_node]
|
|
169
|
+
if query.nil?
|
|
170
|
+
bipartite_subgraph[connected_node] = get_connected_nodes(connected_node, from_layer)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
return bipartite_subgraph
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def get_edge_number # DONE
|
|
180
|
+
node_connections = get_degree(zscore = false).values.inject(0){|sum, n| sum + n}
|
|
181
|
+
return node_connections/2
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def get_degree(zscore=true) # DONE
|
|
185
|
+
degree = {}
|
|
186
|
+
@edges.each do |id, nodes|
|
|
187
|
+
degree[id] = nodes.length
|
|
188
|
+
end
|
|
189
|
+
if zscore
|
|
190
|
+
degree_values = degree.values
|
|
191
|
+
mean_degree = degree_values.mean
|
|
192
|
+
std_degree = degree_values.standard_deviation
|
|
193
|
+
degree.transform_values!{|v| (v - mean_degree).fdiv(std_degree)}
|
|
194
|
+
end
|
|
195
|
+
return degree
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def get_all_intersections(args = {}) # DONE
|
|
199
|
+
intersection_lengths = get_all_pairs(args) do |node1, node2|
|
|
200
|
+
intersection(node1, node2).length
|
|
75
201
|
end
|
|
76
202
|
return intersection_lengths
|
|
77
203
|
end
|
|
78
204
|
|
|
79
|
-
def get_all_pairs(args = {})
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
205
|
+
def get_all_pairs(args = {}) # DONE
|
|
206
|
+
all_pairs = [] #lo que se devolvera
|
|
207
|
+
default = {:layers => :all}
|
|
208
|
+
args = default.merge(args)
|
|
209
|
+
nodeIDsA, nodeIDsB = collect_nodes(args)
|
|
210
|
+
if @compute_autorelations
|
|
211
|
+
if @compute_pairs == :all
|
|
212
|
+
while !nodeIDsA.empty?
|
|
213
|
+
node1 = nodeIDsA.shift
|
|
214
|
+
pairs = Parallel.map(nodeIDsA, in_processes: @threads) do |node2|
|
|
215
|
+
yield(node1, node2)
|
|
216
|
+
end
|
|
217
|
+
all_pairs.concat(pairs)
|
|
218
|
+
end
|
|
219
|
+
elsif @compute_pairs == :conn # TODO: Review this case to avoid return nil values
|
|
220
|
+
while !nodeIDsA.empty?
|
|
221
|
+
node1 = nodeIDsA.shift
|
|
222
|
+
ids_connected_to_n1 = @edges[node1]
|
|
223
|
+
pairs = Parallel.map(nodeIDsA, in_processes: @threads) do |node2|
|
|
224
|
+
result = nil
|
|
225
|
+
ids_connected_to_n2 = @edges[node2]
|
|
226
|
+
if exist_connections?(ids_connected_to_n1, ids_connected_to_n2)
|
|
227
|
+
result = yield(node1, node2)
|
|
228
|
+
end
|
|
229
|
+
result
|
|
230
|
+
end
|
|
231
|
+
pairs.compact!
|
|
232
|
+
all_pairs.concat(pairs)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
84
235
|
else
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
236
|
+
#MAIN METHOD
|
|
237
|
+
if @compute_pairs == :conn
|
|
238
|
+
all_pairs = Parallel.map(nodeIDsA, in_processes: @threads) do |node1|
|
|
239
|
+
ids_connected_to_n1 = @edges[node1]
|
|
240
|
+
node1_pairs = []
|
|
241
|
+
nodeIDsB.each do |node2|
|
|
242
|
+
ids_connected_to_n2 = @edges[node2]
|
|
243
|
+
if exist_connections?(ids_connected_to_n1, ids_connected_to_n2)
|
|
244
|
+
node1_pairs << yield(node1, node2)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
node1_pairs
|
|
248
|
+
end
|
|
249
|
+
all_pairs.flatten!(1)
|
|
250
|
+
elsif @compute_pairs == :all
|
|
251
|
+
raise 'Not implemented'
|
|
88
252
|
end
|
|
89
253
|
end
|
|
90
254
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
255
|
+
return all_pairs
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def collect_nodes(args) # DONE
|
|
259
|
+
nodeIDsA = nil
|
|
260
|
+
nodeIDsB = nil
|
|
261
|
+
if @compute_autorelations
|
|
262
|
+
if args[:layers] == :all
|
|
263
|
+
nodeIDsA = @nodes.keys
|
|
264
|
+
else
|
|
265
|
+
nodeIDsA = []
|
|
266
|
+
args[:layers].each do |layer|
|
|
267
|
+
nodeIDsA.concat(@nodes.select{|id, node| node.type == layer}.keys)
|
|
96
268
|
end
|
|
97
269
|
end
|
|
98
|
-
|
|
99
|
-
|
|
270
|
+
else
|
|
271
|
+
if args[:layers] != :all
|
|
272
|
+
nodeIDsA = @nodes.select{|id, node| node.type == args[:layers][0]}.keys
|
|
273
|
+
nodeIDsB = @nodes.select{|id, node| node.type == args[:layers][1]}.keys
|
|
274
|
+
end
|
|
100
275
|
end
|
|
276
|
+
return nodeIDsA, nodeIDsB
|
|
101
277
|
end
|
|
102
278
|
|
|
103
|
-
def get_nodes_layer(layers)
|
|
279
|
+
def get_nodes_layer(layers) # DONE
|
|
104
280
|
#for creating ny value in hypergeometric and pcc index
|
|
105
281
|
nodes = []
|
|
106
282
|
layers.each do |layer|
|
|
@@ -109,47 +285,237 @@ class Network
|
|
|
109
285
|
return nodes
|
|
110
286
|
end
|
|
111
287
|
|
|
112
|
-
def intersection(node1, node2)
|
|
288
|
+
def intersection(node1, node2) # DONE
|
|
113
289
|
shared_nodes = []
|
|
114
|
-
|
|
115
|
-
associatedIDs_node2 = @edges[node2]
|
|
116
|
-
intersectedIDs = associatedIDs_node1 & associatedIDs_node2
|
|
290
|
+
intersectedIDs = @edges[node1] & @edges[node2]
|
|
117
291
|
intersectedIDs.each do |id|
|
|
118
292
|
shared_nodes << @nodes[id]
|
|
119
293
|
end
|
|
120
294
|
return shared_nodes
|
|
121
295
|
end
|
|
122
296
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
297
|
+
def compute_avg_sht_path(com, paths=false) # DONE
|
|
298
|
+
path_lengths = []
|
|
299
|
+
all_paths = []
|
|
300
|
+
group = com.dup
|
|
301
|
+
while !group.empty?
|
|
302
|
+
node_start = group.shift
|
|
303
|
+
sht_paths = Parallel.map(group, in_processes: @threads) do |node_stop|
|
|
304
|
+
dist, path = shortest_path(node_start, node_stop, paths)
|
|
305
|
+
[dist, path]
|
|
306
|
+
end
|
|
307
|
+
sht_paths.each do |dist, path|
|
|
308
|
+
path_lengths << dist
|
|
309
|
+
all_paths << path
|
|
134
310
|
end
|
|
135
311
|
end
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
312
|
+
if path_lengths.include?(nil)
|
|
313
|
+
avg_sht_path = nil
|
|
314
|
+
else
|
|
315
|
+
avg_sht_path = path_lengths.inject(0){|sum,l| sum + l}.fdiv(path_lengths.length)
|
|
316
|
+
end
|
|
317
|
+
return avg_sht_path, all_paths
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# https://pythoninwonderland.wordpress.com/2017/03/18/how-to-implement-breadth-first-search-in-python/
|
|
321
|
+
# finds shortest path between 2 nodes of a graph using BFS
|
|
322
|
+
def bfs_shortest_path(start, goal, paths=false) # NOT
|
|
323
|
+
dist = nil
|
|
324
|
+
explored = {} # keep track of explored nodes
|
|
325
|
+
previous = {}
|
|
326
|
+
queue = [[start, 0]] # keep track of all the paths to be checked
|
|
327
|
+
is_goal = false
|
|
328
|
+
while !queue.empty? && !is_goal # keeps looping until all possible paths have been checked
|
|
329
|
+
node, dist = queue.pop # pop the first path from the queue
|
|
330
|
+
if !explored.include?(node) # get the last node from the path
|
|
331
|
+
neighbours = @edges[node]
|
|
332
|
+
explored[node] = true # mark node as explored
|
|
333
|
+
next if neighbours.nil?
|
|
334
|
+
dist += 1
|
|
335
|
+
neighbours.each do |neighbour| # go through all neighbour nodes, construct a new path
|
|
336
|
+
next if explored.include?(neighbour)
|
|
337
|
+
queue.unshift([neighbour, dist]) # push it into the queue
|
|
338
|
+
previous[neighbour] = node if paths
|
|
339
|
+
if neighbour == goal # return path if neighbour is goal
|
|
340
|
+
is_goal = true
|
|
341
|
+
break
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
if is_goal
|
|
347
|
+
path = build_path(previous, start, goal) if paths
|
|
348
|
+
else
|
|
349
|
+
dist = nil
|
|
350
|
+
path = []
|
|
351
|
+
end
|
|
352
|
+
return dist, path
|
|
140
353
|
end
|
|
141
354
|
|
|
142
|
-
def
|
|
143
|
-
|
|
144
|
-
|
|
355
|
+
def build_path(previous, startNode, stopNode) # NOT
|
|
356
|
+
path = []
|
|
357
|
+
currentNode = stopNode
|
|
358
|
+
path << currentNode
|
|
359
|
+
while currentNode != startNode
|
|
360
|
+
currentNode = previous[currentNode]
|
|
361
|
+
path << currentNode
|
|
362
|
+
end
|
|
363
|
+
return path
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def shortest_path(node_start, node_stop, paths=false) # DONE
|
|
367
|
+
#https://betterprogramming.pub/5-ways-to-find-the-shortest-path-in-a-graph-88cfefd0030f
|
|
368
|
+
#return bidirectionalSearch(node_start, node_stop)
|
|
369
|
+
#https://efficientcodeblog.wordpress.com/2017/12/13/bidirectional-search-two-end-bfs/
|
|
370
|
+
dist, all_paths = bfs_shortest_path(node_start, node_stop, paths)
|
|
371
|
+
return dist, all_paths
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def get_node_attributes(attr_names)
|
|
375
|
+
attrs = []
|
|
376
|
+
attr_names.each do |attr_name|
|
|
377
|
+
if attr_name == 'get_degree'
|
|
378
|
+
attrs << get_degree(zscore=false)
|
|
379
|
+
elsif attr_name == 'get_degreeZ'
|
|
380
|
+
attrs << get_degree
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
node_ids = attrs.first.keys
|
|
384
|
+
node_attrs = []
|
|
385
|
+
node_ids.each do |n|
|
|
386
|
+
node_attrs << [n].concat(attrs.map{|at| at[n]})
|
|
387
|
+
end
|
|
388
|
+
return node_attrs
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def plot_network(options = {})
|
|
392
|
+
net_data = {
|
|
393
|
+
group_nodes: @group_nodes,
|
|
394
|
+
reference_nodes: @reference_nodes,
|
|
395
|
+
nodes: @nodes,
|
|
396
|
+
edges: @edges,
|
|
397
|
+
layers: @layers
|
|
398
|
+
}
|
|
399
|
+
Net_plotter.new(net_data, options)
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Compute communities/group properties
|
|
403
|
+
#----------------------------------------------
|
|
404
|
+
def compute_group_metrics(output_filename) # DONE
|
|
405
|
+
metrics = []
|
|
406
|
+
header = ['group']
|
|
407
|
+
@group_nodes.keys.each do |k|
|
|
408
|
+
metrics << [k]
|
|
409
|
+
end
|
|
410
|
+
header << 'comparative_degree'
|
|
411
|
+
comparative_degree = communities_comparative_degree(@group_nodes)
|
|
412
|
+
comparative_degree.each_with_index{|val,i| metrics[i] << replace_nil_vals(val)}
|
|
413
|
+
header << 'avg_sht_path'
|
|
414
|
+
avg_sht_path = communities_avg_sht_path(@group_nodes)
|
|
415
|
+
avg_sht_path.each_with_index{|val,i| metrics[i] << replace_nil_vals(val)}
|
|
416
|
+
if !@reference_nodes.empty?
|
|
417
|
+
header.concat(%w[node_com_assoc_by_edge node_com_assoc_by_node])
|
|
418
|
+
node_com_assoc = compute_node_com_assoc_in_precomputed_communities(@group_nodes, @reference_nodes.first)
|
|
419
|
+
node_com_assoc.each_with_index{|val,i| metrics[i].concat(val)}
|
|
420
|
+
end
|
|
421
|
+
File.open(output_filename, 'w') do |f|
|
|
422
|
+
f.puts header.join("\t")
|
|
423
|
+
metrics.each do |gr|
|
|
424
|
+
f. puts gr.join("\t")
|
|
425
|
+
end
|
|
145
426
|
end
|
|
146
427
|
end
|
|
147
428
|
|
|
429
|
+
def communities_comparative_degree(coms) # DONE
|
|
430
|
+
comparative_degrees = []
|
|
431
|
+
coms.each do |com_id, com|
|
|
432
|
+
comparative_degrees << compute_comparative_degree(com)
|
|
433
|
+
end
|
|
434
|
+
return comparative_degrees
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def communities_avg_sht_path(coms) # DONE
|
|
438
|
+
avg_sht_path = []
|
|
439
|
+
coms.each do |com_id, com|
|
|
440
|
+
dist, paths = compute_avg_sht_path(com)
|
|
441
|
+
avg_sht_path << dist
|
|
442
|
+
end
|
|
443
|
+
return avg_sht_path
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def compute_node_com_assoc_in_precomputed_communities(coms, ref_node) # DONE
|
|
447
|
+
node_com_assoc = []
|
|
448
|
+
coms.each do |com_id, com|
|
|
449
|
+
node_com_assoc << [compute_node_com_assoc(com, ref_node)]
|
|
450
|
+
end
|
|
451
|
+
return node_com_assoc
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def compute_comparative_degree(com) # DONE # see Girvan-Newman Benchmark control parameter in http://networksciencebook.com/chapter/9#testing (communities chapter)
|
|
455
|
+
internal_degree = 0
|
|
456
|
+
external_degree = 0
|
|
457
|
+
com.each do |nodeID|
|
|
458
|
+
nodeIDneigh = @edges[nodeID]
|
|
459
|
+
next if nodeIDneigh.nil?
|
|
460
|
+
internal_degree += (nodeIDneigh & com).length
|
|
461
|
+
external_degree += (nodeIDneigh - com).length
|
|
462
|
+
end
|
|
463
|
+
comparative_degree = external_degree.fdiv(external_degree + internal_degree)
|
|
464
|
+
return comparative_degree
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def compute_node_com_assoc(com, ref_node) # DONE
|
|
468
|
+
ref_cons = 0
|
|
469
|
+
ref_secondary_cons = 0
|
|
470
|
+
secondary_nodes = {}
|
|
471
|
+
other_cons = 0
|
|
472
|
+
other_nodes = {}
|
|
473
|
+
|
|
474
|
+
refNneigh = @edges[ref_node]
|
|
475
|
+
com.each do |nodeID|
|
|
476
|
+
nodeIDneigh = @edges[nodeID]
|
|
477
|
+
next if nodeIDneigh.nil?
|
|
478
|
+
ref_cons += 1 if nodeIDneigh.include?(ref_node)
|
|
479
|
+
if !refNneigh.nil?
|
|
480
|
+
common_nodes = nodeIDneigh & refNneigh
|
|
481
|
+
common_nodes.each {|id| secondary_nodes[id] = true}
|
|
482
|
+
ref_secondary_cons += common_nodes.length
|
|
483
|
+
end
|
|
484
|
+
specific_nodes = nodeIDneigh - refNneigh - [ref_node]
|
|
485
|
+
specific_nodes.each {|id| other_nodes[id] = true}
|
|
486
|
+
other_cons += specific_nodes.length
|
|
487
|
+
end
|
|
488
|
+
by_edge = (ref_cons + ref_secondary_cons).fdiv(other_cons)
|
|
489
|
+
by_node = (ref_cons + secondary_nodes.length).fdiv(other_nodes.length)
|
|
490
|
+
return by_edge, by_node
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def expand_clusters(expand_method) # DONE
|
|
494
|
+
clusters = {}
|
|
495
|
+
@group_nodes.each do |id, nodes|
|
|
496
|
+
if expand_method == 'sht_path'
|
|
497
|
+
dist, paths = compute_avg_sht_path(nodes, paths=true) # this uses bfs, maybe Dijkstra is the best one
|
|
498
|
+
new_nodes = paths.flatten.uniq
|
|
499
|
+
clusters[id] = nodes | new_nodes # If some node pair are not connected, recover them
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
return clusters
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
|
|
148
506
|
## ASSOCIATION METHODS
|
|
149
507
|
############################################################
|
|
150
|
-
def
|
|
508
|
+
def clean_autorelations_on_association_values # DONE
|
|
509
|
+
@association_values.each do |meth, values|
|
|
510
|
+
values.select!{|relation| @nodes[relation[0]].type != @nodes[relation[1]].type}
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def get_association_values(layers, base_layer, meth) # DONE
|
|
151
515
|
relations = [] #node A, node B, val
|
|
152
|
-
if meth == :
|
|
516
|
+
if meth == :counts
|
|
517
|
+
relations = get_counts_association(layers, base_layer)
|
|
518
|
+
elsif meth == :jaccard #all networks
|
|
153
519
|
relations = get_jaccard_association(layers, base_layer)
|
|
154
520
|
elsif meth == :simpson #all networks
|
|
155
521
|
relations = get_simpson_association(layers, base_layer)
|
|
@@ -161,6 +527,14 @@ class Network
|
|
|
161
527
|
relations = get_pcc_associations(layers, base_layer)
|
|
162
528
|
elsif meth == :hypergeometric #all networks
|
|
163
529
|
relations = get_hypergeometric_associations(layers, base_layer)
|
|
530
|
+
elsif meth == :hypergeometric_bf #all networks
|
|
531
|
+
relations = get_hypergeometric_associations(layers, base_layer, :bonferroni)
|
|
532
|
+
elsif meth == :hypergeometric_bh #all networks
|
|
533
|
+
relations = get_hypergeometric_associations(layers, base_layer, :benjamini_hochberg)
|
|
534
|
+
elsif meth == :hypergeometric_elim #tripartite networks?
|
|
535
|
+
relations = get_hypergeometric_associations_with_topology(layers, base_layer, :elim)
|
|
536
|
+
elsif meth == :hypergeometric_weight #tripartite networks?
|
|
537
|
+
relations = get_hypergeometric_associations_with_topology(layers, base_layer, :weight)
|
|
164
538
|
elsif meth == :csi #all networks
|
|
165
539
|
relations = get_csi_associations(layers, base_layer)
|
|
166
540
|
elsif meth == :transference #tripartite networks
|
|
@@ -171,22 +545,14 @@ class Network
|
|
|
171
545
|
|
|
172
546
|
## association methods adjacency matrix based
|
|
173
547
|
#---------------------------------------------------------
|
|
174
|
-
|
|
175
|
-
|
|
548
|
+
def get_association_by_transference_resources(firstPairLayers, secondPairLayers, lambda_value1 = 0.5, lambda_value2 = 0.5) # DONE
|
|
549
|
+
relations = []
|
|
176
550
|
matrix1 = @adjacency_matrices[firstPairLayers].first
|
|
177
|
-
rowIds = @adjacency_matrices[firstPairLayers][1]
|
|
178
551
|
matrix2 = @adjacency_matrices[secondPairLayers].first
|
|
552
|
+
finalMatrix = Adv_mat_calc.tranference_resources(matrix1, matrix2, lambda_value1 = lambda_value1, lambda_value2 = lambda_value2)
|
|
553
|
+
rowIds = @adjacency_matrices[firstPairLayers][1]
|
|
179
554
|
colIds = @adjacency_matrices[secondPairLayers][2]
|
|
180
|
-
|
|
181
|
-
m1colNumber = matrix1.cols
|
|
182
|
-
m2rowNumber = matrix2.rows
|
|
183
|
-
m2colNumber = matrix2.cols
|
|
184
|
-
#puts m1rowNumber, m1colNumber, m2rowNumber, m2colNumber
|
|
185
|
-
matrix1Weight = graphWeights(m1colNumber, m1rowNumber, matrix1.transpose, lambda_value1)
|
|
186
|
-
matrix2Weight = graphWeights(m2colNumber, m2rowNumber, matrix2.transpose, lambda_value2)
|
|
187
|
-
matrixWeightProduct = matrix1Weight.dot(matrix2.dot(matrix2Weight))
|
|
188
|
-
finalMatrix = matrix1.dot(matrixWeightProduct)
|
|
189
|
-
relations = nmatrix2relations(finalMatrix, rowIds, colIds)
|
|
555
|
+
relations = matrix2relations(finalMatrix, rowIds, colIds)
|
|
190
556
|
@association_values[:transference] = relations
|
|
191
557
|
return relations
|
|
192
558
|
end
|
|
@@ -194,19 +560,26 @@ class Network
|
|
|
194
560
|
## association methods node pairs based
|
|
195
561
|
#---------------------------------------------------------
|
|
196
562
|
# Bass 2013, doi:10.1038/nmeth.2728
|
|
197
|
-
def get_associations(layers, base_layer) # BASE METHOD
|
|
198
|
-
|
|
199
|
-
get_all_pairs(layers: layers) do |node1, node2|
|
|
563
|
+
def get_associations(layers, base_layer) # DONE BASE METHOD
|
|
564
|
+
associations = get_all_pairs(layers: layers) do |node1, node2|
|
|
200
565
|
associatedIDs_node1 = @edges[node1].map{|id| @nodes[id]}.select{|node| node.type == base_layer}.map{|node| node.id}
|
|
201
566
|
associatedIDs_node2 = @edges[node2].map{|id| @nodes[id]}.select{|node| node.type == base_layer}.map{|node| node.id}
|
|
202
567
|
intersectedIDs = associatedIDs_node1 & associatedIDs_node2
|
|
203
568
|
associationValue = yield(associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2)
|
|
204
|
-
|
|
569
|
+
[node1, node2, associationValue]
|
|
205
570
|
end
|
|
571
|
+
return associations
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def get_counts_association(layers, base_layer) # DONE
|
|
575
|
+
relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
|
|
576
|
+
countValue = intersectedIDs.length
|
|
577
|
+
end
|
|
578
|
+
@association_values[:counts] = relations
|
|
206
579
|
return relations
|
|
207
580
|
end
|
|
208
581
|
|
|
209
|
-
def get_jaccard_association(layers, base_layer)
|
|
582
|
+
def get_jaccard_association(layers, base_layer) # DONE
|
|
210
583
|
relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
|
|
211
584
|
unionIDS = associatedIDs_node1 | associatedIDs_node2
|
|
212
585
|
jaccValue = intersectedIDs.length.to_f/unionIDS.length
|
|
@@ -215,7 +588,7 @@ class Network
|
|
|
215
588
|
return relations
|
|
216
589
|
end
|
|
217
590
|
|
|
218
|
-
def get_simpson_association(layers, base_layer)
|
|
591
|
+
def get_simpson_association(layers, base_layer) # DONE
|
|
219
592
|
relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
|
|
220
593
|
minLength = [associatedIDs_node1.length, associatedIDs_node2.length].min
|
|
221
594
|
simpsonValue = intersectedIDs.length.to_f/minLength
|
|
@@ -224,7 +597,7 @@ class Network
|
|
|
224
597
|
return relations
|
|
225
598
|
end
|
|
226
599
|
|
|
227
|
-
def get_geometric_associations(layers, base_layer)
|
|
600
|
+
def get_geometric_associations(layers, base_layer) # DONE
|
|
228
601
|
#wang 2016 method
|
|
229
602
|
relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
|
|
230
603
|
intersectedIDs = intersectedIDs.length**2
|
|
@@ -235,7 +608,7 @@ class Network
|
|
|
235
608
|
return relations
|
|
236
609
|
end
|
|
237
610
|
|
|
238
|
-
def get_cosine_associations(layers, base_layer)
|
|
611
|
+
def get_cosine_associations(layers, base_layer) # DONE
|
|
239
612
|
relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
|
|
240
613
|
productLength = Math.sqrt(associatedIDs_node1.length * associatedIDs_node2.length)
|
|
241
614
|
cosineValue = intersectedIDs.length/productLength
|
|
@@ -244,9 +617,10 @@ class Network
|
|
|
244
617
|
return relations
|
|
245
618
|
end
|
|
246
619
|
|
|
247
|
-
def get_pcc_associations(layers, base_layer)
|
|
620
|
+
def get_pcc_associations(layers, base_layer) # DONE
|
|
248
621
|
#for Ny calcule use get_nodes_layer
|
|
249
|
-
|
|
622
|
+
base_layer_nodes = get_nodes_layer([base_layer])
|
|
623
|
+
ny = base_layer_nodes.length
|
|
250
624
|
relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
|
|
251
625
|
intersProd = intersectedIDs.length * ny
|
|
252
626
|
nodesProd = associatedIDs_node1.length * associatedIDs_node2.length
|
|
@@ -259,54 +633,98 @@ class Network
|
|
|
259
633
|
return relations
|
|
260
634
|
end
|
|
261
635
|
|
|
262
|
-
def get_hypergeometric_associations(layers, base_layer)
|
|
636
|
+
def get_hypergeometric_associations(layers, base_layer, pvalue_adj_method= nil) # DONE
|
|
263
637
|
ny = get_nodes_layer([base_layer]).length
|
|
638
|
+
fet = Rubystats::FishersExactTest.new
|
|
264
639
|
relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
|
|
265
|
-
|
|
640
|
+
fisher = 0
|
|
266
641
|
intersection_lengths = intersectedIDs.length
|
|
267
|
-
sum = 0
|
|
268
642
|
if intersection_lengths > 0
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if sum == 0
|
|
279
|
-
hypergeometricValue = 0
|
|
280
|
-
else
|
|
281
|
-
hypergeometricValue = -Math.log10(sum)
|
|
643
|
+
n1_items = associatedIDs_node1.length
|
|
644
|
+
n2_items = associatedIDs_node2.length
|
|
645
|
+
fisher = fet.calculate(
|
|
646
|
+
intersection_lengths,
|
|
647
|
+
n1_items - intersection_lengths,
|
|
648
|
+
n2_items - intersection_lengths,
|
|
649
|
+
ny - (n1_items + n2_items - intersection_lengths)
|
|
650
|
+
)
|
|
651
|
+
fisher = fisher[:right]
|
|
282
652
|
end
|
|
283
|
-
|
|
653
|
+
fisher
|
|
284
654
|
end
|
|
285
|
-
|
|
655
|
+
if pvalue_adj_method == :bonferroni
|
|
656
|
+
meth = :hypergeometric_bf
|
|
657
|
+
compute_adjusted_pvalue_bonferroni(relations)
|
|
658
|
+
elsif pvalue_adj_method == :benjamini_hochberg
|
|
659
|
+
meth = :hypergeometric_bh
|
|
660
|
+
compute_adjusted_pvalue_benjaminiHochberg(relations)
|
|
661
|
+
else
|
|
662
|
+
meth = :hypergeometric
|
|
663
|
+
compute_log_transformation(relations)
|
|
664
|
+
end
|
|
665
|
+
@association_values[meth] = relations
|
|
286
666
|
return relations
|
|
287
667
|
end
|
|
288
668
|
|
|
289
|
-
def
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
669
|
+
def get_hypergeometric_associations_with_topology(layers, base_layer, mode, thresold = 0.01) # NOT
|
|
670
|
+
relations = []
|
|
671
|
+
reference_layer = (layers - @layer_ontologies.keys).first
|
|
672
|
+
ontology_layer = (layers - [reference_layer]).first
|
|
673
|
+
ref_nodes = get_nodes_layer([reference_layer]).map{|n| n.id} # get nodes from NOT ontology layer
|
|
674
|
+
ontology = @layer_ontologies[ontology_layer]
|
|
675
|
+
base_layer_length = @nodes.values.count{|n| n.type == base_layer}
|
|
676
|
+
ref_nodes.each do |ref_node|
|
|
677
|
+
base_nodes = get_connected_nodes(ref_node, base_layer)
|
|
678
|
+
ontology_base_subgraph = get_bipartite_subgraph(base_nodes, base_layer, ontology_layer) # get shared nodes between nodes from NOT ontology layer and ONTOLOGY layer. Also get the conections between shared nodes and ontology nodes.
|
|
679
|
+
next if ontology_base_subgraph.empty?
|
|
680
|
+
ontology_base_subgraph.transform_keys!{|k| k.to_sym}
|
|
681
|
+
ontology.load_item_relations_to_terms(ontology_base_subgraph, remove_old_relations = true)
|
|
682
|
+
term_pvals = ontology.compute_relations_to_items(base_nodes, base_layer_length, mode, thresold)
|
|
683
|
+
relations.concat(term_pvals.map{|term| [ref_node, term[0], term[1]]})
|
|
684
|
+
end
|
|
685
|
+
compute_log_transformation(relations)
|
|
686
|
+
if mode == :elim
|
|
687
|
+
meth = :hypergeometric_elim
|
|
688
|
+
elsif mode == :weight
|
|
689
|
+
meth = :hypergeometric_weight
|
|
295
690
|
end
|
|
691
|
+
@association_values[meth] = relations
|
|
692
|
+
return relations
|
|
296
693
|
end
|
|
297
694
|
|
|
298
|
-
def
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
query_node1[node2] = val
|
|
695
|
+
def compute_adjusted_pvalue(relations, log_val=true) # DONE
|
|
696
|
+
relations.each_with_index do |data, i| #p1, p2, pval
|
|
697
|
+
pval_adj = yield(data.last, i)
|
|
698
|
+
pval_adj = -Math.log10(pval_adj) if log_val && pval_adj > 0
|
|
699
|
+
data[2] = pval_adj
|
|
304
700
|
end
|
|
305
701
|
end
|
|
306
702
|
|
|
703
|
+
def compute_log_transformation(relations) # NOT #Only perform log transform whitout adjust pvalue. Called when adjusted method is not defined
|
|
704
|
+
compute_adjusted_pvalue(relations) do |pval, index|
|
|
705
|
+
pval
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
def compute_adjusted_pvalue_bonferroni(relations) # DONE
|
|
710
|
+
n_comparations = relations.length
|
|
711
|
+
compute_adjusted_pvalue(relations) do |pval, index|
|
|
712
|
+
adj = pval * n_comparations
|
|
713
|
+
adj = 1 if adj > 1
|
|
714
|
+
adj
|
|
715
|
+
end
|
|
716
|
+
end
|
|
307
717
|
|
|
308
|
-
def
|
|
718
|
+
def compute_adjusted_pvalue_benjaminiHochberg(relations) # DONE
|
|
719
|
+
adj_pvalues = get_benjaminiHochberg_pvalues(relations.map{|rel| rel.last})
|
|
720
|
+
compute_adjusted_pvalue(relations) do |pval, index|
|
|
721
|
+
adj_pvalues[index]
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
def get_csi_associations(layers, base_layer) # DONE
|
|
309
726
|
pcc_relations = get_pcc_associations(layers, base_layer)
|
|
727
|
+
pcc_relations.select!{|row| !row[2].nan?}
|
|
310
728
|
clean_autorelations_on_association_values if layers.length > 1
|
|
311
729
|
nx = get_nodes_layer(layers).length
|
|
312
730
|
pcc_vals = {}
|
|
@@ -321,167 +739,223 @@ class Network
|
|
|
321
739
|
pcc_relations.each do |node1, node2 ,assoc_index|
|
|
322
740
|
pccAB = assoc_index - 0.05
|
|
323
741
|
valid_nodes = 0
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
csiValue = 1 - (
|
|
331
|
-
# valid_nodes-1 is done due to the connection node1-node2 is counted twice (one for each loop)
|
|
742
|
+
|
|
743
|
+
significant_nodes_from_node1 = node_rels[node1].select{|node| pcc_vals[node1][node] >= pccAB}
|
|
744
|
+
significant_nodes_from_node2 = node_rels[node2].select{|node| pcc_vals[node2][node] >= pccAB}
|
|
745
|
+
all_significant_nodes = significant_nodes_from_node2 | significant_nodes_from_node1
|
|
746
|
+
all_nodes = node_rels[node1] | node_rels[node2]
|
|
747
|
+
|
|
748
|
+
csiValue = 1 - (all_significant_nodes.length).fdiv(all_nodes.length)
|
|
332
749
|
relations << [node1, node2, csiValue]
|
|
333
750
|
end
|
|
334
751
|
@association_values[:csi] = relations
|
|
335
752
|
return relations
|
|
336
753
|
end
|
|
337
754
|
|
|
755
|
+
def get_kernel(layer2kernel, kernel, normalization=false) # DONE
|
|
756
|
+
matrix, node_names = @adjacency_matrices[layer2kernel]
|
|
757
|
+
matrix_result = Adv_mat_calc.get_kernel(matrix, node_names, kernel, normalization=normalization)
|
|
758
|
+
@kernels[layer2kernel] = matrix_result
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
def write_kernel(layer2kernel, output_file) # DONE
|
|
762
|
+
@kernels[layer2kernel].save(output_file)
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
def link_ontology(ontology_file_path, layer_name) # NOT until semtools is migrated
|
|
766
|
+
if !@loaded_obos.include?(ontology_file_path) #Load new ontology
|
|
767
|
+
ontology = Ontology.new(file: ontology_file_path, load_file: true)
|
|
768
|
+
@loaded_obos << ontology_file_path
|
|
769
|
+
@ontologies << ontology
|
|
770
|
+
else #Link loaded ontology to current layer
|
|
771
|
+
ontology = @ontologies[@loaded_obos.index(ontology_file_path)]
|
|
772
|
+
end
|
|
773
|
+
@layer_ontologies[layer_name] = ontology
|
|
774
|
+
end
|
|
338
775
|
|
|
339
|
-
##
|
|
776
|
+
## RAMDOMIZATION METHODS
|
|
340
777
|
############################################################
|
|
341
|
-
def
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
778
|
+
def randomize_monopartite_net_by_nodes # DONE
|
|
779
|
+
layer = @layers.first
|
|
780
|
+
random_network = self.clone
|
|
781
|
+
if @adjacency_matrices[@layers].nil?
|
|
782
|
+
@adjacency_matrices[@layers] = @edges.to_bmatrix
|
|
783
|
+
end
|
|
784
|
+
nodeIds = @adjacency_matrices[@layers][1]
|
|
785
|
+
nodeIds.shuffle!
|
|
786
|
+
@adjacency_matrices[@layers][1] = nodeIds
|
|
787
|
+
@adjacency_matrices[@layers][2] = nodeIds
|
|
788
|
+
@edges = @adjacency_matrices[@layers].first.bmatrix_squared_to_hash(nodeIds) if @edges.empty?
|
|
789
|
+
return random_network
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
def randomize_bipartite_net_by_nodes
|
|
793
|
+
layerA = @layers.first
|
|
794
|
+
layerB = @layers.last
|
|
795
|
+
random_network = self.clone
|
|
796
|
+
if @adjacency_matrices[@layers].nil?
|
|
797
|
+
@adjacency_matrices[@layers] = @edges.to_bmatrix
|
|
798
|
+
end
|
|
799
|
+
rowIds = @adjacency_matrices[@layers][1]
|
|
800
|
+
colIds = @adjacency_matrices[@layers][2]
|
|
801
|
+
rowIds.shuffle!
|
|
802
|
+
@adjacency_matrices[@layers][1] = rowIds
|
|
803
|
+
@edges = @adjacency_matrices[@layers].first.bmatrix_rectangular_to_hash(rowIds, colIds) if !@edges.empty?
|
|
804
|
+
return random_network
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
def randomize_monopartite_net_by_links # DONE
|
|
808
|
+
layer = [@layers.first]
|
|
809
|
+
nodesA = []
|
|
810
|
+
nodesB = []
|
|
811
|
+
## cambio a la funcion creada en el numo_expansion
|
|
812
|
+
relations = diagonal2relations(@adjacency_matrices[layer].first, @adjacency_matrices[layer][1], @adjacency_matrices[layer][2])
|
|
813
|
+
relations.each do |relation|
|
|
814
|
+
nodesA << relation[0]
|
|
815
|
+
nodesB << relation[1]
|
|
816
|
+
end
|
|
817
|
+
nodesB.shuffle!
|
|
818
|
+
@edges = {}
|
|
819
|
+
nodesA.each do |nodeA|
|
|
820
|
+
index_nodeB = 0
|
|
821
|
+
while nodeA == nodesB[index_nodeB]
|
|
822
|
+
index_nodeB += 1
|
|
351
823
|
end
|
|
824
|
+
nodeB = nodesB.delete_at(index_nodeB)
|
|
825
|
+
add_edge(nodeA, nodeB)
|
|
352
826
|
end
|
|
353
|
-
|
|
354
|
-
return control
|
|
827
|
+
generate_adjacency_matrix(layer, layer)
|
|
355
828
|
end
|
|
356
829
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
830
|
+
|
|
831
|
+
def randomize_bipartite_net_by_links(layers)
|
|
832
|
+
nodesA = []
|
|
833
|
+
nodesB = []
|
|
834
|
+
#compruebo si existe la matriz
|
|
835
|
+
if @adjacency_matrices[layers].nil?
|
|
836
|
+
@adjacency_matrices[layers] = @edges.to_bmatrix()
|
|
837
|
+
end
|
|
838
|
+
relations = matrix2relations(@adjacency_matrices[layers].first, @adjacency_matrices[layers][1], @adjacency_matrices[layers][2])
|
|
839
|
+
relations.each do |relation|
|
|
840
|
+
nodesA << relation[0]
|
|
841
|
+
nodesB << relation[1]
|
|
842
|
+
end
|
|
843
|
+
nodesB.shuffle!
|
|
844
|
+
@edges = {}
|
|
845
|
+
|
|
846
|
+
nodesA.each_with_index do |nodeA, i|
|
|
847
|
+
add_edge(nodeA, nodesB[i])
|
|
848
|
+
end
|
|
849
|
+
generate_adjacency_matrix(layers[0], layers[1])
|
|
850
|
+
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
def randomize_network(random_type)
|
|
854
|
+
if random_type == 'nodes'
|
|
855
|
+
if @layers.length == 1
|
|
856
|
+
random_network = self.randomize_monopartite_net_by_nodes
|
|
857
|
+
elsif @layers.length == 2
|
|
858
|
+
random_network = self.randomize_bipartite_net_by_nodes
|
|
859
|
+
end
|
|
860
|
+
elsif random_type == 'links'
|
|
861
|
+
if @layers.length == 1
|
|
862
|
+
random_network = self.randomize_monopartite_net_by_links
|
|
863
|
+
elsif @layers.length == 2
|
|
864
|
+
random_network = self.randomize_bipartite_net_by_links
|
|
374
865
|
end
|
|
866
|
+
else
|
|
867
|
+
abort("ERROR: The randomization is not available for #{random_type} types of nodes")
|
|
375
868
|
end
|
|
376
|
-
return
|
|
869
|
+
return random_network
|
|
377
870
|
end
|
|
871
|
+
|
|
378
872
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
cuts = get_cuts(limits, cut_number)
|
|
385
|
-
cuts.each do |cut|
|
|
386
|
-
prec, rec = pred_rec(preds, cut, top_number)
|
|
387
|
-
performance << [cut, prec, rec]
|
|
873
|
+
def save_adjacency_matrix(layerA, layerB, output_file) # NOT
|
|
874
|
+
if layerA == layerB
|
|
875
|
+
layers = [layerA]
|
|
876
|
+
else
|
|
877
|
+
layers = [layerA, layerB]
|
|
388
878
|
end
|
|
389
|
-
|
|
879
|
+
Npy.save(output_file, @adjacency_matrices[layer].first)
|
|
880
|
+
node_names = @nodes.values.map{|node| node.id}
|
|
881
|
+
File.open(output_file+'.lst', 'w'){|f| f.print node_names.join("\n")}
|
|
390
882
|
end
|
|
391
883
|
|
|
392
|
-
def
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
@control_connections.each do |key, c_labels|
|
|
397
|
-
true_labels += c_labels.length #n
|
|
398
|
-
pred_info = preds[key]
|
|
399
|
-
if !pred_info.nil?
|
|
400
|
-
labels, scores = pred_info
|
|
401
|
-
reliable_labels = get_reliable_labels(labels, scores, cut, top)
|
|
402
|
-
predicted_labels += reliable_labels.length #m
|
|
403
|
-
common_labels += (c_labels & reliable_labels).length #k
|
|
404
|
-
end
|
|
884
|
+
def build_nodes_from_adjacency_matrix(layers_network, layers_adjacency_matrix) # NOT
|
|
885
|
+
nodes_ids = @adjacency_matrices[layers_adjacency_matrix][1].concat(@adjacency_matrices[layers_adjacency_matrix][2]).uniq
|
|
886
|
+
nodes_ids.each do |node_id|
|
|
887
|
+
add_node(node_id, set_layer(layers_network, node_id))
|
|
405
888
|
end
|
|
406
|
-
#puts "cut: #{cut} trueL: #{true_labels} predL: #{predicted_labels} commL: #{common_labels}"
|
|
407
|
-
prec = common_labels.to_f/predicted_labels
|
|
408
|
-
rec = common_labels.to_f/true_labels
|
|
409
|
-
prec = 0.0 if prec.nan?
|
|
410
|
-
rec = 0.0 if rec.nan?
|
|
411
|
-
return prec, rec
|
|
412
889
|
end
|
|
413
890
|
|
|
891
|
+
def build_edges_from_adjacency_matrix(layer) # NOT
|
|
892
|
+
@edges = {}
|
|
893
|
+
relations = matrix2relations(@adjacency_matrices[layer].first, @adjacency_matrices[layer][1], @adjacency_matrices[layer][2])
|
|
894
|
+
relations.each do |relation|
|
|
895
|
+
add_edge(relation[0], relation[1])
|
|
896
|
+
end
|
|
897
|
+
end
|
|
414
898
|
|
|
415
899
|
|
|
416
900
|
## AUXILIAR METHODS
|
|
417
901
|
#######################################################################################
|
|
418
902
|
private
|
|
419
903
|
|
|
420
|
-
def
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
end
|
|
904
|
+
def replace_nil_vals(val) # DONE
|
|
905
|
+
return val.nil? ? 'NULL' : val
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
def add_record(hash, node1, node2) # DONE
|
|
909
|
+
query = hash[node1]
|
|
910
|
+
if query.nil?
|
|
911
|
+
hash[node1] = [node2]
|
|
429
912
|
else
|
|
430
|
-
|
|
913
|
+
query << node2
|
|
431
914
|
end
|
|
432
|
-
return layer
|
|
433
915
|
end
|
|
434
916
|
|
|
435
|
-
def
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
cuts << (cut + n * range).to_f
|
|
917
|
+
def add_nested_record(hash, node1, node2, val) # DONE
|
|
918
|
+
query_node1 = hash[node1]
|
|
919
|
+
if query_node1.nil?
|
|
920
|
+
hash[node1] = {node2 => val}
|
|
921
|
+
else
|
|
922
|
+
query_node1[node2] = val
|
|
442
923
|
end
|
|
443
|
-
return cuts
|
|
444
924
|
end
|
|
445
925
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
926
|
+
def exist_connections?(ids_connected_to_n1, ids_connected_to_n2)
|
|
927
|
+
res = false
|
|
928
|
+
if !ids_connected_to_n1.nil? &&
|
|
929
|
+
!ids_connected_to_n2.nil? &&
|
|
930
|
+
!(ids_connected_to_n1 & ids_connected_to_n2).empty? # check that at least exists one node that connect to n1 and n2
|
|
931
|
+
res = true
|
|
450
932
|
end
|
|
451
|
-
|
|
452
|
-
return reliable_labels
|
|
933
|
+
return res
|
|
453
934
|
end
|
|
454
|
-
|
|
455
|
-
def
|
|
456
|
-
invMatrix = inputMatrix.sum(0).map{|e| 1.0/ e}
|
|
457
|
-
diagonalColSums = NMatrix.diag(invMatrix)
|
|
458
|
-
rowsSums = inputMatrix.sum(1).to_flat_a
|
|
459
|
-
ky = NMatrix.new([rowsNumber, rowsNumber], rowsSums).map{|e| e ** lambdaValue }
|
|
460
|
-
invertLambdaVal = (1 - lambdaValue)
|
|
461
|
-
kx = NMatrix.new([rowsNumber, rowsNumber], rowsSums).transpose.map{|e| e ** invertLambdaVal }
|
|
462
|
-
nx = (ky * kx).map{|e| 1.0/ e}
|
|
463
|
-
weigth = (inputMatrix.dot(diagonalColSums)).transpose
|
|
464
|
-
weigth = inputMatrix.dot(weigth)
|
|
465
|
-
weigth = nx * weigth
|
|
466
|
-
return weigth
|
|
467
|
-
end
|
|
468
|
-
|
|
469
|
-
def nmatrix2relations(finalMatrix, rowIds, colIds)
|
|
935
|
+
|
|
936
|
+
def matrix2relations(finalMatrix, rowIds, colIds) # DONE
|
|
470
937
|
relations = []
|
|
471
938
|
rowIds.each_with_index do |rowId, rowPos|
|
|
472
939
|
colIds.each_with_index do |colId, colPos|
|
|
473
940
|
associationValue = finalMatrix[rowPos, colPos]
|
|
474
|
-
relations << [rowId, colId, associationValue]
|
|
941
|
+
relations << [rowId, colId, associationValue] if associationValue > 0
|
|
475
942
|
end
|
|
476
943
|
end
|
|
477
944
|
return relations
|
|
478
945
|
end
|
|
479
946
|
|
|
480
|
-
def
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
947
|
+
def diagonal2relations(finalMatrix, rowIds, colIds)
|
|
948
|
+
relations = []
|
|
949
|
+
rowIds.each_with_index do |rowId, rowPos|
|
|
950
|
+
colIds.each_with_index do |colId, colPos|
|
|
951
|
+
colMatrix = rowPos + colPos + 1
|
|
952
|
+
if colMatrix < colIds.length
|
|
953
|
+
associationValue = finalMatrix[rowPos, colMatrix]
|
|
954
|
+
relations << [rowId, colIds[colMatrix], associationValue] if associationValue > 0
|
|
955
|
+
end
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
return relations
|
|
486
959
|
end
|
|
960
|
+
|
|
487
961
|
end
|