rgraph 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e957e5282af5a7c082bc428cb198d0c8eb135b6b
4
- data.tar.gz: 27bf0d9882da344b9d132d9fc983416862016d7d
3
+ metadata.gz: 4c4d3fdd3c5cedd1f8ea3f6f12ac4cfc35ced188
4
+ data.tar.gz: b9ee2affd0263db5ec94a3bf395d5569ee8ece85
5
5
  SHA512:
6
- metadata.gz: 300c04213aae09989dcd40ac0edd917ee40560a5ed72aa1965721c873c27b593e1d35afd752c71d988cddfdf5f1fef3ecb4122f422a6be6903716fdd1632d94f
7
- data.tar.gz: b1479e98a98990a428f61608af3c88d9ec41f8d383695623372c2e1098c811d7db04acf5bdcc7017a00e7492505cbcbd766ec509058c058527410066d329131d
6
+ metadata.gz: 7723113a6c7b319dd3dadae64511d8d0c623832f588ce40f418da526df01a81cb052d509fd89550638ad2554c89cc64e02632cda004f73892d88410601e7e158
7
+ data.tar.gz: 86883eca3a63e40d83d3cd1c2e2eb326347e8585a39b75bd555f31c56d44e2ca152da0aab91097e0dcc540b903efe656b745eb422c33e62a5410c6780924f92b
data/lib/rgraph/graph.rb CHANGED
@@ -52,30 +52,26 @@ class Graph
52
52
  end
53
53
 
54
54
  def cumulative_degree
55
- cached_degrees = degrees
56
- out = []
57
-
58
- 0.upto(degrees.max - 1) do |i|
59
- out[i] = cached_degrees.select{|degree| degree > i}.count
60
- end
55
+ out = (0..(degrees.max - 1)).map { |i| degrees.select{|degree| degree > i}.count }
61
56
  out.map{|a| a / out.max.to_f}
62
57
  end
63
58
 
64
59
  def idistance
65
- dist = Array.new(@nodes.size) { Array.new(@nodes.size, 0) }
60
+ dist = matrix(@nodes.size, 0)
66
61
 
67
- @nodes.sort{|a,b| a.id <=> b.id}.each_with_index do |n1, i|
68
- @nodes.sort{|a,b| a.id <=> b.id}.each_with_index do |n2, j|
69
- if i != j
70
- 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
71
- end
62
+ sorted = @nodes.sort { |a,b| a.id <=> b.id}
63
+
64
+ sorted.each_with_index do |n1, i|
65
+ sorted.each_with_index do |n2, j|
66
+ next if i == j
67
+ dist[i][j] = n1.has_neighbour?(n2) ? link_between(n1, n2).weight.to_i : @infinity
72
68
  end
73
69
  end
74
70
  dist
75
71
  end
76
72
 
77
73
  def distances
78
- @path = Array.new(@nodes.size) { Array.new(@nodes.size, nil) }
74
+ @path = matrix(@nodes.size, [nil])
79
75
 
80
76
  @distance = idistance
81
77
  @distance.each_index do |k|
@@ -84,7 +80,10 @@ class Graph
84
80
  new_dist = @distance[i][k] + @distance[k][j]
85
81
  if @distance[i][j] > new_dist
86
82
  @distance[i][j] = new_dist
87
- @path[i][j] = k
83
+ @path[i][j] = [k]
84
+ end
85
+ if @distance[i][j] == new_dist && !(@path[i][j].include? k) && ! [i,j].include?(k)
86
+ @path[i][j] << k
88
87
  end
89
88
  end
90
89
  end
@@ -99,41 +98,51 @@ class Graph
99
98
  fdistance.inject(:+) / fdistance.count.to_f
100
99
  end
101
100
 
102
- def shortest_paths
103
- tmp = []
101
+ def shortest_paths(args = {})
102
+ multiples = args[:multiples] || false
103
+ @shortest_paths = []
104
104
 
105
- distances unless @my_shortest_paths
105
+ distances
106
106
 
107
107
  0.upto(@nodes.size - 1).each do |i|
108
- i.upto(@nodes.size - 1).each do |j|
109
- next if i == j
110
- sp = shortest_path(i, j)
111
- tmp << sp unless sp.empty?
108
+ (i + 1).upto(@nodes.size - 1).each do |j|
109
+ @shortest_paths += shortest_path(i, j, multiples)
112
110
  end
113
111
  end
114
112
 
115
- @shortest_paths = tmp
113
+ @shortest_paths
116
114
  end
117
115
 
118
- def shortest_path(i, j)
116
+ def shortest_path(i, j, multiples = false)
119
117
 
120
118
  return [] if @distance[i][j] == @infinity
121
119
 
122
- k = @path[i][j]
120
+ path = @path[i][j].count == 1 || multiples ? @path[i][j] : [@path[i][j].first]
121
+ out = []
123
122
 
124
- case k
125
- when @infinity
126
- []
127
- when nil
128
- [i,j]
129
- else
130
- #We need to do this or k will appear three times
131
- shortest_path(i, k)[0..-2] + [k] + shortest_path(k, j)[1..-1]
123
+ path.each do |k|
124
+
125
+ case k
126
+ when @infinity
127
+ out << []
128
+ when nil
129
+ out << [i,j]
130
+ else
131
+ i_k = shortest_path(i, k)
132
+ k_j = shortest_path(k, j)
133
+
134
+ i_k.each do |ik|
135
+ k_j.each do |kj|
136
+ out << ik[0..-2] + [k] + kj[1..-1]
137
+ end
138
+ end
139
+ end
132
140
  end
141
+ out
133
142
  end
134
143
 
135
144
  def between(i)
136
- shortest_paths unless @shortest_paths
145
+ shortest_paths(multiples: true)
137
146
 
138
147
  n = @shortest_paths.select{|c| c[1..-2].include?(i)}.size.to_f
139
148
  m = @shortest_paths.size.to_f
@@ -249,8 +258,16 @@ class Graph
249
258
  end
250
259
  end
251
260
 
261
+ def link_between(n1, n2)
262
+ @links.select{ |l| (l.source == n1 && l.target == n2) || (l.source == n2 && l.target == n1) }.first
263
+ end
264
+
252
265
  def possible_connections(node)
253
266
  total = node.neighbours.count
254
267
  total * (total - 1) / 2
255
268
  end
269
+
270
+ def matrix(size, value)
271
+ Array.new(size) { Array.new(size, value) }
272
+ end
256
273
  end
data/lib/rgraph/node.rb CHANGED
@@ -10,6 +10,10 @@ class Node
10
10
  @neighbours.size
11
11
  end
12
12
 
13
+ def has_neighbour?(node)
14
+ @neighbours.include?(node)
15
+ end
16
+
13
17
  def method_missing(name, *args)
14
18
  super unless args.empty?
15
19
  @args[name]
@@ -1,3 +1,3 @@
1
1
  module Rgraph
2
- VERSION = '0.0.13'
2
+ VERSION = '0.0.14'
3
3
  end
@@ -125,6 +125,17 @@ describe Graph do
125
125
  end
126
126
  describe "Paths' metrics" do
127
127
  subject { Graph.new('spec/fixtures/small_graph.csv') }
128
+
129
+ it "calculates a simple shortest path" do
130
+ g = Graph.new_from_string("source,target\n1,2")
131
+ expect(g.shortest_paths).to eq([[0, 1]])
132
+ end
133
+
134
+ it "doesn't return empty paths" do
135
+ g = Graph.new_from_string("source,target\n1,2\n3,4")
136
+ expect(g.shortest_paths).to eq([[0,1],[2,3]])
137
+ end
138
+
128
139
  it "calculates the shortest paths" do
129
140
  expect(subject.shortest_paths.sort).to eq(
130
141
  [[0, 1],
@@ -142,36 +153,39 @@ describe Graph do
142
153
  [3, 4], [3, 5],
143
154
  [4, 3, 5]].sort)
144
155
  end
145
- it "doesn't return empty paths" do
146
- g = Graph.new_from_string("source,target\n1,2\n3,4")
147
- expect(g.shortest_paths).to eq([[0,1],[2,3]])
156
+ it "returns multiples short paths when asked" do
157
+ g = Graph.new_from_string("source,target\n1,2\n2,3\n1,4\n4,3")
158
+ expect(g.shortest_paths.count).to eq(6)
159
+ expect(g.shortest_paths(multiples: true).count).to eq(8)
148
160
  end
149
-
150
161
  it "calculates the betweenness of a single node" do
151
- expect(subject.between(0)).to eq(0 / 15.0)
152
- expect(subject.between(1)).to eq(2 / 15.0)
153
- expect(subject.between(2)).to eq(2 / 15.0)
154
- expect(subject.between(3)).to eq(4 / 15.0)
155
- expect(subject.between(4)).to eq(2 / 15.0)
156
- expect(subject.between(5)).to eq(0 / 15.0)
162
+ expect(subject.between(0)).to eq(0 / 18.0)
163
+ expect(subject.between(1)).to eq(2 / 18.0)
164
+ expect(subject.between(2)).to eq(2 / 18.0)
165
+ expect(subject.between(3)).to eq(6 / 18.0)
166
+ expect(subject.between(4)).to eq(4 / 18.0)
167
+ expect(subject.between(5)).to eq(0 / 18.0)
157
168
  end
158
169
  it "calculates all betweenness" do
159
170
  expect(subject.betweenness).to eq(
160
- [0 / 15.0,
161
- 2 / 15.0,
162
- 2 / 15.0,
163
- 4 / 15.0,
164
- 2 / 15.0,
165
- 0 / 15.0])
171
+ [0 / 18.0,
172
+ 2 / 18.0,
173
+ 2 / 18.0,
174
+ 6 / 18.0,
175
+ 4 / 18.0,
176
+ 0 / 18.0])
166
177
  end
167
178
  it "calculates all betweenness normalized" do
168
179
  expect(subject.betweenness(normalized: true)).to eq(
169
- [0.0, 0.5, 0.5, 1.0, 0.5, 0.0])
180
+ [0.0, 1 / 3.0, 1 / 3.0, 1.0, 2 / 3.0, 0.0])
170
181
  end
171
182
  it "works with holes" do
172
183
  g = Graph.new_from_string("source,target\n1,2\n3,4")
173
184
  expect(g.betweenness.inject(:+)).to eq(0)
174
185
  end
186
+ it "calculates cumulative betweenness" do
187
+ expect(subject.betweenness(cumulative: true)).to eq([[6, 0.0], [4, 1 / 3.0], [2, 2 / 3.0], [1, 1.0]])
188
+ end
175
189
  it "calculates diameter" do
176
190
  expect(subject.diameter).to eq(3)
177
191
  end
@@ -185,9 +199,6 @@ describe Graph do
185
199
  g = Graph.new_from_string("source,target\n1,2\n3,4")
186
200
  expect(g.components).to eq([g.nodes[0..1], g.nodes[2..3]])
187
201
  end
188
- it "calculates cumulative betweenness" do
189
- expect(subject.betweenness(cumulative: true)).to eq([[6, 0.0], [4, 0.5], [1, 1.0]])
190
- end
191
202
  it "calculates clustering of a single node" do
192
203
  nodes = subject.nodes.sort{|a,b| a.id <=> b.id}
193
204
 
@@ -11,4 +11,13 @@ describe Node do
11
11
  describe "#degree" do
12
12
  its(:degree) { should eq 0}
13
13
  end
14
+
15
+ describe "#has_neighbour" do
16
+ it "recognizes its neighbour" do
17
+ n1 = Node.new(id: 1)
18
+ n2 = Node.new(id: 1)
19
+ n1.neighbours << n2
20
+ expect(n1.has_neighbour?(n2)).to be_true
21
+ end
22
+ end
14
23
  end
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.13
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Aride Moulin
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-13 00:00:00.000000000 Z
12
+ date: 2013-09-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler