geoptima 0.1.1 → 0.1.2
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.
- data/README.rdoc +18 -4
- data/bin/csv_chart +67 -27
- data/bin/csv_merge +75 -0
- data/bin/show_geoptima +1 -1
- data/examples/csv_chart.rb +67 -27
- data/examples/csv_merge.rb +75 -0
- data/examples/show_geoptima.rb +1 -1
- data/lib/geoptima/chart.rb +11 -3
- data/lib/geoptima/daterange.rb +54 -3
- data/lib/geoptima/version.rb +1 -1
- metadata +6 -4
data/README.rdoc
CHANGED
@@ -11,7 +11,7 @@ For more information on Geoptima refer to:
|
|
11
11
|
|
12
12
|
The current version includes the core geoptima.rb library as well as a few command-line apps for using the library. We have not yet documented the library, but some of the command-line scripts are described below.
|
13
13
|
|
14
|
-
|
14
|
+
==== show_geoptima
|
15
15
|
|
16
16
|
This script imports the JSON files on the command-line and then:
|
17
17
|
* Prints out basic information about each file (subscriber information, start date and number of events)
|
@@ -38,15 +38,15 @@ Which event types to include and various other options are available using the c
|
|
38
38
|
|
39
39
|
Currently the script also locates events that are close enough in time to GPS events. We hope to improve this with interpolation in the near future to be more compatible with the results from the commercial solutions. This time-window is also used for some of the extended header information, like LAC and CI, and effectively duplicates those fields from their own events to others. Take this into account when doing statistics on the results. It is better to use the original values, not the duplicates, if you want reliable statistics.
|
40
40
|
|
41
|
-
|
41
|
+
==== csv_stats
|
42
42
|
|
43
43
|
This script reads any CSV file and outputs histograms of the values for each column. These can be manually imported into a spreadsheet or charting program for further analysis or charting.
|
44
44
|
|
45
|
-
|
45
|
+
==== csv_charts
|
46
46
|
|
47
47
|
This script also performs statistics on CSV files but is much more powerful, including automatic output of charts based on columns of data, as well as a configurable system for adding new columns by modifying or merging other columns, as designing specific charts for output.
|
48
48
|
|
49
|
-
Usage: csv_chart <-dhamt> <-S specfile> <-N name> <-D dir> <-T range> <-P diversity> files...
|
49
|
+
Usage: csv_chart <-dhamt> <-S specfile> <-N name> <-D dir> <-T range> <-P diversity> files...
|
50
50
|
-d debug mode
|
51
51
|
-h print this help (true)
|
52
52
|
-a automatically create charts for all properties (true)
|
@@ -109,6 +109,14 @@ This example uses some fancy Ruby code to replace cryptic HTTP URL codes with no
|
|
109
109
|
|
110
110
|
This one copies the 'runningApp.appName' column to the 'Apps' column, but only if the 'runningApps.state' column is set to 'STARTED'.
|
111
111
|
|
112
|
+
=== Charts
|
113
|
+
|
114
|
+
The charts described above can also be seen as examples in the Gallery wiki page at https://github.com/craigtaverner/geoptima.rb/wiki/Gallery.
|
115
|
+
|
116
|
+
One example included here is a distribution of call status over time.
|
117
|
+
|
118
|
+
https://github.com/craigtaverner/geoptima.rb/blob/master/images/Chart_All_Call%20Status_category_distribution.png?raw=true
|
119
|
+
|
112
120
|
=== Installation
|
113
121
|
|
114
122
|
Two options:
|
@@ -125,6 +133,12 @@ This should produce a file called 357841036600753.csv containing a CSV version o
|
|
125
133
|
|
126
134
|
The examples directory also contains a number of sample *.spec files for the csv_chart command.
|
127
135
|
|
136
|
+
==== Installing on Windows
|
137
|
+
|
138
|
+
For windows use the RubyInstaller at http://rubyinstaller.org/
|
139
|
+
|
140
|
+
To get chart support working you will probably need to install the DevKit as described on https://github.com/oneclick/rubyinstaller/wiki/Development-Kit
|
141
|
+
|
128
142
|
=== Contributing
|
129
143
|
|
130
144
|
Have you found a bug, need help or have a patch ?
|
data/bin/csv_chart
CHANGED
@@ -10,11 +10,12 @@ require 'geoptima/options'
|
|
10
10
|
require 'fileutils'
|
11
11
|
require 'geoptima/daterange'
|
12
12
|
|
13
|
-
Geoptima::assert_version("0.1.
|
13
|
+
Geoptima::assert_version("0.1.2")
|
14
14
|
Geoptima::Chart.available? || puts("No charting libraries available") || exit(-1)
|
15
15
|
|
16
16
|
$export_dir = '.'
|
17
17
|
$diversity = 40.0
|
18
|
+
$chart_width = 1024
|
18
19
|
|
19
20
|
$files = Geoptima::Options.process_args do |option|
|
20
21
|
option.m {$merge_all = true}
|
@@ -24,15 +25,15 @@ $files = Geoptima::Options.process_args do |option|
|
|
24
25
|
option.N {$merged_name = ARGV.shift}
|
25
26
|
option.S {$specfile = ARGV.shift}
|
26
27
|
option.P {$diversity = ARGV.shift.to_f}
|
28
|
+
option.W {$chart_width = ARGV.shift.to_i}
|
27
29
|
option.T do
|
28
|
-
$time_range = Geoptima::DateRange.
|
29
|
-
DateTime.parse t
|
30
|
-
end))
|
30
|
+
$time_range = Geoptima::DateRange.from ARGV.shift
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
FileUtils.mkdir_p $export_dir
|
35
35
|
|
36
|
+
$chart_width=100 if($chart_width.to_i < 100)
|
36
37
|
$create_all = true unless($specfile)
|
37
38
|
$merge_all = true if($time_split)
|
38
39
|
$help = true unless($files.length>0)
|
@@ -49,6 +50,7 @@ Usage: csv_chart <-dhamt> <-S specfile> <-N name> <-D dir> <-T range> <-P divers
|
|
49
50
|
-S use chart specification in specified file: #{$specfile}
|
50
51
|
-P diversity threshold in percentage for automatic reports: #{$diversity}
|
51
52
|
-T set time-range filter: #{$time_range}
|
53
|
+
-W set default chart-width: #{$chart_width}
|
52
54
|
Files to import: #{$files.join(', ')}
|
53
55
|
EOHELP
|
54
56
|
exit
|
@@ -63,8 +65,7 @@ class Stats
|
|
63
65
|
@numerical = true
|
64
66
|
end
|
65
67
|
def add(field)
|
66
|
-
|
67
|
-
if field && field =~ /\w/
|
68
|
+
unless field.nil? || field == ''
|
68
69
|
@numerical &&= is_number?(field)
|
69
70
|
stats[field] ||= 0
|
70
71
|
stats[field] += 1
|
@@ -129,12 +130,12 @@ class StatsManager
|
|
129
130
|
end
|
130
131
|
def add_all(fields,headers)
|
131
132
|
fields.each_with_index do |field,index|
|
132
|
-
add(field,headers[index])
|
133
|
+
add(field,headers[index],index)
|
133
134
|
end
|
134
135
|
$specs && $specs.add_fields(self,fields)
|
135
136
|
end
|
136
|
-
def add(field,header)
|
137
|
-
puts "\tAdding field '#{field}' for property #{header}" if($debug)
|
137
|
+
def add(field,header,index)
|
138
|
+
puts "\tAdding field[#{index}] '#{field}' for property #{header}" if($debug)
|
138
139
|
add_header(header) unless(@stats[header])
|
139
140
|
@stats[header].add(field)
|
140
141
|
end
|
@@ -148,10 +149,11 @@ end
|
|
148
149
|
|
149
150
|
module Geoptima
|
150
151
|
class StatSpec
|
151
|
-
attr_reader :header, :index, :indices, :fields, :options, :proc, :groups
|
152
|
+
attr_reader :header, :event, :index, :indices, :fields, :options, :proc, :groups
|
152
153
|
def initialize(header,*fields,&block)
|
153
154
|
@header = header
|
154
155
|
@fields = fields
|
156
|
+
@event = @fields[0].split(/\./)[0]
|
155
157
|
@proc = block
|
156
158
|
@groups = {}
|
157
159
|
if @fields[-1].is_a?(Hash)
|
@@ -182,20 +184,42 @@ module Geoptima
|
|
182
184
|
key = @group.call(time)
|
183
185
|
ghead = "#{header} #{key}"
|
184
186
|
@groups[key] = ghead
|
185
|
-
stats_manager.add(map(fields),ghead)
|
187
|
+
stats_manager.add(map(fields),ghead,nil)
|
186
188
|
end
|
187
189
|
rescue ArgumentError
|
188
190
|
puts "Error: Unable to process time field[#{time}]: #{$!}"
|
189
191
|
end
|
190
192
|
end
|
191
|
-
stats_manager.add(map(fields),header)
|
193
|
+
stats_manager.add(map(fields),header,index)
|
192
194
|
end
|
193
|
-
def
|
194
|
-
|
195
|
-
div = options[:div].to_i
|
196
|
-
div = 1 if(div<1)
|
195
|
+
def div
|
196
|
+
unless @div
|
197
|
+
@div = options[:div].to_i
|
198
|
+
@div = 1 if(@div<1)
|
199
|
+
end
|
200
|
+
@div
|
201
|
+
end
|
202
|
+
def cats
|
203
|
+
@cats ||= (options[:categories] || []).map{|c| c.to_i}
|
204
|
+
end
|
205
|
+
def mk_div_range(val)
|
206
|
+
unless val.nil? || val == ''
|
197
207
|
min = val.to_i/div * div
|
198
|
-
"#{min}..#{min+div}"
|
208
|
+
(div == 1) ? min : "#{min}..#{min+div}"
|
209
|
+
else
|
210
|
+
val
|
211
|
+
end
|
212
|
+
end
|
213
|
+
def mk_cat_range(val)
|
214
|
+
if val.to_s =~ /\w/
|
215
|
+
val = val.to_i
|
216
|
+
puts "\t\tSearching for value[#{val}] in categories #{cats.inspect}" if($debug)
|
217
|
+
min = cats[0]
|
218
|
+
cats.each do |c|
|
219
|
+
max = c
|
220
|
+
return "#{min}..#{max}" if(min<=val && max>val)
|
221
|
+
min = c
|
222
|
+
end
|
199
223
|
else
|
200
224
|
val
|
201
225
|
end
|
@@ -204,12 +228,22 @@ module Geoptima
|
|
204
228
|
if @indices
|
205
229
|
puts "StatSpec[#{self}]: #{options.inspect}" if($debug)
|
206
230
|
vals = @indices.map{|i| values[i]}
|
231
|
+
puts "\tVALUES: #{vals.inspect}" if($debug)
|
232
|
+
(options[:filter] || {}).each do |field,expected|
|
233
|
+
puts "\t\tChecking if field #{field} is #{expected}" if($debug)
|
234
|
+
puts "\t\tLooking for #{field} or #{event}.#{field} in #{@fields.inspect}" if($debug)
|
235
|
+
hi = @fields.index(field.to_s) || @fields.index("#{event}.#{field}")
|
236
|
+
puts "\t\t#{field} -> #{hi} -> #{hi && vals[hi]}" if($debug)
|
237
|
+
return nil unless(hi && vals[hi] && (expected === vals[hi].downcase || vals[hi].downcase === expected.to_s.downcase))
|
238
|
+
end
|
239
|
+
val = proc.nil? ? vals[0] : proc.call(*vals)
|
240
|
+
puts "\tBLOCK MAP: #{vals.inspect} --> #{val.inspect}" if($debug)
|
207
241
|
if options[:div]
|
208
|
-
|
242
|
+
val = mk_div_range(val)
|
243
|
+
elsif options[:categories]
|
244
|
+
val = mk_cat_range(val)
|
209
245
|
end
|
210
|
-
puts "
|
211
|
-
val = proc && proc.call(*vals) || vals[0]
|
212
|
-
puts "StatSpec[#{self}]: #{vals.inspect} --> #{val.inspect}" if($debug)
|
246
|
+
puts "\tDIV/CAT MAP: #{vals.inspect} --> #{val.inspect}" if($debug)
|
213
247
|
val
|
214
248
|
end
|
215
249
|
end
|
@@ -260,15 +294,19 @@ module Geoptima
|
|
260
294
|
if grouped_stats.length > 0
|
261
295
|
title = options[:title]
|
262
296
|
title ||= "#{header} Distribution"
|
263
|
-
options.merge!( :title => title, :width =>
|
297
|
+
options.merge!( :title => title, :width => $chart_width )
|
264
298
|
value_map = {}
|
265
299
|
groups = grouped_stats.keys.sort
|
266
300
|
groups.each_with_index do |name,index|
|
267
301
|
gs = grouped_stats[name]
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
302
|
+
if gs
|
303
|
+
hist = gs.stats
|
304
|
+
hist.keys.each do |k|
|
305
|
+
value_map[k] ||= [].fill(0,0...groups.length)
|
306
|
+
value_map[k][index] = hist[k]
|
307
|
+
end
|
308
|
+
else
|
309
|
+
puts "No grouped stats found for #{name}"
|
272
310
|
end
|
273
311
|
end
|
274
312
|
legends = value_map.keys.sort
|
@@ -284,7 +322,7 @@ module Geoptima
|
|
284
322
|
end
|
285
323
|
values = keys.map{|k| hist[k]}
|
286
324
|
title ||= "#{header} Distribution"
|
287
|
-
options.merge!( :title => title, :width =>
|
325
|
+
options.merge!( :title => title, :width => $chart_width )
|
288
326
|
g = Geoptima::Chart.send "draw_#{chart_type}_chart", stats_manager.name, keys, values, options
|
289
327
|
end
|
290
328
|
g.write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_#{chart_type}_distribution.png")
|
@@ -321,7 +359,9 @@ module Geoptima
|
|
321
359
|
end
|
322
360
|
end
|
323
361
|
def add_fields(stats_manager,fields)
|
362
|
+
puts "Adding fields to #{stat_specs.length} StatSpec's" if($debug)
|
324
363
|
stat_specs.each do |stat_spec|
|
364
|
+
puts "Adding fields to StatSpec: #{stat_spec}" if($debug)
|
325
365
|
stat_spec.add(stats_manager,fields)
|
326
366
|
end
|
327
367
|
end
|
data/bin/csv_merge
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# useful if being run inside a source code checkout
|
4
|
+
$: << 'lib'
|
5
|
+
$: << '../lib'
|
6
|
+
|
7
|
+
require 'geoptima/version'
|
8
|
+
require 'geoptima/options'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'geoptima/daterange'
|
11
|
+
|
12
|
+
Geoptima::assert_version("0.1.2")
|
13
|
+
|
14
|
+
$export_dir = '.'
|
15
|
+
$export_name = 'merged.csv'
|
16
|
+
|
17
|
+
$files = Geoptima::Options.process_args do |option|
|
18
|
+
option.t {$time_split = true}
|
19
|
+
option.D {$export_dir = ARGV.shift}
|
20
|
+
option.N {$export_name = ARGV.shift}
|
21
|
+
option.T do
|
22
|
+
$time_range = Geoptima::DateRange.new(*(ARGV.shift.split(/[\,]+/).map do |t|
|
23
|
+
DateTime.parse t
|
24
|
+
end))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
FileUtils.mkdir_p $export_dir
|
29
|
+
|
30
|
+
$help = true unless($files.length>0)
|
31
|
+
if $help
|
32
|
+
puts <<EOHELP
|
33
|
+
Usage: csv_chart <-dht> <-N name> <-D dir> <-T range> files...
|
34
|
+
-d debug mode #{cw $debug}
|
35
|
+
-h print this help #{cw $help}
|
36
|
+
-t merge and split by time (days) #{cw $time_split}
|
37
|
+
-N use specified name for merged dataset: #{$merged_name}
|
38
|
+
-D export to specified directory: #{$export_dir}
|
39
|
+
-T set time-range filter: #{$time_range}
|
40
|
+
Files to import: #{$files.join(', ')}
|
41
|
+
EOHELP
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
class CSVData
|
46
|
+
attr_reader :headers
|
47
|
+
def initialize(headers)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
$files.each do |file|
|
52
|
+
lines = 0
|
53
|
+
headers = nil
|
54
|
+
filename = File.basename(file)
|
55
|
+
(names = filename.split(/[_\.]/)).pop
|
56
|
+
name = $merge_all ? ($merged_name || 'All') : names.join('_')
|
57
|
+
$stats_managers[name] ||= StatsManager.new(name)
|
58
|
+
puts "About to read file #{file}"
|
59
|
+
File.open(file).each do |line|
|
60
|
+
lines += 1
|
61
|
+
fields=line.chomp.split(/\t/)
|
62
|
+
if headers
|
63
|
+
puts "Processing line: #{line}" if($debug)
|
64
|
+
$stats_managers[name].add_all(fields,headers)
|
65
|
+
else
|
66
|
+
headers = fields
|
67
|
+
if headers.length<2
|
68
|
+
puts "Too few headers, rejecting #{file}"
|
69
|
+
break
|
70
|
+
end
|
71
|
+
$stats_managers[name].set_headers(headers)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
data/bin/show_geoptima
CHANGED
data/examples/csv_chart.rb
CHANGED
@@ -10,11 +10,12 @@ require 'geoptima/options'
|
|
10
10
|
require 'fileutils'
|
11
11
|
require 'geoptima/daterange'
|
12
12
|
|
13
|
-
Geoptima::assert_version("0.1.
|
13
|
+
Geoptima::assert_version("0.1.2")
|
14
14
|
Geoptima::Chart.available? || puts("No charting libraries available") || exit(-1)
|
15
15
|
|
16
16
|
$export_dir = '.'
|
17
17
|
$diversity = 40.0
|
18
|
+
$chart_width = 1024
|
18
19
|
|
19
20
|
$files = Geoptima::Options.process_args do |option|
|
20
21
|
option.m {$merge_all = true}
|
@@ -24,15 +25,15 @@ $files = Geoptima::Options.process_args do |option|
|
|
24
25
|
option.N {$merged_name = ARGV.shift}
|
25
26
|
option.S {$specfile = ARGV.shift}
|
26
27
|
option.P {$diversity = ARGV.shift.to_f}
|
28
|
+
option.W {$chart_width = ARGV.shift.to_i}
|
27
29
|
option.T do
|
28
|
-
$time_range = Geoptima::DateRange.
|
29
|
-
DateTime.parse t
|
30
|
-
end))
|
30
|
+
$time_range = Geoptima::DateRange.from ARGV.shift
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
FileUtils.mkdir_p $export_dir
|
35
35
|
|
36
|
+
$chart_width=100 if($chart_width.to_i < 100)
|
36
37
|
$create_all = true unless($specfile)
|
37
38
|
$merge_all = true if($time_split)
|
38
39
|
$help = true unless($files.length>0)
|
@@ -49,6 +50,7 @@ Usage: csv_chart <-dhamt> <-S specfile> <-N name> <-D dir> <-T range> <-P divers
|
|
49
50
|
-S use chart specification in specified file: #{$specfile}
|
50
51
|
-P diversity threshold in percentage for automatic reports: #{$diversity}
|
51
52
|
-T set time-range filter: #{$time_range}
|
53
|
+
-W set default chart-width: #{$chart_width}
|
52
54
|
Files to import: #{$files.join(', ')}
|
53
55
|
EOHELP
|
54
56
|
exit
|
@@ -63,8 +65,7 @@ class Stats
|
|
63
65
|
@numerical = true
|
64
66
|
end
|
65
67
|
def add(field)
|
66
|
-
|
67
|
-
if field && field =~ /\w/
|
68
|
+
unless field.nil? || field == ''
|
68
69
|
@numerical &&= is_number?(field)
|
69
70
|
stats[field] ||= 0
|
70
71
|
stats[field] += 1
|
@@ -129,12 +130,12 @@ class StatsManager
|
|
129
130
|
end
|
130
131
|
def add_all(fields,headers)
|
131
132
|
fields.each_with_index do |field,index|
|
132
|
-
add(field,headers[index])
|
133
|
+
add(field,headers[index],index)
|
133
134
|
end
|
134
135
|
$specs && $specs.add_fields(self,fields)
|
135
136
|
end
|
136
|
-
def add(field,header)
|
137
|
-
puts "\tAdding field '#{field}' for property #{header}" if($debug)
|
137
|
+
def add(field,header,index)
|
138
|
+
puts "\tAdding field[#{index}] '#{field}' for property #{header}" if($debug)
|
138
139
|
add_header(header) unless(@stats[header])
|
139
140
|
@stats[header].add(field)
|
140
141
|
end
|
@@ -148,10 +149,11 @@ end
|
|
148
149
|
|
149
150
|
module Geoptima
|
150
151
|
class StatSpec
|
151
|
-
attr_reader :header, :index, :indices, :fields, :options, :proc, :groups
|
152
|
+
attr_reader :header, :event, :index, :indices, :fields, :options, :proc, :groups
|
152
153
|
def initialize(header,*fields,&block)
|
153
154
|
@header = header
|
154
155
|
@fields = fields
|
156
|
+
@event = @fields[0].split(/\./)[0]
|
155
157
|
@proc = block
|
156
158
|
@groups = {}
|
157
159
|
if @fields[-1].is_a?(Hash)
|
@@ -182,20 +184,42 @@ module Geoptima
|
|
182
184
|
key = @group.call(time)
|
183
185
|
ghead = "#{header} #{key}"
|
184
186
|
@groups[key] = ghead
|
185
|
-
stats_manager.add(map(fields),ghead)
|
187
|
+
stats_manager.add(map(fields),ghead,nil)
|
186
188
|
end
|
187
189
|
rescue ArgumentError
|
188
190
|
puts "Error: Unable to process time field[#{time}]: #{$!}"
|
189
191
|
end
|
190
192
|
end
|
191
|
-
stats_manager.add(map(fields),header)
|
193
|
+
stats_manager.add(map(fields),header,index)
|
192
194
|
end
|
193
|
-
def
|
194
|
-
|
195
|
-
div = options[:div].to_i
|
196
|
-
div = 1 if(div<1)
|
195
|
+
def div
|
196
|
+
unless @div
|
197
|
+
@div = options[:div].to_i
|
198
|
+
@div = 1 if(@div<1)
|
199
|
+
end
|
200
|
+
@div
|
201
|
+
end
|
202
|
+
def cats
|
203
|
+
@cats ||= (options[:categories] || []).map{|c| c.to_i}
|
204
|
+
end
|
205
|
+
def mk_div_range(val)
|
206
|
+
unless val.nil? || val == ''
|
197
207
|
min = val.to_i/div * div
|
198
|
-
"#{min}..#{min+div}"
|
208
|
+
(div == 1) ? min : "#{min}..#{min+div}"
|
209
|
+
else
|
210
|
+
val
|
211
|
+
end
|
212
|
+
end
|
213
|
+
def mk_cat_range(val)
|
214
|
+
if val.to_s =~ /\w/
|
215
|
+
val = val.to_i
|
216
|
+
puts "\t\tSearching for value[#{val}] in categories #{cats.inspect}" if($debug)
|
217
|
+
min = cats[0]
|
218
|
+
cats.each do |c|
|
219
|
+
max = c
|
220
|
+
return "#{min}..#{max}" if(min<=val && max>val)
|
221
|
+
min = c
|
222
|
+
end
|
199
223
|
else
|
200
224
|
val
|
201
225
|
end
|
@@ -204,12 +228,22 @@ module Geoptima
|
|
204
228
|
if @indices
|
205
229
|
puts "StatSpec[#{self}]: #{options.inspect}" if($debug)
|
206
230
|
vals = @indices.map{|i| values[i]}
|
231
|
+
puts "\tVALUES: #{vals.inspect}" if($debug)
|
232
|
+
(options[:filter] || {}).each do |field,expected|
|
233
|
+
puts "\t\tChecking if field #{field} is #{expected}" if($debug)
|
234
|
+
puts "\t\tLooking for #{field} or #{event}.#{field} in #{@fields.inspect}" if($debug)
|
235
|
+
hi = @fields.index(field.to_s) || @fields.index("#{event}.#{field}")
|
236
|
+
puts "\t\t#{field} -> #{hi} -> #{hi && vals[hi]}" if($debug)
|
237
|
+
return nil unless(hi && vals[hi] && (expected === vals[hi].downcase || vals[hi].downcase === expected.to_s.downcase))
|
238
|
+
end
|
239
|
+
val = proc.nil? ? vals[0] : proc.call(*vals)
|
240
|
+
puts "\tBLOCK MAP: #{vals.inspect} --> #{val.inspect}" if($debug)
|
207
241
|
if options[:div]
|
208
|
-
|
242
|
+
val = mk_div_range(val)
|
243
|
+
elsif options[:categories]
|
244
|
+
val = mk_cat_range(val)
|
209
245
|
end
|
210
|
-
puts "
|
211
|
-
val = proc && proc.call(*vals) || vals[0]
|
212
|
-
puts "StatSpec[#{self}]: #{vals.inspect} --> #{val.inspect}" if($debug)
|
246
|
+
puts "\tDIV/CAT MAP: #{vals.inspect} --> #{val.inspect}" if($debug)
|
213
247
|
val
|
214
248
|
end
|
215
249
|
end
|
@@ -260,15 +294,19 @@ module Geoptima
|
|
260
294
|
if grouped_stats.length > 0
|
261
295
|
title = options[:title]
|
262
296
|
title ||= "#{header} Distribution"
|
263
|
-
options.merge!( :title => title, :width =>
|
297
|
+
options.merge!( :title => title, :width => $chart_width )
|
264
298
|
value_map = {}
|
265
299
|
groups = grouped_stats.keys.sort
|
266
300
|
groups.each_with_index do |name,index|
|
267
301
|
gs = grouped_stats[name]
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
302
|
+
if gs
|
303
|
+
hist = gs.stats
|
304
|
+
hist.keys.each do |k|
|
305
|
+
value_map[k] ||= [].fill(0,0...groups.length)
|
306
|
+
value_map[k][index] = hist[k]
|
307
|
+
end
|
308
|
+
else
|
309
|
+
puts "No grouped stats found for #{name}"
|
272
310
|
end
|
273
311
|
end
|
274
312
|
legends = value_map.keys.sort
|
@@ -284,7 +322,7 @@ module Geoptima
|
|
284
322
|
end
|
285
323
|
values = keys.map{|k| hist[k]}
|
286
324
|
title ||= "#{header} Distribution"
|
287
|
-
options.merge!( :title => title, :width =>
|
325
|
+
options.merge!( :title => title, :width => $chart_width )
|
288
326
|
g = Geoptima::Chart.send "draw_#{chart_type}_chart", stats_manager.name, keys, values, options
|
289
327
|
end
|
290
328
|
g.write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_#{chart_type}_distribution.png")
|
@@ -321,7 +359,9 @@ module Geoptima
|
|
321
359
|
end
|
322
360
|
end
|
323
361
|
def add_fields(stats_manager,fields)
|
362
|
+
puts "Adding fields to #{stat_specs.length} StatSpec's" if($debug)
|
324
363
|
stat_specs.each do |stat_spec|
|
364
|
+
puts "Adding fields to StatSpec: #{stat_spec}" if($debug)
|
325
365
|
stat_spec.add(stats_manager,fields)
|
326
366
|
end
|
327
367
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# useful if being run inside a source code checkout
|
4
|
+
$: << 'lib'
|
5
|
+
$: << '../lib'
|
6
|
+
|
7
|
+
require 'geoptima/version'
|
8
|
+
require 'geoptima/options'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'geoptima/daterange'
|
11
|
+
|
12
|
+
Geoptima::assert_version("0.1.2")
|
13
|
+
|
14
|
+
$export_dir = '.'
|
15
|
+
$export_name = 'merged.csv'
|
16
|
+
|
17
|
+
$files = Geoptima::Options.process_args do |option|
|
18
|
+
option.t {$time_split = true}
|
19
|
+
option.D {$export_dir = ARGV.shift}
|
20
|
+
option.N {$export_name = ARGV.shift}
|
21
|
+
option.T do
|
22
|
+
$time_range = Geoptima::DateRange.new(*(ARGV.shift.split(/[\,]+/).map do |t|
|
23
|
+
DateTime.parse t
|
24
|
+
end))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
FileUtils.mkdir_p $export_dir
|
29
|
+
|
30
|
+
$help = true unless($files.length>0)
|
31
|
+
if $help
|
32
|
+
puts <<EOHELP
|
33
|
+
Usage: csv_chart <-dht> <-N name> <-D dir> <-T range> files...
|
34
|
+
-d debug mode #{cw $debug}
|
35
|
+
-h print this help #{cw $help}
|
36
|
+
-t merge and split by time (days) #{cw $time_split}
|
37
|
+
-N use specified name for merged dataset: #{$merged_name}
|
38
|
+
-D export to specified directory: #{$export_dir}
|
39
|
+
-T set time-range filter: #{$time_range}
|
40
|
+
Files to import: #{$files.join(', ')}
|
41
|
+
EOHELP
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
class CSVData
|
46
|
+
attr_reader :headers
|
47
|
+
def initialize(headers)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
$files.each do |file|
|
52
|
+
lines = 0
|
53
|
+
headers = nil
|
54
|
+
filename = File.basename(file)
|
55
|
+
(names = filename.split(/[_\.]/)).pop
|
56
|
+
name = $merge_all ? ($merged_name || 'All') : names.join('_')
|
57
|
+
$stats_managers[name] ||= StatsManager.new(name)
|
58
|
+
puts "About to read file #{file}"
|
59
|
+
File.open(file).each do |line|
|
60
|
+
lines += 1
|
61
|
+
fields=line.chomp.split(/\t/)
|
62
|
+
if headers
|
63
|
+
puts "Processing line: #{line}" if($debug)
|
64
|
+
$stats_managers[name].add_all(fields,headers)
|
65
|
+
else
|
66
|
+
headers = fields
|
67
|
+
if headers.length<2
|
68
|
+
puts "Too few headers, rejecting #{file}"
|
69
|
+
break
|
70
|
+
end
|
71
|
+
$stats_managers[name].set_headers(headers)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
data/examples/show_geoptima.rb
CHANGED
data/lib/geoptima/chart.rb
CHANGED
@@ -45,7 +45,7 @@ module Geoptima
|
|
45
45
|
def self.draw_line_chart(legend,keys,values,options={})
|
46
46
|
g = make_chart(:line,{:show_points => false}.merge(options))
|
47
47
|
g.data(legend, values)
|
48
|
-
g.labels= {0=>keys[0], (keys.length-1)=>keys[-1]}
|
48
|
+
g.labels= {0=>keys[0].to_s, (keys.length-1)=>keys[-1].to_s}
|
49
49
|
options[:maximum_value] && g.maximum_value = options[:maximum_value].to_i
|
50
50
|
options[:minimum_value] && g.minimum_value = options[:minimum_value].to_i
|
51
51
|
options[:filename] && g.write(options[:filename])
|
@@ -57,7 +57,15 @@ module Geoptima
|
|
57
57
|
g = make_chart(chart_type, options)
|
58
58
|
g.data(legend, values)
|
59
59
|
g.minimum_value = 0
|
60
|
-
|
60
|
+
mod = 1
|
61
|
+
unless options[:side]
|
62
|
+
while(keys.length/mod > 10) do
|
63
|
+
mod+=1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
labels = {}
|
67
|
+
keys.each_with_index{|v,i| labels[i] = v.to_s if(i%mod == 0)}
|
68
|
+
g.labels = labels
|
61
69
|
options[:filename] && g.write(options[:filename])
|
62
70
|
g
|
63
71
|
end
|
@@ -69,7 +77,7 @@ module Geoptima
|
|
69
77
|
g.data(legend, values[legend])
|
70
78
|
end
|
71
79
|
g.minimum_value = 0
|
72
|
-
g.labels = keys.inject({}){|a,v| a[a.length] = v;a}
|
80
|
+
g.labels = keys.inject({}){|a,v| a[a.length] = v.to_s;a}
|
73
81
|
options[:filename] && g.write(options[:filename])
|
74
82
|
g
|
75
83
|
end
|
data/lib/geoptima/daterange.rb
CHANGED
@@ -19,10 +19,10 @@ end
|
|
19
19
|
module Geoptima
|
20
20
|
class DateRange
|
21
21
|
attr_reader :min, :max, :range
|
22
|
-
def initialize(min,max)
|
22
|
+
def initialize(min,max=nil)
|
23
23
|
@min = min
|
24
|
-
@max = max
|
25
|
-
@range = Range.new(min
|
24
|
+
@max = max || (min + 1.0)
|
25
|
+
@range = Range.new(@min,@max)
|
26
26
|
end
|
27
27
|
if ENV['RUBY_VERSION'] =~ /1\.8/
|
28
28
|
puts "Defining Range.include? to wrap for 1.8"
|
@@ -35,6 +35,57 @@ module Geoptima
|
|
35
35
|
(time >= min) && (time <= @max)
|
36
36
|
end
|
37
37
|
end
|
38
|
+
def to_s
|
39
|
+
@range.to_s
|
40
|
+
end
|
41
|
+
def self.from(spec)
|
42
|
+
if spec =~ /\.\./
|
43
|
+
puts "New time range spec: #{spec}" if($debug)
|
44
|
+
DateRanges.new(spec)
|
45
|
+
elsif spec.split(/\,/).length > 2
|
46
|
+
puts "New days array: #{spec}" if($debug)
|
47
|
+
DaysRange.new(spec)
|
48
|
+
else
|
49
|
+
puts "Classic time range: #{spec}" if($debug)
|
50
|
+
DateRange.new(*spec.split(/\,/).map{|t| DateTime.parse(t)})
|
51
|
+
end
|
52
|
+
end
|
53
|
+
def self.test
|
54
|
+
[
|
55
|
+
'2012-03-15,2012-03-16',
|
56
|
+
'2012-03-15..2012-03-16',
|
57
|
+
'2012-03-15..2012-03-20',
|
58
|
+
'2012-03-15..2012-03-16,2012-03-20..2012.03.21',
|
59
|
+
'2012-03-15..2012-03-16,2012-03-20',
|
60
|
+
'2012-03-15,2012-03-16,2012-03-20'
|
61
|
+
].each do |test|
|
62
|
+
puts "Testing: #{test}"
|
63
|
+
puts "\t#{Geoptima::DateRange.from(test)}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
class DateRanges
|
68
|
+
def initialize(spec)
|
69
|
+
@ranges = spec.split(/\,/).map do |range|
|
70
|
+
minmax = range.split(/\.\./).map{|t| DateTime.parse t}
|
71
|
+
DateRange.new(*minmax)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
def include?(time)
|
75
|
+
@ranges.each{|r| return true if(r.include?(time))}
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
def to_s
|
79
|
+
@ranges.join(',')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
class DaysRange < DateRanges
|
83
|
+
def initialize(spec)
|
84
|
+
@ranges = spec.split(/\,/).map do |day|
|
85
|
+
min = DateTime.parse(DateTime.parse(day).strftime("%Y-%m-%d"))
|
86
|
+
DateRange.new(min)
|
87
|
+
end
|
88
|
+
end
|
38
89
|
end
|
39
90
|
end
|
40
91
|
|
data/lib/geoptima/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geoptima
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Craig Taverner
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-03-
|
18
|
+
date: 2012-03-26 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: multi_json
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- bin/csv_chart
|
75
75
|
- bin/geoptima_file_time
|
76
76
|
- bin/csv_stats
|
77
|
+
- bin/csv_merge
|
77
78
|
- lib/geoptima/version.rb
|
78
79
|
- lib/geoptima/data.rb
|
79
80
|
- lib/geoptima/chart.rb
|
@@ -85,6 +86,7 @@ files:
|
|
85
86
|
- examples/csv_chart.rb
|
86
87
|
- examples/geoptima_file_time.rb
|
87
88
|
- examples/csv_stats.rb
|
89
|
+
- examples/csv_merge.rb
|
88
90
|
- examples/sample_geoptima.json
|
89
91
|
- README.rdoc
|
90
92
|
- CHANGELOG
|