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 +4 -4
- data/lib/rgraph/graph.rb +50 -33
- data/lib/rgraph/node.rb +4 -0
- data/lib/rgraph/version.rb +1 -1
- data/spec/rgraph/graph_spec.rb +31 -20
- data/spec/rgraph/node_spec.rb +9 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c4d3fdd3c5cedd1f8ea3f6f12ac4cfc35ced188
|
4
|
+
data.tar.gz: b9ee2affd0263db5ec94a3bf395d5569ee8ece85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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 =
|
60
|
+
dist = matrix(@nodes.size, 0)
|
66
61
|
|
67
|
-
@nodes.sort{|a,b| a.id <=> b.id}
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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 =
|
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
|
-
|
101
|
+
def shortest_paths(args = {})
|
102
|
+
multiples = args[:multiples] || false
|
103
|
+
@shortest_paths = []
|
104
104
|
|
105
|
-
distances
|
105
|
+
distances
|
106
106
|
|
107
107
|
0.upto(@nodes.size - 1).each do |i|
|
108
|
-
i.upto(@nodes.size - 1).each do |j|
|
109
|
-
|
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
|
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
|
-
|
120
|
+
path = @path[i][j].count == 1 || multiples ? @path[i][j] : [@path[i][j].first]
|
121
|
+
out = []
|
123
122
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
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
data/lib/rgraph/version.rb
CHANGED
data/spec/rgraph/graph_spec.rb
CHANGED
@@ -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 "
|
146
|
-
g = Graph.new_from_string("source,target\n1,2\
|
147
|
-
expect(g.shortest_paths).to eq(
|
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 /
|
152
|
-
expect(subject.between(1)).to eq(2 /
|
153
|
-
expect(subject.between(2)).to eq(2 /
|
154
|
-
expect(subject.between(3)).to eq(
|
155
|
-
expect(subject.between(4)).to eq(
|
156
|
-
expect(subject.between(5)).to eq(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 /
|
161
|
-
2 /
|
162
|
-
2 /
|
163
|
-
|
164
|
-
|
165
|
-
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
|
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
|
|
data/spec/rgraph/node_spec.rb
CHANGED
@@ -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.
|
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-
|
12
|
+
date: 2013-09-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|