NetAnalyzer 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 727af54e55b813b7fa85e05009bcb96d448f0c53
4
+ data.tar.gz: de44f4108459832c9f4fa833d1b9145fc84d352c
5
+ SHA512:
6
+ metadata.gz: 23a875e1483ddda5f2ae1c5ad4c7e0b29bdde9b59255de09fd0f93fe2183263a3e5f197af50bad84e1164d669778439a17e40ead514d67e34312ed683c6fd2a3
7
+ data.tar.gz: 8a26685f7232671499994564613d6cb6171819905be75cd4501986deca6ce82812dc38802ae7b0ff62002209d775ec775394b8d2f9ca0ee5d52e59d081d8a7d2
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in NetAnalyzer.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 elenarojano
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'NetAnalyzer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "NetAnalyzer"
8
+ spec.version = NetAnalyzer::VERSION
9
+ spec.authors = ["Elena Rojano, Pedro Seoane"]
10
+ spec.email = ["elenarojano@uma.es, seoanezonjic@uma.es"]
11
+
12
+ spec.summary = %q{Network analysis tool that calculate and validate different association indices.}
13
+ spec.description = %q{NetAnalyzer is a useful network analysis tool developed in Ruby that can 1) analyse any type of unweighted network, regardless of the number of layers, 2) calculate the relationship between different layers, using various association indices (Jaccard, Simpson, PCC, geometric, cosine and hypergeometric) and 3) validate the results}
14
+ spec.homepage = "https://github.com/ElenaRojano/NetAnalyzer"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.11"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_dependency "nmatrix"
26
+ spec.add_dependency "bigdecimal"
27
+ end
@@ -0,0 +1,41 @@
1
+ # NetAnalyzer
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/NetAnalyzer`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'NetAnalyzer'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install NetAnalyzer
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ElenaRojano/NetAnalyzer.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,156 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ ROOT_PATH = File.dirname(__FILE__)
4
+ $: << File.expand_path(File.join(ROOT_PATH, '..', 'lib', 'NetAnalyzer'))
5
+ $: << File.expand_path(File.join(ROOT_PATH, '..', 'lib', 'NetAnalyzer', 'methods'))
6
+
7
+ require 'network'
8
+ require 'optparse'
9
+
10
+ ##############################
11
+ # MAIN METHODS
12
+ ##############################
13
+
14
+ def set_layer(layer_definitions, node_name)
15
+ layer = nil
16
+ if layer_definitions.length > 1
17
+ layer_definitions.each do |layer_name, regexp|
18
+ if node_name =~ regexp
19
+ layer = layer_name
20
+ break
21
+ end
22
+ end
23
+ else
24
+ layer = layer_definitions.first.first
25
+ end
26
+ return layer
27
+ end
28
+
29
+ ##############################
30
+ #OPTPARSE
31
+ ##############################
32
+
33
+ options = {}
34
+ OptionParser.new do |opts|
35
+ opts.banner = "Usage: #{__FILE__} [options]"
36
+
37
+ options[:input_file] = nil
38
+ opts.on("-i", "--input_file PATH", "Input file to create bipartite networks for further analysis") do |input_file|
39
+ options[:input_file] = input_file
40
+ end
41
+
42
+ options[:split_char] = "\t"
43
+ opts.on("-s", "--split_char STRING", "Character for splitting input file. Default: tab") do |split_char|
44
+ options[:split_char] = split_char
45
+ end
46
+
47
+ options[:output_file] = "network2plot"
48
+ opts.on("-o", "--output_file PATH", "Output file name") do |output_file|
49
+ options[:output_file] = output_file
50
+ end
51
+
52
+ options[:assoc_file] = "assoc_values.txt"
53
+ opts.on("-a", "--assoc_file PATH", "Output file name for association values") do |output_file|
54
+ options[:assoc_file] = output_file
55
+ end
56
+
57
+ options[:performance_file] = "perf_values.txt"
58
+ opts.on("-p", "--performance_file PATH", "Output file name for performance values") do |output_file|
59
+ options[:performance_file] = output_file
60
+ end
61
+
62
+ options[:layers] = [:layer, '-']
63
+ opts.on("-l", "--layers STRING", "Layer definition on network: layer1name,regexp1;layer2name,regexp2...") do |layers|
64
+ layers_definition = layers.split(";").map{|layer_attr| layer_attr.split(',')}
65
+ layers_definition.map!{|layer_attr| [layer_attr.first.to_sym, /#{layer_attr.last}/]}
66
+ options[:layers] = layers_definition
67
+ end
68
+
69
+ options[:use_layers] = []
70
+ opts.on("-u", "--use_layers STRING", "Set which layers must be used on association methods: layer1,layer2;layerA,layerB") do |string|
71
+ options[:use_layers] = string.split(";").map{|layer_attr| layer_attr.split(',').map{|layer| layer.to_sym}}
72
+ end
73
+
74
+ options[:control_file] = nil
75
+ opts.on("-c", "--control_file PATH", "Control file name") do |file|
76
+ options[:control_file] = file
77
+ end
78
+
79
+ options[:output_style] = "neato"
80
+ opts.on("-t", "--output_style STRING", "Style to plot output file") do |output_style|
81
+ options[:output_style] = output_style
82
+ end
83
+
84
+ options[:meth] = nil
85
+ opts.on("-m", "--association_method STRING", "Association method to use on network") do |meth|
86
+ options[:meth] = meth.to_sym
87
+ end
88
+
89
+ options[:no_autorelations] = FALSE
90
+ opts.on("-N", "--no_autorelations", "Remove association values between nodes os same type") do
91
+ options[:no_autorelations] = TRUE
92
+ end
93
+
94
+ end.parse!
95
+
96
+ ##########################
97
+ #MAIN
98
+ ##########################
99
+
100
+ fullNet = Network.new(options[:layers].map{|layer| layer.first})
101
+ puts "Loading network data"
102
+ File.open(options[:input_file]).each("\n") do |line|
103
+ line.chomp!
104
+ pair = line.split(options[:splitChar])
105
+ node1 = pair[0]
106
+ node2 = pair[1]
107
+ fullNet.add_node(node1, set_layer(options[:layers], node1))
108
+ fullNet.add_node(node2, set_layer(options[:layers], node2))
109
+ fullNet.add_edge(node1, node2)
110
+ end
111
+ #fullNet.plot(options[:output_file], options[:output_style])
112
+
113
+
114
+ if !options[:meth].nil?
115
+ puts "Performing association method #{options[:meth]} on network"
116
+ if options[:meth] == :transference
117
+ fullNet.generate_adjacency_matrix(options[:use_layers][0][0], options[:use_layers][0][1])
118
+ fullNet.generate_adjacency_matrix(options[:use_layers][1][0], options[:use_layers][1][1])
119
+ fullNet.get_association_values(
120
+ [options[:use_layers][0][0], options[:use_layers][0][1]],
121
+ [options[:use_layers][1][0], options[:use_layers][1][1]],
122
+ :transference)
123
+ else
124
+ fullNet.get_association_values(
125
+ options[:use_layers][0],
126
+ options[:use_layers][1].first,
127
+ options[:meth])
128
+ end
129
+ puts 'Clean autorelations' if options[:no_autorelations]
130
+ fullNet.clean_autorelations_on_association_values if options[:no_autorelations]
131
+ File.open(options[:assoc_file], 'w') do |f|
132
+ fullNet.association_values[options[:meth]].each do |val|
133
+ f.puts val.join("\t")
134
+ end
135
+ end
136
+ end
137
+
138
+ if !options[:meth].nil? && !options[:control_file].nil?
139
+ puts "Doing validation on association values obtained from method #{options[:meth]}"
140
+ control = []
141
+ File.open(options[:control_file]).each("\n") do |line|
142
+ line.chomp!
143
+ control << line.split("\t")
144
+ end
145
+ fullNet.load_control(control)
146
+ performance = fullNet.get_pred_rec(options[:meth])
147
+ File.open(options[:performance_file], 'w') do |f|
148
+ f.puts %w[cut prec rec meth].join("\t")
149
+ performance.each do |item|
150
+ item << options[:meth].to_s
151
+ f.puts item.join("\t")
152
+ end
153
+ end
154
+ end
155
+
156
+ puts "End of analysis: #{options[:meth]}"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "NetAnalyzer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ require "NetAnalyzer/version"
2
+
3
+ module NetAnalyzer
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,452 @@
1
+ require 'nodes'
2
+ require 'nmatrix'
3
+ require 'pp'
4
+ require 'bigdecimal'
5
+
6
+ class Network
7
+ attr_reader :association_values
8
+
9
+ ## BASIC METHODS
10
+ ############################################################
11
+ def initialize(layers)
12
+ @nodes = {}
13
+ @edges = {}
14
+ @adjacency_matrices = {}
15
+ @layers = layers
16
+ @association_values = {}
17
+ @control_connections = {}
18
+ end
19
+
20
+ def add_node(nodeID, nodeType = 0)
21
+ @nodes[nodeID] = Node.new(nodeID, nodeType)
22
+ end
23
+
24
+ def add_edge(nodeID1, nodeID2)
25
+ query_edge(nodeID1, nodeID2)
26
+ query_edge(nodeID2, nodeID1)
27
+ end
28
+
29
+ def query_edge(nodeA, nodeB)
30
+ query = @edges[nodeA]
31
+ if query.nil?
32
+ @edges[nodeA] = [nodeB]
33
+ else
34
+ query << nodeB
35
+ end
36
+ end
37
+
38
+ def plot(output_filename, layout="dot")
39
+ roboWrite = File.open(output_filename, 'w')
40
+ roboWrite.puts "digraph g {"
41
+ @edges.each do |nodeID, associatedIDs|
42
+ associatedIDs.each do |associatedID|
43
+ roboWrite.puts "\"#{nodeID}\"->\"#{associatedID}\";"
44
+ end
45
+ end
46
+ roboWrite.puts "}"
47
+ roboWrite.close
48
+ cmd = "#{layout} -Tpng #{output_filename} -o #{output_filename}.png"
49
+ system(cmd)
50
+ end
51
+
52
+ def get_all_intersections
53
+ intersection_lengths = []
54
+ get_all_pairs do |node1, node2|
55
+ intersection_lengths << intersection(node1, node2).length
56
+ end
57
+ return intersection_lengths
58
+ end
59
+
60
+ def get_all_pairs(args = {})
61
+ default = {:meth => :all, :layers => :all}
62
+ args = default.merge(args)
63
+ if args[:layers] == :all
64
+ nodeIDs = @nodes.keys
65
+ else
66
+ nodeIDs = []
67
+ args[:layers].each do |layer|
68
+ nodeIDs.concat(@nodes.select{|id, node| node.type == layer}.keys)
69
+ end
70
+ end
71
+
72
+ if args[:meth] == :all
73
+ while !nodeIDs.empty?
74
+ node1 = nodeIDs.shift
75
+ nodeIDs.each do |node2|
76
+ yield(node1, node2)
77
+ end
78
+ end
79
+ #elsif args[:meth] == :conn
80
+
81
+ end
82
+ end
83
+
84
+ def get_nodes_layer(layers)
85
+ #for creating ny value in hypergeometric and pcc index
86
+ nodes = []
87
+ layers.each do |layer|
88
+ nodes.concat(@nodes.select{|nodeId, node| node.type == layer}.values)
89
+ end
90
+ return nodes
91
+ end
92
+
93
+ def intersection(node1, node2)
94
+ shared_nodes = []
95
+ associatedIDs_node1 = @edges[node1]
96
+ associatedIDs_node2 = @edges[node2]
97
+ intersectedIDs = associatedIDs_node1 & associatedIDs_node2
98
+ intersectedIDs.each do |id|
99
+ shared_nodes << @nodes[id]
100
+ end
101
+ return shared_nodes
102
+ end
103
+
104
+ def generate_adjacency_matrix(layerA, layerB)
105
+ layerAidNodes = @nodes.select{|id, node| node.type == layerA}.keys
106
+ layerBidNodes = @nodes.select{|id, node| node.type == layerB}.keys
107
+ adjacency_matrix = []
108
+ layerAidNodes.each do |nodeA|
109
+ layerBidNodes.each do |nodeB|
110
+ if @edges[nodeB].include?(nodeA)
111
+ adjacency_matrix << 1
112
+ else
113
+ adjacency_matrix << 0
114
+ end
115
+ end
116
+ end
117
+ matrix = NMatrix.new([layerAidNodes.length, layerBidNodes.length], adjacency_matrix)
118
+ all_info_matrix = [matrix, layerAidNodes, layerBidNodes]
119
+ @adjacency_matrices[[layerA, layerB]] = all_info_matrix
120
+ return all_info_matrix
121
+ end
122
+
123
+ def clean_autorelations_on_association_values
124
+ @association_values.each do |meth, values|
125
+ values.select!{|relation| @nodes[relation[0]].type != @nodes[relation[1]].type}
126
+ end
127
+ end
128
+
129
+ ## ASSOCIATION METHODS
130
+ ############################################################
131
+ def get_association_values(layers, base_layer, meth)
132
+ relations = [] #node A, node B, val
133
+ if meth == :jaccard #all networks
134
+ relations = get_jaccard_association(layers, base_layer)
135
+ elsif meth == :simpson #all networks
136
+ relations = get_simpson_association(layers, base_layer)
137
+ elsif meth == :geometric #all networks
138
+ relations = get_geometric_associations(layers, base_layer)
139
+ elsif meth == :cosine #all networks
140
+ relations = get_cosine_associations(layers, base_layer)
141
+ elsif meth == :pcc #all networks
142
+ relations = get_pcc_associations(layers, base_layer)
143
+ elsif meth == :hypergeometric #all networks
144
+ relations = get_hypergeometric_associations(layers, base_layer)
145
+ elsif meth == :csi #all networks
146
+ relations = get_csi_associations(layers, base_layer)
147
+ elsif meth == :transference #tripartite networks
148
+ relations = get_association_by_transference_resources(layers, base_layer)
149
+ end
150
+ return relations
151
+ end
152
+
153
+ ## association methods adjacency matrix based
154
+ #---------------------------------------------------------
155
+ # Alaimo 2014, doi: 10.3389/fbioe.2014.00071
156
+ def get_association_by_transference_resources(firstPairLayers, secondPairLayers, lambda_value1 = 0.5, lambda_value2 = 0.5)
157
+ matrix1 = @adjacency_matrices[firstPairLayers].first
158
+ rowIds = @adjacency_matrices[firstPairLayers][1]
159
+ matrix2 = @adjacency_matrices[secondPairLayers].first
160
+ colIds = @adjacency_matrices[secondPairLayers][2]
161
+ m1rowNumber = matrix1.rows
162
+ m1colNumber = matrix1.cols
163
+ m2rowNumber = matrix2.rows
164
+ m2colNumber = matrix2.cols
165
+ #puts m1rowNumber, m1colNumber, m2rowNumber, m2colNumber
166
+ matrix1Weight = graphWeights(m1colNumber, m1rowNumber, matrix1.transpose, lambda_value1)
167
+ matrix2Weight = graphWeights(m2colNumber, m2rowNumber, matrix2.transpose, lambda_value2)
168
+ matrixWeightProduct = matrix1Weight.dot(matrix2.dot(matrix2Weight))
169
+ finalMatrix = matrix1.dot(matrixWeightProduct)
170
+ relations = nmatrix2relations(finalMatrix, rowIds, colIds)
171
+ @association_values[:transference] = relations
172
+ return relations
173
+ end
174
+
175
+ ## association methods node pairs based
176
+ #---------------------------------------------------------
177
+ # Bass 2013, doi:10.1038/nmeth.2728
178
+ def get_associations(layers, base_layer) # BASE METHOD
179
+ relations = []
180
+ get_all_pairs(layers: layers) do |node1, node2|
181
+ associatedIDs_node1 = @edges[node1].map{|id| @nodes[id]}.select{|node| node.type == base_layer}.map{|node| node.id}
182
+ associatedIDs_node2 = @edges[node2].map{|id| @nodes[id]}.select{|node| node.type == base_layer}.map{|node| node.id}
183
+ intersectedIDs = associatedIDs_node1 & associatedIDs_node2
184
+ associationValue = yield(associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2)
185
+ relations << [node1, node2, associationValue]
186
+ end
187
+ return relations
188
+ end
189
+
190
+ def get_jaccard_association(layers, base_layer)
191
+ relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
192
+ unionIDS = associatedIDs_node1 | associatedIDs_node2
193
+ jaccValue = intersectedIDs.length.to_f/unionIDS.length
194
+ end
195
+ @association_values[:jaccard] = relations
196
+ return relations
197
+ end
198
+
199
+ def get_simpson_association(layers, base_layer)
200
+ relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
201
+ minLength = [associatedIDs_node1.length, associatedIDs_node2.length].min
202
+ simpsonValue = intersectedIDs.length.to_f/minLength
203
+ end
204
+ @association_values[:simpson] = relations
205
+ return relations
206
+ end
207
+
208
+ def get_geometric_associations(layers, base_layer)
209
+ relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
210
+ intersectedIDs = intersectedIDs.length**2
211
+ productLength = associatedIDs_node1.length * associatedIDs_node2.length
212
+ geometricValue = intersectedIDs.to_f/productLength
213
+ end
214
+ @association_values[:geometric] = relations
215
+ return relations
216
+ end
217
+
218
+ def get_cosine_associations(layers, base_layer)
219
+ relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
220
+ productLength = Math.sqrt(associatedIDs_node1.length * associatedIDs_node2.length)
221
+ cosineValue = intersectedIDs.length/productLength
222
+ end
223
+ @association_values[:cosine] = relations
224
+ return relations
225
+ end
226
+
227
+ def get_pcc_associations(layers, base_layer)
228
+ #for Ny calcule use get_nodes_layer
229
+ ny = get_nodes_layer(layers).length
230
+ relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
231
+ intersProd = intersectedIDs.length * ny
232
+ nodesProd = associatedIDs_node1.length * associatedIDs_node2.length
233
+ nodesSubs = intersProd - nodesProd
234
+ nodesAInNetwork = ny - associatedIDs_node1.length
235
+ nodesBInNetwork = ny - associatedIDs_node2.length
236
+ pccValue = nodesSubs.to_f / Math.sqrt(nodesProd * nodesAInNetwork * nodesBInNetwork)
237
+ end
238
+ @association_values[:pcc] = relations
239
+ return relations
240
+ end
241
+
242
+ def get_hypergeometric_associations(layers, base_layer)
243
+ ny = get_nodes_layer(layers).length
244
+ relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
245
+ minLength = [associatedIDs_node1.length, associatedIDs_node2.length].min
246
+ intersection_lengths = intersectedIDs.length
247
+ sum = 0
248
+ nA = associatedIDs_node1.length
249
+ nB = associatedIDs_node2.length
250
+ #Using index from A layer proyected to B
251
+ (intersection_lengths..minLength).each do |i|
252
+ binom_product = binom(nA, i) * binom(ny - nA, nB - i)
253
+ binom_product_float = binom_product.to_f
254
+ to_f = false
255
+ if binom_product_float.infinite? # Handle bignum coercition to bigdecimal to avoid infinity values on float class.
256
+ binom_product_float = BigDecimal.new(binom_product)
257
+ to_f = true
258
+ end
259
+ sum += binom_product_float / binom(ny, nB)
260
+ sum = sum.to_f if to_f # once the operation has finished, sum is corced from bigdecimal to float
261
+ end
262
+ if sum == 0
263
+ hypergeometricValue = 0
264
+ else
265
+ hypergeometricValue = -Math.log10(sum)
266
+ end
267
+ hypergeometricValue
268
+ end
269
+ @association_values[:hypergeometric] = relations
270
+ return relations
271
+ end
272
+
273
+ def add_record(hash, key, key2, value)
274
+ query = hash[key]
275
+ if query.nil?
276
+ hash[key]={key2 => value}
277
+ else
278
+ query[key2] = value
279
+ end
280
+ end
281
+
282
+ def get_csi_associations(layers, base_layer)
283
+ pcc_relations = get_pcc_associations(layers, base_layer)
284
+ indexed_pcc_relations = {}
285
+ pcc_relations.each do |node1, node2, assoc_index|
286
+ if assoc_index > 0
287
+ add_record(indexed_pcc_relations, node1, node2, assoc_index)
288
+ add_record(indexed_pcc_relations, node2, node1, assoc_index)
289
+ end
290
+ end
291
+ ny = get_nodes_layer(layers).length
292
+ relations = get_associations(layers, base_layer) do |associatedIDs_node1, associatedIDs_node2, intersectedIDs, node1, node2|
293
+ query = indexed_pcc_relations[node1]
294
+ if query.nil?
295
+ valid_A_nodes = []
296
+ pccAB = -0.05
297
+ else
298
+ nested_query = query[node2]
299
+ if nested_query.nil?
300
+ pccAB = -0.05
301
+ else
302
+ pccAB = nested_query - 0.05
303
+ end
304
+ valid_A_nodes = query.select{|node_id, pcc| pcc>= pccAB}.keys
305
+ end
306
+ query2 = indexed_pcc_relations[node2]
307
+ if query2.nil?
308
+ valid_B_nodes = []
309
+ else
310
+ valid_B_nodes = query2.select{|node_id, pcc| pcc>= pccAB}.keys
311
+ end
312
+ valid_connections = valid_A_nodes | valid_B_nodes
313
+ csiValue = 1 - valid_connections.length.to_f/ny
314
+ end
315
+ @association_values[:csi] = relations
316
+ return relations
317
+ end
318
+
319
+ ## PERFORMANCE METHODS
320
+ ############################################################
321
+ def load_control(ref_array)
322
+ control = {}
323
+ ref_array.each do |node1, node2|
324
+ if node2 != '-'
325
+ query = control[node1]
326
+ if query.nil?
327
+ control[node1] = [node2]
328
+ else
329
+ query << node2
330
+ end
331
+ end
332
+ end
333
+ @control_connections = control
334
+ return control
335
+ end
336
+
337
+ def load_prediction(pairs_array)
338
+ pred = {}
339
+ min = nil
340
+ max = nil
341
+ pairs_array.each do |key, label, score|
342
+ query = pred[key]
343
+ if !min.nil? && !max.nil?
344
+ min = score if score < min
345
+ max = score if score > max
346
+ else
347
+ min = score; max = score
348
+ end
349
+ if query.nil?
350
+ pred[key] = [[label], [score]]
351
+ else
352
+ query.first << label
353
+ query.last << score
354
+ end
355
+ end
356
+ return pred, [min, max]
357
+ end
358
+
359
+
360
+ # Pandey 2007, Association Analysis-based Transformations for Protein Interaction Networks: A Function Prediction Case Study
361
+ def get_pred_rec(meth, cut_number = 100, top_number = 10000)
362
+ performance = [] #cut, pred, rec
363
+ preds, limits = load_prediction(@association_values[meth])
364
+ cuts = get_cuts(limits, cut_number)
365
+ cuts.each do |cut|
366
+ prec, rec = pred_rec(preds, cut, top_number)
367
+ performance << [cut, prec, rec]
368
+ end
369
+ return performance
370
+ end
371
+
372
+ def pred_rec(preds, cut, top)
373
+ predicted_labels = 0 #m
374
+ true_labels = 0 #n
375
+ common_labels = 0 # k
376
+ @control_connections.each do |key, c_labels|
377
+ true_labels += c_labels.length #n
378
+ pred_info = preds[key]
379
+ if !pred_info.nil?
380
+ labels, scores = pred_info
381
+ reliable_labels = get_reliable_labels(labels, scores, cut, top)
382
+
383
+ predicted_labels += reliable_labels.length #m
384
+ common_labels += (c_labels & reliable_labels).length #k
385
+ end
386
+ end
387
+ prec = common_labels.to_f/predicted_labels
388
+ rec = common_labels.to_f/true_labels
389
+ prec = 0.0 if prec.nan?
390
+ rec = 0.0 if rec.nan?
391
+ return prec, rec
392
+ end
393
+
394
+
395
+
396
+ ## AUXILIAR METHODS
397
+ #######################################################################################
398
+ private
399
+
400
+ def get_cuts(limits, n_cuts)
401
+ cuts = []
402
+ range = (limits.last - limits.first).to_f/n_cuts
403
+ cut = limits.first
404
+ n_cuts.times do
405
+ cuts << cut
406
+ cut += range
407
+ end
408
+ return cuts
409
+ end
410
+
411
+ def get_reliable_labels(labels, scores, cut, top)
412
+ reliable_labels = []
413
+ scores.each_with_index do |score, i|
414
+ reliable_labels << [labels[i], score] if score >= cut
415
+ end
416
+ reliable_labels = reliable_labels.sort!{|l1,l2| l2.last <=> l1.last}[0..top-1].map{|pred| pred.first}
417
+ return reliable_labels
418
+ end
419
+
420
+ def graphWeights (rowsNumber, colsNumber, inputMatrix, lambdaValue = 0.5)
421
+ invMatrix = inputMatrix.sum(0).map{|e| 1.0/ e}
422
+ diagonalColSums = NMatrix.diag(invMatrix)
423
+ rowsSums = inputMatrix.sum(1).to_flat_a
424
+ ky = NMatrix.new([rowsNumber, rowsNumber], rowsSums).map{|e| e ** lambdaValue }
425
+ invertLambdaVal = (1 - lambdaValue)
426
+ kx = NMatrix.new([rowsNumber, rowsNumber], rowsSums).transpose.map{|e| e ** invertLambdaVal }
427
+ nx = (ky * kx).map{|e| 1.0/ e}
428
+ weigth = (inputMatrix.dot(diagonalColSums)).transpose
429
+ weigth = inputMatrix.dot(weigth)
430
+ weigth = nx * weigth
431
+ return weigth
432
+ end
433
+
434
+ def nmatrix2relations(finalMatrix, rowIds, colIds)
435
+ relations = []
436
+ rowIds.each_with_index do |rowId, rowPos|
437
+ colIds.each_with_index do |colId, colPos|
438
+ associationValue = finalMatrix[rowPos, colPos]
439
+ relations << [rowId, colId, associationValue]
440
+ end
441
+ end
442
+ return relations
443
+ end
444
+
445
+ def binom(n,k)
446
+ if k > 0 && k < n
447
+ res = (1+n-k..n).inject(:*)/(1..k).inject(:*)
448
+ else
449
+ res = 1
450
+ end
451
+ end
452
+ end
@@ -0,0 +1,7 @@
1
+ class Node
2
+ attr_reader :type, :id
3
+ def initialize(id, type)
4
+ @id = id
5
+ @type = type
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module NetAnalyzer
2
+ VERSION = "0.1.1"
3
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: NetAnalyzer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Elena Rojano, Pedro Seoane
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nmatrix
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bigdecimal
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: NetAnalyzer is a useful network analysis tool developed in Ruby that
84
+ can 1) analyse any type of unweighted network, regardless of the number of layers,
85
+ 2) calculate the relationship between different layers, using various association
86
+ indices (Jaccard, Simpson, PCC, geometric, cosine and hypergeometric) and 3) validate
87
+ the results
88
+ email:
89
+ - elenarojano@uma.es, seoanezonjic@uma.es
90
+ executables:
91
+ - NetAnalyzer.rb
92
+ - console
93
+ - setup
94
+ extensions: []
95
+ extra_rdoc_files: []
96
+ files:
97
+ - ".gitignore"
98
+ - ".rspec"
99
+ - ".travis.yml"
100
+ - Gemfile
101
+ - LICENSE.txt
102
+ - NetAnalyzer.gemspec
103
+ - README.md
104
+ - Rakefile
105
+ - bin/NetAnalyzer.rb
106
+ - bin/console
107
+ - bin/setup
108
+ - lib/NetAnalyzer.rb
109
+ - lib/NetAnalyzer/network.rb
110
+ - lib/NetAnalyzer/nodes.rb
111
+ - lib/NetAnalyzer/version.rb
112
+ homepage: https://github.com/ElenaRojano/NetAnalyzer
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.4.8
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Network analysis tool that calculate and validate different association indices.
136
+ test_files: []