meslog 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3ac08605cefe4c5f7b125cfb6cbe83a1caae6efd
4
+ data.tar.gz: 58ec3f7dd3cd86204ebd32ea45f566257b23cff1
5
+ SHA512:
6
+ metadata.gz: 155a3e6a2c813e475d9b84a62ae18b712cf18a1b0aae958e13d21c5a09ec9aeb4a97d6221cdd604ee1c6ba409c6a35b286fd62163d44f3a8868225bcdc0de940
7
+ data.tar.gz: 38f564f6d4ba0f18194f36ce734041c1fc0679c41dd1bd75d989517940f78d8bbd92ca2b4f6fdceb232b9a3aeb909cefa2ea91ee0351433ab6dcd986baa3488a
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
4
+ before_install: gem install bundler -v 1.10.5
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in meslog.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Meslog
2
+
3
+ This project defines MESLOG data format, and provides the implementation of MESLOG data analyzer utility.
4
+
5
+ ## What is MESLOG
6
+
7
+ MESLOG is designed for ease the pain of data summarization and analysis of experimental result.
8
+
9
+ ## MESLOG data format
10
+
11
+ MESLOG data format is line-oriented text based format.
12
+ Actually any text files are qualified to be MESLOG files.
13
+ A MESLOG file is composed with (1) MESLOG data line and (2) normal line.
14
+ An example of an MESLOG data line is like:
15
+
16
+ [MESLOG.sample]{"params":{"num_threads", 1},"data":{"flops": 1.0e9}}
17
+
18
+ `[MESLOG.sample]` is a mark that means this line might be special data line tagged by "sample".
19
+ After the mark, a string of JSON map object follows, which has two keys "params" and "data".
20
+ Bit more formal definition of MESLOG data line and normal line is:
21
+ * Iff a string matches with `/\A\[MESLOG(\.[a-zA-Z0-9_-])?\]\Z/`, the string is "MESLOG header mark."
22
+ * Iff a JSON map object has only two key "params", "data", the object is "MESLOG compliant JSON object."
23
+ * Iff a line starts with a MESLOG header mark, followed by a string of a MESLOG compliant JSON object terminated by a newline character, the line is "MESLOG data line".
24
+ * If a line is not a MESLOG data line, the line is "normal line".
25
+
26
+
27
+
28
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/meslog`. To experiment with that code, run `bin/console` for an interactive prompt.
29
+
30
+ TODO: Delete this and the text above, and describe your gem
31
+
32
+ ## Installation
33
+
34
+ Add this line to your application's Gemfile:
35
+
36
+ ```ruby
37
+ gem 'meslog'
38
+ ```
39
+
40
+ And then execute:
41
+
42
+ $ bundle
43
+
44
+ Or install it yourself as:
45
+
46
+ $ gem install meslog
47
+
48
+ ## Usage
49
+
50
+ TODO: Write usage instructions here
51
+
52
+ ## Development
53
+
54
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
55
+
56
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
57
+
58
+ ## Contributing
59
+
60
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/meslog. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
61
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "meslog"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/exe/meslog ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'meslog'
4
+
5
+ exit(Meslog::CLI::Runner.new.run(ARGV))
data/lib/meslog.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "meslog/version"
2
+ require 'meslog/dataframe'
3
+ require 'meslog/cli'
4
+
5
+ module Meslog
6
+ # Your code goes here...
7
+ end
data/lib/meslog/cli.rb ADDED
@@ -0,0 +1,362 @@
1
+ # coding: utf-8
2
+
3
+ require 'json'
4
+ require 'optparse'
5
+
6
+ module Meslog
7
+ module CLI
8
+ class Runner
9
+ def initialize
10
+ end
11
+
12
+ def run(argv)
13
+ $progname = $0
14
+
15
+ if argv.first == "-h" || argv.first == "--help"
16
+ puts "TODO: show help"
17
+ elsif argv.first == "agg"
18
+ $cmdname = argv.shift
19
+ return ::Meslog::Command::Agg.new.run(argv)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ class DataCell
26
+ def initialize
27
+ @values = []
28
+ end
29
+
30
+ def push_value(val)
31
+ @values.push(val)
32
+ end
33
+
34
+ def avg
35
+ @values.inject(&:+) / @values.size.to_f
36
+ end
37
+
38
+ def size
39
+ @values.size
40
+ end
41
+ end
42
+
43
+ module Command
44
+ class BaseCommand
45
+ def initialize
46
+ @parser = OptionParser.new
47
+
48
+ # setup common options here
49
+ @x_axis = nil
50
+ @parser.on('-x', '--x-axis PARAM_PATH') do |param_path|
51
+ @x_axis = parse_param_path(param_path)
52
+ end
53
+
54
+ @select_matchers = []
55
+ @parser.on('--select MATCHER_EXP', 'Select only data lines which satisfies MATCHER_EXP and discard other lines. MATCHER_EXP ::= <param_path> \'=\' <param_val>') do |matcher_exp|
56
+ @select_matchers.push(parse_matcher_exp(matcher_exp))
57
+ end
58
+
59
+ @filter_matchers = []
60
+ @parser.on('--filter MATCHER_EXP') do |matcher_exp|
61
+ @filter_matchers.push(parse_matcher_exp(matcher_exp))
62
+ end
63
+ end
64
+
65
+ def parse_param_path(param_path_str)
66
+ param_path_str
67
+ end
68
+
69
+ def parse_matcher_exp(matcher_exp_str)
70
+ # TODO: refine after deciding specs of param_path
71
+
72
+ # bogus checking
73
+ unless matcher_exp_str =~ /^([a-z0-9_]+)=(.+)$/
74
+ raise ArgumentError.new("Invalid argument for MATCHER_EXP: #{matcher_exp_str}")
75
+ end
76
+
77
+ param_path = $~[1]
78
+ param_val_str = $~[2]
79
+
80
+ case param_val_str
81
+ when /^\d+$/
82
+ param_val = param_val_str.to_i
83
+ when /^[-+]?(?:[0-9]+(\.[0-9]*)?|(\.[0-9]+))([eE][-+]?[0-9]+)?$/
84
+ param_val = param_val_str.to_f
85
+ else
86
+ param_val = param_val_str
87
+ end
88
+
89
+ ret = {param_path: param_path, param_val: param_val}
90
+
91
+ def ret.match?(record)
92
+ record["params"][self[:param_path]] == self[:param_val]
93
+ end
94
+
95
+ ret
96
+ end
97
+
98
+ def fmt_num(x)
99
+ w = 6
100
+ w -= (Math.log10(x)).ceil
101
+ if w < 0
102
+ w = 0
103
+ end
104
+ fmtstr = "%.#{w}f"
105
+ sprintf(fmtstr, x)
106
+ end
107
+
108
+ def process_meslog(io_or_path)
109
+ if io_or_path.is_a? String
110
+ input = File.open(io_or_path)
111
+ else
112
+ input = io_or_path
113
+ end
114
+
115
+ records = []
116
+ param_paths = []
117
+ data_paths = []
118
+
119
+ # 1st pass: extract JSON records, parameter paths, and data paths
120
+ input.each_line do |line|
121
+ unless line =~ /^\[MESLOG(?:\.([a-z0-9_]+))?\](.+)$/
122
+ next
123
+ end
124
+
125
+ tag = $~[1]
126
+ json = JSON.parse($~[2])
127
+
128
+ if tag
129
+ raise NotImplementedError.new("tag is not implemented yet.")
130
+ end
131
+
132
+ record = json.dup
133
+ record["tag"] = tag
134
+
135
+ if @select_matchers.size > 0
136
+ matched = @select_matchers.any? do |matcher|
137
+ matcher.match?(record)
138
+ end
139
+
140
+ next unless matched
141
+ end
142
+
143
+ filtered = @filter_matchers.any? do |matcher|
144
+ matcher.match?(record)
145
+ end
146
+ next if filtered
147
+
148
+ records.push(record)
149
+
150
+ record["params"].keys.each do |param_path|
151
+ param_paths.push(param_path) unless param_paths.include?(param_path)
152
+ end
153
+
154
+ record["data"].keys.each do |data_path|
155
+ data_paths.push(data_path) unless data_paths.include?(data_path)
156
+ end
157
+ end
158
+
159
+ if records.size == 1
160
+ return true
161
+ end
162
+
163
+ const_param_paths = []
164
+ candidate_param_paths = []
165
+
166
+ fst_record = records.first
167
+ param_paths.each do |param_path|
168
+ if records.all? {|record|
169
+ fst_record["params"][param_path] == record["params"][param_path] }
170
+ const_param_paths.push(param_path)
171
+ else
172
+ candidate_param_paths.push(param_path)
173
+ end
174
+ end
175
+
176
+ # auto-selecting x-axis
177
+ if @x_axis.nil?
178
+ if candidate_param_paths.size == 0
179
+ puts "[ERROR] no candidate for x-axis."
180
+ exit(false)
181
+ elsif candidate_param_paths.size > 1
182
+ puts("[ERROR] multiple candidates for x-axis auto-selection. Specify one by --x-axis option.")
183
+ puts(" candidates: " + candidate_param_paths.join(", "))
184
+ exit(false)
185
+ end
186
+
187
+ @x_axis = candidate_param_paths.first
188
+ end
189
+
190
+ records = records.sort_by do |record|
191
+ record["params"][@x_axis]
192
+ end
193
+
194
+ # 2nd-pass: grouping records with parameter values excluding x-axis.
195
+ preset_param_paths = param_paths.select do |param_path|
196
+ param_path != @x_axis
197
+ end
198
+
199
+ record_groups = Hash.new
200
+ records.each do |record|
201
+ preset_params = preset_param_paths.map do |param_path|
202
+ record["params"][param_path]
203
+ end
204
+
205
+ record_groups[preset_params] ||= []
206
+ group = record_groups[preset_params]
207
+
208
+ group.push(record)
209
+ end
210
+
211
+ dataframe_list = []
212
+
213
+ # generate dataframe for each group
214
+ record_groups.each do |preset_params, group|
215
+ dataframe = ::Meslog::Dataframe.new(@x_axis, data_paths)
216
+
217
+ group.each do |record|
218
+ x = record["params"][@x_axis]
219
+
220
+ data_paths.each do |data_path|
221
+ dataframe[x][data_path] ||= DataCell.new
222
+ dataframe[x][data_path].push_value(record["data"][data_path])
223
+ end
224
+ end
225
+
226
+ preset_param_paths.each do |path|
227
+ idx = preset_param_paths.index(path)
228
+ val = preset_params[idx]
229
+ dataframe.preset_params[path] = val
230
+ if const_param_paths.include?(path)
231
+ dataframe.const_params[path] = val
232
+ end
233
+ end
234
+
235
+ dataframe_list.push(dataframe)
236
+ end
237
+
238
+ return dataframe_list
239
+ end
240
+ end
241
+
242
+ class Plot < BaseCommand
243
+ def initialize
244
+ super()
245
+
246
+ @parser.banner = <<EOS
247
+ #{$progname} agg MESLOG_FILE [options]
248
+
249
+ Options:
250
+ EOS
251
+
252
+ @y_axis = nil
253
+ @parser.on('-y', '--y-axis PARAM_PATH') do |param_path|
254
+ @y_axis = parse_param_path(param_path)
255
+ end
256
+ end
257
+
258
+ def run(argv)
259
+ @parser.parse!(argv)
260
+
261
+ if argv.size == 0
262
+ print_help("MESLOG_FILE required.", true)
263
+ end
264
+
265
+ file = argv.shift
266
+
267
+ if file == "-"
268
+ file = $stdin
269
+ else
270
+ file = File.open(file)
271
+ end
272
+
273
+ ret = process_meslog(file)
274
+
275
+ record_groups = ret[:record_groups]
276
+ preset_param_paths = ret[:preset_param_paths]
277
+ const_param_paths = ret[:const_param_paths]
278
+ data_paths = ret[:data_paths]
279
+
280
+ record_groups.each do |preset_params, group|
281
+
282
+ end
283
+ end
284
+ end
285
+
286
+ class Agg < BaseCommand
287
+ def initialize
288
+ super()
289
+
290
+ @parser.banner = <<EOS
291
+ #{$progname} agg MESLOG_FILE [options]
292
+
293
+ Options:
294
+ EOS
295
+ end
296
+
297
+ def plaintext_frame_label(dataframe)
298
+ const_param_label = dataframe.const_params.map do |key, val|
299
+ "#{key}: #{val}"
300
+ end.join(", ")
301
+
302
+ other_param_label = dataframe.preset_params.select do |key, val|
303
+ ! dataframe.const_params.has_key?(key)
304
+ end.map do |key, val|
305
+ "#{key} = #{val}"
306
+ end.join(", ")
307
+
308
+ if other_param_label.empty?
309
+ const_param_label
310
+ else
311
+ "#{const_param_label} || #{other_param_label}"
312
+ end
313
+ end
314
+
315
+ def run(argv)
316
+ @parser.parse!(argv)
317
+
318
+ if argv.size == 0
319
+ print_help("MESLOG_FILE required.", true)
320
+ end
321
+
322
+ file = argv.shift
323
+
324
+ if file == "-"
325
+ file = $stdin
326
+ else
327
+ file = File.open(file)
328
+ end
329
+
330
+ dataframe_list = process_meslog(file)
331
+
332
+ dataframe_list.each do |dataframe|
333
+ frame_label = plaintext_frame_label(dataframe)
334
+
335
+ puts("#== #{frame_label} ==")
336
+ puts(([dataframe.axis_path, "num_records"] + dataframe.data_paths).join("\t"))
337
+ dataframe.each do |x, data_cells|
338
+ puts([x,
339
+ data_cells[dataframe.data_paths.first].size,
340
+ *dataframe.data_paths.map{|path|
341
+ fmt_num(data_cells[path].avg)
342
+ }].map(&:to_s).join("\t"))
343
+ end
344
+
345
+ puts("")
346
+ puts("")
347
+ end
348
+
349
+ true
350
+ end
351
+
352
+ def print_help(errmsg = nil, do_exit = false)
353
+ if errmsg
354
+ $stderr.puts("[ERROR] #{errmsg}")
355
+ end
356
+ puts(@parser.help)
357
+
358
+ exit(false) if do_exit
359
+ end
360
+ end
361
+ end
362
+ end
@@ -0,0 +1,33 @@
1
+ module Meslog
2
+ class Dataframe
3
+ attr_accessor :preset_params
4
+ attr_accessor :const_params
5
+ attr_reader :axis_path
6
+ attr_reader :data_paths
7
+
8
+ def initialize(axis_path, data_paths)
9
+ @records = Hash.new
10
+ @axis_path = axis_path
11
+ @data_paths = data_paths
12
+ @preset_params = Hash.new
13
+ @const_params = Hash.new
14
+ end
15
+
16
+ def [](key)
17
+ if @records.has_key?(key)
18
+ record = @records[key]
19
+ else
20
+ record = Hash.new
21
+ @records[key] = record
22
+ end
23
+
24
+ record
25
+ end
26
+
27
+ def each
28
+ @records.each do |key,val|
29
+ yield(key, val)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Meslog
2
+ VERSION = "0.1.0"
3
+ end
data/meslog.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'meslog/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "meslog"
8
+ spec.version = Meslog::VERSION
9
+ spec.authors = ["Yuto Hayamizu"]
10
+ spec.email = ["y.hayamizu@gmail.com"]
11
+
12
+ spec.summary = %q{Measurement result analyzer}
13
+ spec.description = %q{MESLOG is an utility for analyzing measurement result data recorded in plain text log files.}
14
+ spec.homepage = "https://github.com/hayamiz/meslog"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: meslog
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yuto Hayamizu
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-10-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: MESLOG is an utility for analyzing measurement result data recorded in
56
+ plain text log files.
57
+ email:
58
+ - y.hayamizu@gmail.com
59
+ executables:
60
+ - meslog
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - ".rspec"
66
+ - ".travis.yml"
67
+ - CODE_OF_CONDUCT.md
68
+ - Gemfile
69
+ - README.md
70
+ - Rakefile
71
+ - bin/console
72
+ - bin/setup
73
+ - exe/meslog
74
+ - lib/meslog.rb
75
+ - lib/meslog/cli.rb
76
+ - lib/meslog/dataframe.rb
77
+ - lib/meslog/version.rb
78
+ - meslog.gemspec
79
+ homepage: https://github.com/hayamiz/meslog
80
+ licenses: []
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.5.1
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Measurement result analyzer
102
+ test_files: []