rgraph 0.0.6 → 0.0.8

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.
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