rgraphum 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +26 -0
- data/GLOSSARIES.md +108 -0
- data/GREMLIN.md +1398 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +136 -0
- data/Rakefile +16 -0
- data/bin/.irbrc +41 -0
- data/bin/rgraphum_console +61 -0
- data/bin/rgraphum_runner +57 -0
- data/examples/ba_model/make.rb +19 -0
- data/examples/ba_model/make_dummy_twitter_rt_data.rb +0 -0
- data/examples/basic/check_modularity.rb +27 -0
- data/examples/basic/make_graph.rb +12 -0
- data/examples/parser/dot.rb +28 -0
- data/examples/sis_model/lifegame.rb +161 -0
- data/graph_struct.jpg +0 -0
- data/lib/rgraphum/analyzer/linear_regression.rb +31 -0
- data/lib/rgraphum/analyzer/meme_tracker.rb +296 -0
- data/lib/rgraphum/analyzer/twitter/rt_at_mark.rb +45 -0
- data/lib/rgraphum/analyzer.rb +8 -0
- data/lib/rgraphum/cluster.rb +67 -0
- data/lib/rgraphum/communities.rb +65 -0
- data/lib/rgraphum/community.rb +86 -0
- data/lib/rgraphum/cosine_similarity_matrix.rb +40 -0
- data/lib/rgraphum/edge.rb +194 -0
- data/lib/rgraphum/edges.rb +161 -0
- data/lib/rgraphum/ext/cosine_similarity_matrix.rb +79 -0
- data/lib/rgraphum/ext/linear_regression.rb +22 -0
- data/lib/rgraphum/ext/tf_idf.rb +52 -0
- data/lib/rgraphum/graph/gremlin.rb +193 -0
- data/lib/rgraphum/graph/math/clustering_coefficient.rb +53 -0
- data/lib/rgraphum/graph/math/community_detection.rb +141 -0
- data/lib/rgraphum/graph/math/degree_distribution.rb +50 -0
- data/lib/rgraphum/graph/math/dijkstra.rb +331 -0
- data/lib/rgraphum/graph/math.rb +45 -0
- data/lib/rgraphum/graph.rb +267 -0
- data/lib/rgraphum/importer.rb +97 -0
- data/lib/rgraphum/marshal.rb +26 -0
- data/lib/rgraphum/motifs.rb +8 -0
- data/lib/rgraphum/parsers/flare.rb +42 -0
- data/lib/rgraphum/parsers/gephi.rb +193 -0
- data/lib/rgraphum/parsers/graphviz.rb +78 -0
- data/lib/rgraphum/parsers/miserables.rb +54 -0
- data/lib/rgraphum/parsers.rb +32 -0
- data/lib/rgraphum/path.rb +37 -0
- data/lib/rgraphum/query.rb +130 -0
- data/lib/rgraphum/rgraphum_array.rb +159 -0
- data/lib/rgraphum/rgraphum_array_dividers.rb +43 -0
- data/lib/rgraphum/rgraphum_random.rb +5 -0
- data/lib/rgraphum/simulator/ba_model.rb +140 -0
- data/lib/rgraphum/simulator/sir_model.rb +178 -0
- data/lib/rgraphum/simulator/sis_model.rb +158 -0
- data/lib/rgraphum/simulator.rb +29 -0
- data/lib/rgraphum/statistic/power_law.rb +9 -0
- data/lib/rgraphum/t.rb +12 -0
- data/lib/rgraphum/tf_idf.rb +27 -0
- data/lib/rgraphum/version.rb +3 -0
- data/lib/rgraphum/vertex.rb +354 -0
- data/lib/rgraphum/vertices.rb +97 -0
- data/lib/rgraphum.rb +38 -0
- data/performance/add-vertices-edges.rb +20 -0
- data/performance/add-vertices.rb +12 -0
- data/performance/build-graph.rb +19 -0
- data/performance/delete-graph.rb +24 -0
- data/performance/delete-vertices.rb +25 -0
- data/performance/refer-graph.rb +23 -0
- data/rgraphum.gemspec +30 -0
- data/test/lib/rgraphum/analyzer/linear_regression_test.rb +20 -0
- data/test/lib/rgraphum/analyzer/meme_tracker_test.rb +383 -0
- data/test/lib/rgraphum/analyzer/twitter/rt_at_mark_test.rb +120 -0
- data/test/lib/rgraphum/array_test.rb +95 -0
- data/test/lib/rgraphum/bubble_test.rb +7 -0
- data/test/lib/rgraphum/communities_test.rb +53 -0
- data/test/lib/rgraphum/cosine_similarity_test.rb +18 -0
- data/test/lib/rgraphum/edge_test.rb +89 -0
- data/test/lib/rgraphum/edges_test.rb +178 -0
- data/test/lib/rgraphum/graph_builder_test.rb +64 -0
- data/test/lib/rgraphum/graph_dup_test.rb +199 -0
- data/test/lib/rgraphum/graph_plus_test.rb +80 -0
- data/test/lib/rgraphum/graph_test.rb +512 -0
- data/test/lib/rgraphum/gremlin_test.rb +145 -0
- data/test/lib/rgraphum/importers/idg_json_edges.json +20 -0
- data/test/lib/rgraphum/importers/idg_json_test.rb +207 -0
- data/test/lib/rgraphum/importers/idg_json_vertices.json +46 -0
- data/test/lib/rgraphum/math/average_distance_matrix_test.rb +142 -0
- data/test/lib/rgraphum/math/clustering_coefficient_test.rb +219 -0
- data/test/lib/rgraphum/math/community_test.rb +78 -0
- data/test/lib/rgraphum/math/degree_distribution_test.rb +40 -0
- data/test/lib/rgraphum/math/dijkstra_test.rb +146 -0
- data/test/lib/rgraphum/math/modularity_test.rb +154 -0
- data/test/lib/rgraphum/math/quick_average_distance_matrix_test.rb +84 -0
- data/test/lib/rgraphum/path_test.rb +44 -0
- data/test/lib/rgraphum/query/enumerable_test.rb +42 -0
- data/test/lib/rgraphum/query/where_operators_test.rb +75 -0
- data/test/lib/rgraphum/query/where_test.rb +59 -0
- data/test/lib/rgraphum/simulator/ba_model_test.rb +75 -0
- data/test/lib/rgraphum/simulator/sir_model_test.rb +513 -0
- data/test/lib/rgraphum/simulator/sis_model_test.rb +478 -0
- data/test/lib/rgraphum/simulator_test.rb +22 -0
- data/test/lib/rgraphum/tf_idf_test.rb +30 -0
- data/test/lib/rgraphum/vertex_test.rb +50 -0
- data/test/lib/rgraphum/vertices_test.rb +180 -0
- data/test/test_helper.rb +98 -0
- data/tmp/.gitkeep +0 -0
- metadata +254 -0
@@ -0,0 +1,267 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# FIXME some 'edge.source's are edge.source.id
|
4
|
+
# FIXME some 'edge.target's are edge.target.id
|
5
|
+
|
6
|
+
class Rgraphum::Graph
|
7
|
+
RGRAPHUM = Rgraphum
|
8
|
+
|
9
|
+
include Rgraphum::Graph::Math
|
10
|
+
include Rgraphum::Graph::Gremlin
|
11
|
+
include Rgraphum::Marshal
|
12
|
+
include Rgraphum::Simulator
|
13
|
+
include Rgraphum::Importer
|
14
|
+
include Rgraphum::Parsers
|
15
|
+
|
16
|
+
attr_accessor :aspect
|
17
|
+
attr_accessor :label
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
def build_from_adjacency_matrix( matrix, vertex_labels = [], options={} )
|
22
|
+
options = { loop: false, limit: 0.65 }.merge(options)
|
23
|
+
|
24
|
+
graph = new
|
25
|
+
if vertex_labels.size == matrix.size
|
26
|
+
vertex_labels.each do |label|
|
27
|
+
v = graph.vertices.build(label:label)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
matrix.size.times do
|
31
|
+
graph.vertices.build
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
matrix.each_with_index do | row,row_index|
|
36
|
+
row.each_with_index do |weight,col_index|
|
37
|
+
next if col_index == row_index and !options[:loop]
|
38
|
+
|
39
|
+
if weight and weight >= options[:limit]
|
40
|
+
graph.edges.build( {source:graph.vertices[row_index], target:graph.vertices[col_index],weight:weight} )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
graph
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param [Hash] options
|
50
|
+
# @option options [Rgraphum::Vertices] :vertices
|
51
|
+
# @option options [Rgraphum::Edges] :edges
|
52
|
+
#
|
53
|
+
def initialize(options={})
|
54
|
+
@vertices = Rgraphum::Vertices.new
|
55
|
+
if options[:vertices]
|
56
|
+
self.vertices = options[:vertices]
|
57
|
+
else
|
58
|
+
@vertices.graph = self
|
59
|
+
end
|
60
|
+
|
61
|
+
@edges = Rgraphum::Edges.new
|
62
|
+
if options[:edges]
|
63
|
+
@edges.graph = self
|
64
|
+
self.edges = options[:edges]
|
65
|
+
else
|
66
|
+
@edges.graph = self
|
67
|
+
end
|
68
|
+
|
69
|
+
@aspect = "real"
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# 再計算が必要な物にnilをセットする。
|
74
|
+
def clear_cache
|
75
|
+
@m = nil
|
76
|
+
@m_with_weight = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
# m is size of edges
|
80
|
+
def m
|
81
|
+
@m ||= @edges.size
|
82
|
+
end
|
83
|
+
|
84
|
+
# degree
|
85
|
+
def degree
|
86
|
+
self.m * 2
|
87
|
+
end
|
88
|
+
|
89
|
+
# m with weight
|
90
|
+
def m_with_weight
|
91
|
+
@m_with_weight ||= @edges.inject(0.0) { |sum, edge| sum + edge.weight }
|
92
|
+
end
|
93
|
+
|
94
|
+
# average degree
|
95
|
+
def average_degree
|
96
|
+
@average_degree ||= (2 * @m / @vertices.size)
|
97
|
+
end
|
98
|
+
|
99
|
+
def average_path_length
|
100
|
+
raise NotImplementedError
|
101
|
+
end
|
102
|
+
|
103
|
+
# basic method
|
104
|
+
def vertices
|
105
|
+
@vertices
|
106
|
+
end
|
107
|
+
|
108
|
+
def vertices=(vertex_array)
|
109
|
+
@vertices = @vertices.substitute(vertex_array) do |vertex|
|
110
|
+
Rgraphum::Vertex(vertex)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def edges
|
115
|
+
@edges
|
116
|
+
end
|
117
|
+
|
118
|
+
def edges=(edge_array)
|
119
|
+
@edges = @edges.substitute(edge_array) do |edge|
|
120
|
+
Rgraphum::Edge(edge)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# to_real mean edge has vertex pointer
|
125
|
+
def real_aspect!
|
126
|
+
return self if @aspect == "real"
|
127
|
+
|
128
|
+
edges.each do |edge|
|
129
|
+
edge.source = @vertices.find_by_id(edge.source) # FIXME
|
130
|
+
edge.target = @vertices.find_by_id(edge.target) # FIXME
|
131
|
+
end
|
132
|
+
|
133
|
+
@aspect = "real"
|
134
|
+
self
|
135
|
+
end
|
136
|
+
|
137
|
+
# to_id mean edge has vertex id
|
138
|
+
def id_aspect!
|
139
|
+
return self if @aspect == "id"
|
140
|
+
|
141
|
+
edges.each do |edge|
|
142
|
+
edge.source = edge.source.id
|
143
|
+
edge.target = edge.target.id
|
144
|
+
end
|
145
|
+
|
146
|
+
@aspect = "id"
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
# pickup start vertices
|
151
|
+
# it mean pick vertices having no in degree
|
152
|
+
def start_root_vertices
|
153
|
+
vertices.select do |vertex|
|
154
|
+
vertex.inE.empty?
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# pickup end vertices
|
159
|
+
# it mean pick vertices having no out degree
|
160
|
+
def end_root_vertices
|
161
|
+
vertices.select do |vertex|
|
162
|
+
vertex.outE.empty?
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def dup
|
167
|
+
new_graph = Rgraphum::Graph.new
|
168
|
+
|
169
|
+
@vertices.each do |vertex|
|
170
|
+
new_vertex = vertex.dup
|
171
|
+
new_vertex.edges = Rgraphum::Edges.new
|
172
|
+
new_graph.vertices << new_vertex
|
173
|
+
end
|
174
|
+
|
175
|
+
@edges.each do |edge|
|
176
|
+
new_graph.edges << edge.dup
|
177
|
+
end
|
178
|
+
|
179
|
+
new_graph
|
180
|
+
end
|
181
|
+
|
182
|
+
def +(other)
|
183
|
+
new_graph = Rgraphum::Graph.new
|
184
|
+
start_vertex_id = @vertices.id.max
|
185
|
+
start_edge_id = @edges.id.max
|
186
|
+
|
187
|
+
other_dup = other.dup
|
188
|
+
other_dup.vertices.each do | vertex |
|
189
|
+
vertex.id = vertex.id + start_vertex_id
|
190
|
+
end
|
191
|
+
other_dup.edges.each do |edge|
|
192
|
+
edge.id = edge.id + start_edge_id
|
193
|
+
end
|
194
|
+
|
195
|
+
new_graph.vertices = self.dup.vertices + other_dup.vertices
|
196
|
+
new_graph.edges = self.dup.edges + other_dup.edges
|
197
|
+
|
198
|
+
new_graph
|
199
|
+
end
|
200
|
+
|
201
|
+
def compact_with_label(options={})
|
202
|
+
compact_with(:label, self, options)
|
203
|
+
end
|
204
|
+
|
205
|
+
def marge_with_label(target)
|
206
|
+
new_graph = self + target
|
207
|
+
new_graph.compact_with_label
|
208
|
+
end
|
209
|
+
|
210
|
+
def divide_by_time(interval=20)
|
211
|
+
@vertices.divide_by_time(interval)
|
212
|
+
@edges.divide_by_time(interval)
|
213
|
+
|
214
|
+
new_edges = Rgraphum::Edges.new
|
215
|
+
new_edges.graph = self
|
216
|
+
@edges.each do |edge|
|
217
|
+
conditions = { source: edge.source, target: edge.target, start: edge.start }
|
218
|
+
same_edge = new_edges.where(conditions).first
|
219
|
+
if same_edge
|
220
|
+
same_edge.weight += edge.weight
|
221
|
+
else
|
222
|
+
new_edges << edge
|
223
|
+
end
|
224
|
+
end
|
225
|
+
self.edges = new_edges
|
226
|
+
end
|
227
|
+
|
228
|
+
def compact_with(method_name, graph=self, options ={})
|
229
|
+
new_vertices = Rgraphum::Vertices.new
|
230
|
+
new_vertices.graph = graph
|
231
|
+
graph.vertices.each do |vertex|
|
232
|
+
same_vertex = new_vertices.find{ |v| v.send(method_name) == vertex.send(method_name) }
|
233
|
+
unless same_vertex
|
234
|
+
new_vertex = vertex.dup
|
235
|
+
new_vertex.edges = Rgraphum::Edges.new
|
236
|
+
new_vertices << new_vertex
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
new_edges = Rgraphum::Edges.new
|
241
|
+
graph.edges.each do |edge|
|
242
|
+
source_label = edge.source.send(method_name)
|
243
|
+
target_label = edge.target.send(method_name)
|
244
|
+
edge.source = new_vertices.find{ |vertex| vertex.send(method_name) == source_label }
|
245
|
+
edge.target = new_vertices.find{ |vertex| vertex.send(method_name) == target_label }
|
246
|
+
|
247
|
+
same_edge = new_edges.find{ |e| e.source.equal?(edge.source) and e.source.equal?(edge.source) }
|
248
|
+
if same_edge
|
249
|
+
same_edge.weight += edge.weight
|
250
|
+
else
|
251
|
+
new_edges << edge
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
graph.vertices = new_vertices
|
256
|
+
graph.edges = new_edges
|
257
|
+
graph
|
258
|
+
end
|
259
|
+
|
260
|
+
def ==(other)
|
261
|
+
return false unless aspect == other.aspect
|
262
|
+
return false unless label == other.label
|
263
|
+
return false unless vertices == other.vertices
|
264
|
+
return false unless edges == other.edges
|
265
|
+
true
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Rgraphum
|
4
|
+
module Importer
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
# Load graph from files
|
11
|
+
#
|
12
|
+
#
|
13
|
+
def load(options={})
|
14
|
+
parse_options = options.dup
|
15
|
+
parse_options[:vertices] &&= open(parse_options[:vertices])
|
16
|
+
parse_options[:edges] &&= open(parse_options[:edges])
|
17
|
+
parse_options[:path] &&= open(parse_options[:path])
|
18
|
+
parse_options[:options] = options[:options]
|
19
|
+
parse parse_options
|
20
|
+
end
|
21
|
+
|
22
|
+
# Parse str and load graph
|
23
|
+
#
|
24
|
+
#
|
25
|
+
def parse(options={})
|
26
|
+
graph = Rgraphum::Graph.new
|
27
|
+
|
28
|
+
case options[:format]
|
29
|
+
when :idg_json
|
30
|
+
build_graph_from_idg_json graph, options[:vertices], options[:edges], (options[:options] || {})
|
31
|
+
when :dump
|
32
|
+
graph = load_from(options[:path])
|
33
|
+
else
|
34
|
+
raise ArgumentError, "Rgraphum::Importer::ClassMethods.parse: Unknown format: '#{options[:format]}'"
|
35
|
+
end
|
36
|
+
|
37
|
+
graph
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def build_graph_from_idg_json(graph, vertices_json_str_or_stream, edges_json_str_or_stream, options={})
|
43
|
+
vertex_id_hash = {}
|
44
|
+
community_hash = {}
|
45
|
+
verbose = options[:verbose]
|
46
|
+
|
47
|
+
puts "Loading vertices ... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
|
48
|
+
$stdout.flush if verbose
|
49
|
+
if vertices_json_str_or_stream
|
50
|
+
puts "JSON.load start ... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
|
51
|
+
json = JSON.load(vertices_json_str_or_stream)
|
52
|
+
puts "JSON.load end ... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
|
53
|
+
json["result"].each_with_index do |vertex_hash, i|
|
54
|
+
vertex_id_hash[vertex_hash["rid"]] ||= vertex_id_hash.size
|
55
|
+
community_hash[vertex_hash["c_id"]] ||= graph.communities.build
|
56
|
+
params = {
|
57
|
+
id: vertex_id_hash[vertex_hash["rid"]],
|
58
|
+
label: vertex_hash["screen_name"],
|
59
|
+
community_id: community_hash[vertex_hash["c_id"]].id,
|
60
|
+
}
|
61
|
+
graph.vertices.build(params)
|
62
|
+
if verbose
|
63
|
+
puts ".................... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')} #{i}" if (i % 10000) == 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
puts "Loading edges ...... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
|
69
|
+
$stdout.flush if verbose
|
70
|
+
start_edge_number = (options[:start_edge_number].to_i rescue 1) || 1
|
71
|
+
end_edge_number = (options[:end_edge_number].to_i rescue nil)
|
72
|
+
end_edge_number = nil if end_edge_number == 0
|
73
|
+
if edges_json_str_or_stream
|
74
|
+
json = JSON.load(edges_json_str_or_stream)
|
75
|
+
json["result"].each_with_index do |edge_hash, i|
|
76
|
+
next if i < start_edge_number
|
77
|
+
break if end_edge_number && end_edge_number < i
|
78
|
+
params = {
|
79
|
+
weight: edge_hash["weight"].to_f,
|
80
|
+
source: vertex_id_hash[edge_hash["in"]],
|
81
|
+
target: vertex_id_hash[edge_hash["out"]],
|
82
|
+
}
|
83
|
+
if graph.class::RGRAPHUM::Edge.has_field?(:created_at)
|
84
|
+
params[:created_at] = Time.at(edge_hash["created_at"].to_i)
|
85
|
+
end
|
86
|
+
graph.edges.build(params)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
puts "Loaded! #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
|
91
|
+
$stdout.flush if verbose
|
92
|
+
|
93
|
+
graph
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Rgraphum::Marshal
|
4
|
+
def self.included(base)
|
5
|
+
base.__send__ :include, InstanceMethods
|
6
|
+
base.__send__ :extend, ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
def dump_to(path)
|
11
|
+
data = Marshal.dump(self)
|
12
|
+
File.write(path, data)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def load_from(path)
|
18
|
+
data = File.read(path)
|
19
|
+
graph = Marshal.load(data)
|
20
|
+
unless graph.is_a?(self)
|
21
|
+
raise TypeError, "No #{self} instance in: #{path} (#{graph.class})"
|
22
|
+
end
|
23
|
+
graph
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Rgraphum::Parsers
|
6
|
+
class FlareParser
|
7
|
+
class << self
|
8
|
+
#
|
9
|
+
def builder(graph)
|
10
|
+
stream = "var flare = { \"#{graph.label}\" : "
|
11
|
+
|
12
|
+
vertices_hash = {}
|
13
|
+
graph.vertices.each do |vertex|
|
14
|
+
vertices_hash[vertex.label] = vertex.amount
|
15
|
+
end
|
16
|
+
stream += vertices_hash.to_json
|
17
|
+
stream += "};"
|
18
|
+
stream
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Options:
|
23
|
+
#
|
24
|
+
def initialize(options={})
|
25
|
+
default_options = {
|
26
|
+
}
|
27
|
+
@options = default_options.merge(options)
|
28
|
+
builder(@options[:graph]) if @options.key?(:graph)
|
29
|
+
end
|
30
|
+
|
31
|
+
def builder(graph)
|
32
|
+
@stream = self.class.builder(graph)
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
unless @stream
|
37
|
+
raise ArgumentError, "Didn't build stream with builder(graph)"
|
38
|
+
end
|
39
|
+
@stream
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'builder/xmlmarkup'
|
4
|
+
|
5
|
+
module Rgraphum::Parsers
|
6
|
+
class GephiParser
|
7
|
+
class << self
|
8
|
+
def builder(graph)
|
9
|
+
new.builder(graph)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Options:
|
14
|
+
#
|
15
|
+
def initialize(options={})
|
16
|
+
default_options = {
|
17
|
+
}
|
18
|
+
@options = default_options.merge(options)
|
19
|
+
builder(@options[:graph]) if @options.key?(:graph)
|
20
|
+
end
|
21
|
+
|
22
|
+
def builder(graph)
|
23
|
+
xml = ""
|
24
|
+
@xmlobj = Builder::XmlMarkup.new(target: xml , indent: 2)
|
25
|
+
set_header
|
26
|
+
|
27
|
+
options = {
|
28
|
+
"xmlns" => "http://www.gexf.net/1.2draft",
|
29
|
+
"xmlns:viz" => "http://www.gexf.net/1.1draft/viz",
|
30
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
31
|
+
"xsi:schemaLocation" => "http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd",
|
32
|
+
"version" => "1.2",
|
33
|
+
}
|
34
|
+
@xmlobj.gexf(options) do
|
35
|
+
@xmlobj.meta(lastmodifieddate: Time.now.strftime("%Y-%m-%d")) do
|
36
|
+
@xmlobj.creator("Gephi 0.8")
|
37
|
+
end
|
38
|
+
opts = {
|
39
|
+
defaultedgetype: "directed",
|
40
|
+
mode: "dynamic",
|
41
|
+
idtype: "string",
|
42
|
+
timeformat: "dateTime",
|
43
|
+
}
|
44
|
+
@xmlobj.graph(opts) do
|
45
|
+
vertices_to_xml(graph.vertices)
|
46
|
+
edges_to_xml(graph.edges)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@xml = xml
|
51
|
+
@xml
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
unless @xml
|
56
|
+
raise ArgumentError, "Didn't build xml with builder(graph)"
|
57
|
+
end
|
58
|
+
@xml
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def vertices_to_xml(vertices)
|
64
|
+
# Vertexの登録
|
65
|
+
@xmlobj.nodes() do
|
66
|
+
vertices.each do |vertex|
|
67
|
+
node_to_xml(vertex)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
alias :nodes_to_xml :vertices_to_xml
|
72
|
+
|
73
|
+
def node_to_xml(vertex)
|
74
|
+
new_vertex = vertex.dup
|
75
|
+
new_vertex.start = time_format(vertex.start) if vertex.start
|
76
|
+
new_vertex.end = time_format(vertex.end) if vertex.end
|
77
|
+
|
78
|
+
@xmlobj.node(id: vertex.id, label: vertex.label) do
|
79
|
+
# if vertex.attvalues and vertex.attvalues !=[]
|
80
|
+
# xmlobj.spells(){
|
81
|
+
# vertex.attvalues.each do |attvalue|
|
82
|
+
# opts = {
|
83
|
+
# start: time_format(attvalue.start),
|
84
|
+
# endopen: time_format(attvalue.end),
|
85
|
+
# }
|
86
|
+
# @xmlobj.spell(opts)
|
87
|
+
# end
|
88
|
+
# }
|
89
|
+
# end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
alias :vertex_to_xml :node_to_xml
|
93
|
+
|
94
|
+
def edges_to_xml(edges)
|
95
|
+
# @xmlobj.attributes(class: "edge", mode: "dynamic") do
|
96
|
+
# @xmlobj.attribute(id: "weight", title: "Weight", type: "float")
|
97
|
+
# end
|
98
|
+
@xmlobj.edges() do
|
99
|
+
edges.each do |edge|
|
100
|
+
edge_to_xml(edge.dup)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def edge_to_xml(edge)
|
106
|
+
edge_opts = {
|
107
|
+
id: edge.id,
|
108
|
+
source: edge.source.id,
|
109
|
+
target: edge.target.id,
|
110
|
+
label: edge.label,
|
111
|
+
weight: (edge.weight || 0.0),
|
112
|
+
}
|
113
|
+
@xmlobj.edge(edge_opts) do
|
114
|
+
if edge.attvalues and !edge.attvalues.empty?
|
115
|
+
mlobj.spells() do
|
116
|
+
edge.attvalues.each do |attvalue|
|
117
|
+
opts = {
|
118
|
+
start: time_format(attvalue.start),
|
119
|
+
endopen: time_format(attvalue.end),
|
120
|
+
}
|
121
|
+
xmlobj.spell(opts)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
xmlobj.attvalues() do
|
126
|
+
edge.attvalues.each do |attvalue|
|
127
|
+
attrs = {
|
128
|
+
:for => "weight",
|
129
|
+
:value => attvalue.weight.to_f,
|
130
|
+
:start => time_format(attvalue.start),
|
131
|
+
:end => time_format(attvalue.end),
|
132
|
+
}
|
133
|
+
xmlobj.attvalue(attrs)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def make_spells_compact_with(field_name, graph, options={})
|
141
|
+
graph.divide_by_time(60 * 8)
|
142
|
+
|
143
|
+
new_vertices = Rgraphum::Vertices.new
|
144
|
+
new_vertices.graph = graph
|
145
|
+
|
146
|
+
graph.vertices.each do |vertex|
|
147
|
+
same_vertex = new_vertices.find { |v|
|
148
|
+
v.send(field_name) == vertex.send(field_name)
|
149
|
+
}
|
150
|
+
unless same_vertex
|
151
|
+
new_vertex = vertex.dup
|
152
|
+
new_vertex.edges = []
|
153
|
+
new_vertices << new_vertex
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
new_edges = Rgraphum::Edges.new
|
158
|
+
|
159
|
+
graph.edges.each do |edge|
|
160
|
+
source_label = edge.source.send(field_name)
|
161
|
+
target_label = edge.target.send(field_name)
|
162
|
+
|
163
|
+
edge.source = new_vertices.find { |vertex| vertex.send(field_name) == source_label }
|
164
|
+
edge.target = new_vertices.find { |vertex| vertex.send(field_name) == target_label }
|
165
|
+
|
166
|
+
new_edge = new_edges.find { |e|
|
167
|
+
e.source == edge.source and e.source == edge.source
|
168
|
+
}
|
169
|
+
if new_edge
|
170
|
+
new_edge.weight += edge.weight
|
171
|
+
else
|
172
|
+
new_edges << edge
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
graph.vertices = new_vertices
|
177
|
+
graph.edges = new_edges
|
178
|
+
graph
|
179
|
+
end
|
180
|
+
|
181
|
+
def set_header
|
182
|
+
@xmlobj.instruct! :xml, encoding: 'UTF-8'
|
183
|
+
end
|
184
|
+
|
185
|
+
def add_spells(vertex_a, vertex_b)
|
186
|
+
raise NotImplementedError
|
187
|
+
end
|
188
|
+
|
189
|
+
def time_format(t)
|
190
|
+
t.strftime("%Y-%m-%dT%H:%M:%S")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
module Rgraphum::Parsers
|
6
|
+
# Graphviz is Graph drowing application.
|
7
|
+
# Using dot formatted file, it drow picture.
|
8
|
+
# This class make dot formatted file from graph and write file
|
9
|
+
# if you want to know details, please see graphviz manual
|
10
|
+
class GraphvizParser
|
11
|
+
class << self
|
12
|
+
# build graphviz file(.dot) content as string
|
13
|
+
# @param [Rgraphum::Graph] graph target graph .
|
14
|
+
# @param [String] layout type of layout, "dot", "neato", "fdp", "sfdp", "twopi", "circo". please see graphviz manual.
|
15
|
+
def builder(graph, layout="dot")
|
16
|
+
dot = "digraph #{graph.label || "sample"} { \n"
|
17
|
+
dot += " graph [ layout = \"#{layout}\", overlap = false ] \n\n"
|
18
|
+
|
19
|
+
graph.edges.each do |edge|
|
20
|
+
s = edge[:source][:label] || edge[:source][:id]
|
21
|
+
t = edge[:target][:label] || edge[:target][:id]
|
22
|
+
label = "[label = \"#{edge[:label]}\"]" if edge[:label]
|
23
|
+
dot += " \"#{s}\" -> \"#{t}\" #{label}; \n"
|
24
|
+
end
|
25
|
+
|
26
|
+
dot += "}"
|
27
|
+
|
28
|
+
dot
|
29
|
+
end
|
30
|
+
|
31
|
+
# save graph as .dot and image files
|
32
|
+
#
|
33
|
+
# @param [Rgraphum::Graph] graph
|
34
|
+
# @param [String] path_prefix Will create path_prefix.{dot,*} files
|
35
|
+
# @param [String] type picture type, "jpeg", "jpg", "png" and etc..
|
36
|
+
#
|
37
|
+
def export(graph, path_prefix, type)
|
38
|
+
dot = builder(graph)
|
39
|
+
open("#{path_prefix}.dot", "w") do |file|
|
40
|
+
file.write(dot)
|
41
|
+
end
|
42
|
+
cmd = "dot -T#{type} #{path_prefix}.dot -o #{path_prefix}.#{type}"
|
43
|
+
o, e, s = Open3.capture3(cmd)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(options={})
|
48
|
+
default_options = {
|
49
|
+
layout: "dot",
|
50
|
+
}
|
51
|
+
@options = default_options.merge(options)
|
52
|
+
builder(@options[:graph]) if @options.key?(:graph)
|
53
|
+
end
|
54
|
+
|
55
|
+
# dot output builder
|
56
|
+
# it call only class.method
|
57
|
+
# @see GraphvizParser::builder
|
58
|
+
def builder(graph)
|
59
|
+
@dot = self.class.builder(graph, @options[:layout])
|
60
|
+
end
|
61
|
+
|
62
|
+
# save dot file and image
|
63
|
+
# @param [String] path_prefix Will create path_prefix.{dot,*} files
|
64
|
+
# @param [String] type picture type, "jpeg", "jpg", "png" and etc..
|
65
|
+
def export(path_prefix, type=nil)
|
66
|
+
graph = @options[:graph]
|
67
|
+
builder(graph) unless @dot
|
68
|
+
self.class.export(graph, path_prefix, type)
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
unless @dot
|
73
|
+
raise ArgumentError, "Didn't build dot with builder(graph)"
|
74
|
+
end
|
75
|
+
@dot
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|