rgraph 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/Guardfile CHANGED
@@ -1,5 +1,5 @@
1
1
  guard :rspec do
2
2
  watch(%r{^spec/(.+)/.+_spec\.rb$})
3
- watch(%r{^lib/(.+)/(.+)\.rb$}) { |m| "spec/m[1]/#{m[2]}_spec.rb" }
3
+ watch(%r{^lib/(.+)/(.+)\.rb$}) { |m| "spec/#{m[1]}/#{m[2]}_spec.rb" }
4
4
  watch('spec/spec_helper.rb') { "spec" }
5
5
  end
data/lib/rgraph/graph.rb CHANGED
@@ -1,14 +1,16 @@
1
+ #encoding: utf-8
1
2
  require 'csv'
2
3
  require_relative '../../lib/rgraph/link'
3
4
  require_relative '../../lib/rgraph/node'
4
5
 
5
-
6
6
  class Graph
7
- attr_accessor :nodes, :links
7
+ attr_accessor :nodes, :links, :infinity
8
8
 
9
9
  def initialize(csv)
10
10
  @nodes = []
11
11
  @links = []
12
+ @distance = nil
13
+ @infinity = 100_000
12
14
  raise Exception.new("the file must be a .csv") unless File.extname(csv) == ".csv"
13
15
 
14
16
  CSV.foreach(csv, headers: true) do |row|
@@ -16,14 +18,10 @@ class Graph
16
18
  source_id = row.delete('source').last
17
19
  target_id = row.delete('target').last
18
20
 
19
- unless source = get_node_by_id(source_id)
20
- source = Node.new(id: source_id)
21
- @nodes << source
22
- end
23
- unless target = get_node_by_id(target_id)
24
- target = Node.new(id: target_id)
25
- @nodes << target
26
- end
21
+ source = get_node_by_id(source_id) || Node.new(id: source_id)
22
+ target = get_node_by_id(target_id) || Node.new(id: target_id)
23
+
24
+ maybe_add_to_nodes(source, target)
27
25
 
28
26
  @links << Link.new(source: source, target: target, weight: row['weight'], year: row['year'])
29
27
  end
@@ -47,12 +45,103 @@ class Graph
47
45
 
48
46
  def cumulative_degree
49
47
  cached_degrees = degrees
50
- cum = []
48
+ out = []
51
49
 
52
50
  0.upto(degrees.max - 1) do |i|
53
- cum[i] = cached_degrees.select{|degree| degree > i}.count
51
+ out[i] = cached_degrees.select{|degree| degree > i}.count
54
52
  end
55
- cum.map{|a| a / cum.max.to_f}
53
+ out.map{|a| a / out.max.to_f}
54
+ end
55
+
56
+ def idistance
57
+ dist = Array.new(@nodes.size) { Array.new(@nodes.size, 0) }
58
+
59
+ @nodes.sort{|a,b| a.id <=> b.id}.each_with_index do |n1, i|
60
+ @nodes.sort{|a,b| a.id <=> b.id}.each_with_index do |n2, j|
61
+ if i != j
62
+ dist[i][j] = n1.neighbours.include?(n2) ? @links.select{ |link| (link.source == n1 || link.source == n2) && (link.target == n1 || link.target == n2) }.first.weight.to_i : @infinity
63
+ end
64
+ end
65
+ end
66
+ dist
67
+ end
68
+
69
+ def distances
70
+ @path = Array.new(@nodes.size) { Array.new(@nodes.size, nil) }
71
+
72
+ @distance = idistance
73
+ @distance.each_index do |k|
74
+ @distance.each_index do |i|
75
+ @distance.each_index do |j|
76
+ new_dist = @distance[i][k] + @distance[k][j]
77
+ if @distance[i][j] > new_dist
78
+ @distance[i][j] = new_dist
79
+ @path[i][j] = k
80
+ end
81
+ end
82
+ end
83
+ end
84
+ @distance
85
+ end
86
+
87
+ def shortest_paths
88
+ tmp = []
89
+
90
+ distances unless @my_shortest_paths
91
+
92
+ 0.upto(@nodes.size - 1).each do |i|
93
+ i.upto(@nodes.size - 1).each do |j|
94
+ next if i == j
95
+ sp = shortest_path(i, j)
96
+ tmp << sp if sp
97
+ end
98
+ end
99
+
100
+ @shortest_paths = tmp
101
+ end
102
+
103
+ def shortest_path(i, j)
104
+
105
+ return [] if @distance[i][j] == @infinity
106
+
107
+ k = @path[i][j]
108
+
109
+ case k
110
+ when @infinity
111
+ []
112
+ when nil
113
+ [i,j]
114
+ else
115
+ #We need to do this or k will appear three times
116
+ shortest_path(i, k)[0..-2] + [k] + shortest_path(k, j)[1..-1]
117
+ end
118
+ end
119
+
120
+ def between(i)
121
+ shortest_paths unless @shortest_paths
122
+
123
+ n = @shortest_paths.select{|c| c[1..-2].include?(i)}.size.to_f
124
+ m = @shortest_paths.size.to_f
125
+ n / m
126
+ end
127
+
128
+ def betweenness(normalized = false)
129
+ bts = 0.upto(@nodes.size - 1).map { |i| between(i) }
130
+
131
+ if normalized
132
+ max = bts.max
133
+ min = bts.min
134
+
135
+ bts.map{|bt| (bt - min) / (max - min)}
136
+ else
137
+ bts
138
+ end
139
+ end
140
+
141
+ def diameter
142
+ distances unless @distance
143
+
144
+ (distances.flatten - [@infinity]).max
56
145
  end
57
146
 
58
147
  private
@@ -60,4 +149,10 @@ class Graph
60
149
  def get_node_by_id(node_id)
61
150
  @nodes.select{|n| n.id == node_id}.first
62
151
  end
152
+
153
+ def maybe_add_to_nodes(*nodes)
154
+ nodes.each do |node|
155
+ @nodes << node unless get_node_by_id(node.id)
156
+ end
157
+ end
63
158
  end
data/lib/rgraph/link.rb CHANGED
@@ -1,24 +1,24 @@
1
- class Link
2
- attr_accessor :source, :target
1
+ class Link
2
+ attr_accessor :source, :target
3
3
 
4
- def initialize(arg)
5
- @args = arg
6
- @source = @args.delete(:source)
7
- @target = @args.delete(:target)
4
+ def initialize(arg)
5
+ @args = arg
6
+ @source = @args.delete(:source)
7
+ @target = @args.delete(:target)
8
8
 
9
- raise Exception.new("source cant be nil") unless @source
10
- raise Exception.new("target cant be nil") unless @target
11
- raise Exception.new("source must be of type Node") unless @source.is_a? Node
12
- raise Exception.new("target must be of type Node") unless @target.is_a? Node
9
+ raise Exception.new("source cant be nil") unless @source
10
+ raise Exception.new("target cant be nil") unless @target
11
+ raise Exception.new("source must be of type Node") unless @source.is_a? Node
12
+ raise Exception.new("target must be of type Node") unless @target.is_a? Node
13
13
 
14
- @args[:weight] ||= 1
14
+ @args[:weight] ||= 1
15
15
 
16
- @source.neighbours << @target
17
- @target.neighbours << @source
18
- end
16
+ @source.neighbours << @target
17
+ @target.neighbours << @source
18
+ end
19
19
 
20
- def method_missing(name, *args)
21
- super unless args.empty?
22
- @args[name]
23
- end
20
+ def method_missing(name, *args)
21
+ super unless args.empty?
22
+ @args[name]
24
23
  end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module Rgraph
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.8"
3
3
  end
data/lib/rgraph.rb CHANGED
@@ -2,7 +2,3 @@ require "rgraph/version"
2
2
  require "rgraph/link"
3
3
  require "rgraph/node"
4
4
  require "rgraph/graph"
5
-
6
- module Rgraph
7
- # Your code goes here...
8
- end
data/rgraph.gemspec CHANGED
@@ -14,12 +14,11 @@ Gem::Specification.new do |spec|
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.test_files = spec.files.grep(%r{^spec/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency "rake"
22
22
  spec.add_development_dependency "rspec", "~> 2.14.1"
23
23
  spec.add_development_dependency "guard-rspec", "~> 3.0.2"
24
- spec.add_development_dependency "fastercsv", "~> 1.5.5"
25
24
  end
@@ -0,0 +1,8 @@
1
+ source,target
2
+ 1,2
3
+ 1,5
4
+ 2,3
5
+ 2,5
6
+ 3,4
7
+ 4,5
8
+ 4,6
@@ -1,4 +1,4 @@
1
1
  source,target,weight,year
2
2
  n1,n2,1,2005
3
- n2,n3,1,2005
4
- n3,n1,1,2005
3
+ n3,n2,1,2005
4
+ n1,n3,1,2005
@@ -0,0 +1,4 @@
1
+ source,target,weight,year
2
+ n1,n2,1,2005
3
+ n2,n3,1,2005
4
+ n4,n5,1,2005
@@ -1,8 +1,11 @@
1
+ #coding: utf-8
2
+
1
3
  require_relative '../../lib/rgraph/graph'
2
4
 
3
5
  describe Graph do
4
6
  describe "read .csv" do
5
7
  subject { Graph.new('spec/fixtures/three_links.csv') }
8
+
6
9
  it "creates three nodes" do
7
10
  expect(subject.nodes.size).to eq(3)
8
11
  end
@@ -43,6 +46,11 @@ describe Graph do
43
46
 
44
47
  describe "Metrics" do
45
48
  subject { Graph.new('spec/fixtures/three_links.csv') }
49
+
50
+ it "sets default infinity to 100_000" do
51
+ expect(subject.infinity).to eq(100_000)
52
+ end
53
+
46
54
  it "all nodes degree" do
47
55
  expect(subject.degrees).to eq([2,2,2])
48
56
  end
@@ -53,4 +61,90 @@ describe Graph do
53
61
  expect(subject.cumulative_degree).to eq([1.0, 1.0])
54
62
  end
55
63
  end
64
+
65
+ describe "Distance" do
66
+ subject { Graph.new('spec/fixtures/small_graph.csv') }
67
+
68
+ it "calculates the initial distance matrix" do
69
+ k = subject.infinity
70
+ expect(subject.idistance).to eq(
71
+ [[0,1,k,k,1,k],
72
+ [1,0,1,k,1,k],
73
+ [k,1,0,1,k,k],
74
+ [k,k,1,0,1,1],
75
+ [1,1,k,1,0,k],
76
+ [k,k,k,1,k,0]])
77
+ end
78
+
79
+ it "checks distances" do
80
+ expect(subject.distances).to eq(
81
+ [[0,1,2,2,1,3],
82
+ [1,0,1,2,1,3],
83
+ [2,1,0,1,2,2],
84
+ [2,2,1,0,1,1],
85
+ [1,1,2,1,0,2],
86
+ [3,3,2,1,2,0]])
87
+ end
88
+
89
+ it "understands holes on the graph" do
90
+ graph = Graph.new('spec/fixtures/two_links_with_hole.csv')
91
+ k = graph.infinity
92
+ expect(graph.idistance).to eq(
93
+ [[0,1,k,k,k],
94
+ [1,0,1,k,k],
95
+ [k,1,0,k,k],
96
+ [k,k,k,0,1],
97
+ [k,k,k,1,0]])
98
+ expect(graph.distances).to eq(
99
+ [[0,1,2,k,k],
100
+ [1,0,1,k,k],
101
+ [2,1,0,k,k],
102
+ [k,k,k,0,1],
103
+ [k,k,k,1,0]])
104
+ end
105
+ end
106
+ describe "Paths' metrics" do
107
+ subject { Graph.new('spec/fixtures/small_graph.csv') }
108
+ it "calculates the shortest paths" do
109
+ expect(subject.shortest_paths.sort).to eq(
110
+ [[0, 1],
111
+ [0, 1, 2],
112
+ [0, 4],
113
+ [0, 4, 3],
114
+ [0, 4, 3, 5],
115
+ [1, 2],
116
+ [1, 2, 3],
117
+ [1, 2, 3, 5],
118
+ [1, 4],
119
+ [2, 1, 4],
120
+ [2, 3],
121
+ [2, 3, 5],
122
+ [3, 4], [3, 5],
123
+ [4, 3, 5]].sort)
124
+ end
125
+ it "calculates the betweenness of a single node" do
126
+ expect(subject.between(0)).to eq(0 / 15.0)
127
+ expect(subject.between(1)).to eq(2 / 15.0)
128
+ expect(subject.between(2)).to eq(2 / 15.0)
129
+ expect(subject.between(3)).to eq(4 / 15.0)
130
+ expect(subject.between(4)).to eq(2 / 15.0)
131
+ expect(subject.between(5)).to eq(0 / 15.0)
132
+ end
133
+ it "calculates all betweenness" do
134
+ expect(subject.betweenness).to eq(
135
+ [0 / 15.0,
136
+ 2 / 15.0,
137
+ 2 / 15.0,
138
+ 4 / 15.0,
139
+ 2 / 15.0,
140
+ 0 / 15.0])
141
+ end
142
+ it "calculates all betweenness normalized" do
143
+ expect(subject.betweenness(true)).to eq(
144
+ [0.0, 0.5, 0.5, 1.0, 0.5, 0.0])
145
+ end
146
+ it "calculates diameter" do
147
+ expect(subject.diameter).to eq(3)
148
+ end
149
+ end
56
150
  end
@@ -9,6 +9,12 @@ describe Link do
9
9
  its(:target) { should be_kind_of Node }
10
10
  its(:weight) { should == 1 }
11
11
  its(:years) { should == [2011, 2012] }
12
+
13
+ it "checks the creation of neighbours" do
14
+ expect(subject.source.neighbours).to eq([subject.target])
15
+ expect(subject.target.neighbours).to eq([subject.source])
16
+ end
17
+
12
18
  end
13
19
 
14
20
  describe "creates a link passing a weight" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rgraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-08-22 00:00:00.000000000 Z
13
+ date: 2013-08-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -76,22 +76,6 @@ dependencies:
76
76
  - - ~>
77
77
  - !ruby/object:Gem::Version
78
78
  version: 3.0.2
79
- - !ruby/object:Gem::Dependency
80
- name: fastercsv
81
- requirement: !ruby/object:Gem::Requirement
82
- none: false
83
- requirements:
84
- - - ~>
85
- - !ruby/object:Gem::Version
86
- version: 1.5.5
87
- type: :development
88
- prerelease: false
89
- version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
- requirements:
92
- - - ~>
93
- - !ruby/object:Gem::Version
94
- version: 1.5.5
95
79
  description: Ruby's Graph Library
96
80
  email:
97
81
  - aride.moulin@gmail.com
@@ -116,8 +100,10 @@ files:
116
100
  - lib/rgraph/version.rb
117
101
  - rgraph.gemspec
118
102
  - spec/fixtures/2005.csv
103
+ - spec/fixtures/small_graph.csv
119
104
  - spec/fixtures/three_links.csv
120
105
  - spec/fixtures/two_links.csv
106
+ - spec/fixtures/two_links_with_hole.csv
121
107
  - spec/rgraph/graph_spec.rb
122
108
  - spec/rgraph/link_spec.rb
123
109
  - spec/rgraph/node_spec.rb
@@ -150,8 +136,10 @@ specification_version: 3
150
136
  summary: A Ruby's Graph Library
151
137
  test_files:
152
138
  - spec/fixtures/2005.csv
139
+ - spec/fixtures/small_graph.csv
153
140
  - spec/fixtures/three_links.csv
154
141
  - spec/fixtures/two_links.csv
142
+ - spec/fixtures/two_links_with_hole.csv
155
143
  - spec/rgraph/graph_spec.rb
156
144
  - spec/rgraph/link_spec.rb
157
145
  - spec/rgraph/node_spec.rb