rgraph 0.0.13 → 0.0.14

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