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.
@@ -1,34 +1,94 @@
1
- require 'nodes'
2
- require 'nmatrix'
3
- require 'pp'
4
- require 'bigdecimal'
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
- class Network
13
+ require 'semtools'
14
+ require 'expcalc'
8
15
 
9
- attr_accessor :association_values, :control_connections
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
- @nodes = {}
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 add_node(nodeID, nodeType = 0)
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
- query_edge(nodeID1, nodeID2)
28
- query_edge(nodeID2, nodeID1)
86
+ def add_edge(nodeID1, nodeID2) # DONE
87
+ add_edge2hash(nodeID1, nodeID2)
88
+ add_edge2hash(nodeID2, nodeID1)
29
89
  end
30
90
 
31
- def query_edge(nodeA, nodeB)
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 load_network_by_pairs(file, layers, split_character="\t")
41
- File.open(file).each("\n") do |line|
42
- line.chomp!
43
- pair = line.split(split_character)
44
- node1 = pair[0]
45
- node2 = pair[1]
46
- add_node(node1, set_layer(layers, node1))
47
- add_node(node2, set_layer(layers, node2))
48
- add_edge(node1, node2)
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 get_edge_number
53
- node_connections = @edges.values.map{|connections| connections.length}.inject(0){|sum, n| sum + n}
54
- return node_connections/2
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
- def plot(output_filename, layout="dot")
58
- roboWrite = File.open(output_filename, 'w')
59
- roboWrite.puts "digraph g {"
60
- @edges.each do |nodeID, associatedIDs|
61
- associatedIDs.each do |associatedID|
62
- roboWrite.puts "\"#{nodeID}\"->\"#{associatedID}\";"
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
- roboWrite.puts "}"
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 get_all_intersections
72
- intersection_lengths = []
73
- get_all_pairs do |node1, node2|
74
- intersection_lengths << intersection(node1, node2).length
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
- default = {:meth => :all, :layers => :all}
81
- args = default.merge(args)
82
- if args[:layers] == :all
83
- nodeIDs = @nodes.keys
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
- nodeIDs = []
86
- args[:layers].each do |layer|
87
- nodeIDs.concat(@nodes.select{|id, node| node.type == layer}.keys)
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
- if args[:meth] == :all
92
- while !nodeIDs.empty?
93
- node1 = nodeIDs.shift
94
- nodeIDs.each do |node2|
95
- yield(node1, node2)
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
- #elsif args[:meth] == :conn
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
- associatedIDs_node1 = @edges[node1]
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 generate_adjacency_matrix(layerA, layerB)
124
- layerAidNodes = @nodes.select{|id, node| node.type == layerA}.keys
125
- layerBidNodes = @nodes.select{|id, node| node.type == layerB}.keys
126
- adjacency_matrix = []
127
- layerAidNodes.each do |nodeA|
128
- layerBidNodes.each do |nodeB|
129
- if @edges[nodeB].include?(nodeA)
130
- adjacency_matrix << 1
131
- else
132
- adjacency_matrix << 0
133
- end
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
- matrix = NMatrix.new([layerAidNodes.length, layerBidNodes.length], adjacency_matrix)
137
- all_info_matrix = [matrix, layerAidNodes, layerBidNodes]
138
- @adjacency_matrices[[layerA, layerB]] = all_info_matrix
139
- return all_info_matrix
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 clean_autorelations_on_association_values
143
- @association_values.each do |meth, values|
144
- values.select!{|relation| @nodes[relation[0]].type != @nodes[relation[1]].type}
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 get_association_values(layers, base_layer, meth)
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 == :jaccard #all networks
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
- # Alaimo 2014, doi: 10.3389/fbioe.2014.00071
175
- def get_association_by_transference_resources(firstPairLayers, secondPairLayers, lambda_value1 = 0.5, lambda_value2 = 0.5)
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
- m1rowNumber = matrix1.rows
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
- relations = []
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
- relations << [node1, node2, associationValue]
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
- ny = get_nodes_layer([base_layer]).length
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
- minLength = [associatedIDs_node1.length, associatedIDs_node2.length].min
640
+ fisher = 0
266
641
  intersection_lengths = intersectedIDs.length
267
- sum = 0
268
642
  if intersection_lengths > 0
269
- nA = associatedIDs_node1.length
270
- nB = associatedIDs_node2.length
271
- #Using index from A layer proyected to B
272
- hyper_denom = binom(ny, nB)
273
- (intersection_lengths..minLength).each do |i|
274
- binom_product = binom(nA, i) * binom(ny - nA, nB - i)
275
- sum += binom_product.fdiv(hyper_denom)
276
- end
277
- end
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
- hypergeometricValue
653
+ fisher
284
654
  end
285
- @association_values[:hypergeometric] = relations
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 add_record(hash, node1, node2)
290
- query = hash[node1]
291
- if query.nil?
292
- hash[node1] = [node2]
293
- else
294
- query << node2
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 add_nested_record(hash, node1, node2, val)
299
- query_node1 = hash[node1]
300
- if query_node1.nil?
301
- hash[node1] = {node2 => val}
302
- else
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 get_csi_associations(layers, base_layer)
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
- node_rels[node1].each do |node|
325
- valid_nodes += 1 if pcc_vals[node1][node] >= pccAB
326
- end
327
- node_rels[node2].each do |node|
328
- valid_nodes += 1 if pcc_vals[node2][node] >= pccAB
329
- end
330
- csiValue = 1 - (valid_nodes-1).fdiv(nx)
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
- ## PERFORMANCE METHODS
776
+ ## RAMDOMIZATION METHODS
340
777
  ############################################################
341
- def load_control(ref_array)
342
- control = {}
343
- ref_array.each do |node1, node2|
344
- if node2 != '-'
345
- query = control[node1]
346
- if query.nil?
347
- control[node1] = [node2]
348
- else
349
- query << node2
350
- end
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
- @control_connections = control
354
- return control
827
+ generate_adjacency_matrix(layer, layer)
355
828
  end
356
829
 
357
- def load_prediction(pairs_array)
358
- pred = {}
359
- min = nil
360
- max = nil
361
- pairs_array.each do |key, label, score|
362
- query = pred[key]
363
- if !min.nil? && !max.nil?
364
- min = score if score < min
365
- max = score if score > max
366
- else
367
- min = score; max = score
368
- end
369
- if query.nil?
370
- pred[key] = [[label], [score]]
371
- else
372
- query.first << label
373
- query.last << score
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 pred, [min, max]
869
+ return random_network
377
870
  end
871
+
378
872
 
379
-
380
- # Pandey 2007, Association Analysis-based Transformations for Protein Interaction Networks: A Function Prediction Case Study
381
- def get_pred_rec(meth, cut_number = 100, top_number = 10000)
382
- performance = [] #cut, pred, rec
383
- preds, limits = load_prediction(@association_values[meth])
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
- return performance
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 pred_rec(preds, cut, top)
393
- predicted_labels = 0 #m
394
- true_labels = 0 #n
395
- common_labels = 0 # k
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 set_layer(layer_definitions, node_name)
421
- layer = nil
422
- if layer_definitions.length > 1
423
- layer_definitions.each do |layer_name, regexp|
424
- if node_name =~ regexp
425
- layer = layer_name
426
- break
427
- end
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
- layer = layer_definitions.first.first
913
+ query << node2
431
914
  end
432
- return layer
433
915
  end
434
916
 
435
- def get_cuts(limits, n_cuts)
436
- cuts = []
437
- range = (limits.last - limits.first).abs.fdiv(n_cuts)
438
- range = BigDecimal(range, 10)
439
- cut = limits.first
440
- (n_cuts + 1).times do |n|
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
- def get_reliable_labels(labels, scores, cut, top)
447
- reliable_labels = []
448
- scores.each_with_index do |score, i|
449
- reliable_labels << [labels[i], score] if score >= cut
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
- reliable_labels = reliable_labels.sort!{|l1,l2| l2.last <=> l1.last}[0..top-1].map{|pred| pred.first}
452
- return reliable_labels
933
+ return res
453
934
  end
454
-
455
- def graphWeights (rowsNumber, colsNumber, inputMatrix, lambdaValue = 0.5)
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 binom(n,k)
481
- if k > 0 && k < n
482
- res = (1+n-k..n).inject(:*)/(1..k).inject(:*)
483
- else
484
- res = 1
485
- end
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