analytica 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +7 -0
- data/Manifest.txt +2 -2
- data/lib/analytica.rb +15 -5
- data/lib/computation.rb +209 -0
- data/lib/visualization.rb +68 -0
- metadata +6 -6
- data/lib/analytica_comp.rb +0 -95
- data/lib/analytica_viz.rb +0 -67
data/History.txt
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
=== 0.0.7 / 2010-08-09
|
2
|
+
* 3 enhancements
|
3
|
+
|
4
|
+
* Cleaned up structure of analytica package
|
5
|
+
* Analytica::DataSet now has a default constructor (empty DataSet) + type-safety on 'concat' and '+'
|
6
|
+
* Implemented moving_average, lma, sma and ema + approprate sub-computations
|
7
|
+
|
1
8
|
=== 0.0.5 / 2010-08-03
|
2
9
|
|
3
10
|
* 3 enhancements
|
data/Manifest.txt
CHANGED
data/lib/analytica.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'typestrict'
|
2
2
|
|
3
|
-
require File.join(File.dirname(__FILE__), '
|
4
|
-
require File.join(File.dirname(__FILE__), '
|
3
|
+
require File.join(File.dirname(__FILE__), 'computation')
|
4
|
+
require File.join(File.dirname(__FILE__), 'visualization')
|
5
5
|
|
6
6
|
module Analytica
|
7
|
-
VERSION = '0.0.
|
7
|
+
VERSION = '0.0.7'
|
8
8
|
|
9
9
|
include Strict
|
10
10
|
|
11
11
|
class DataSet < Array
|
12
|
-
include Computation
|
13
|
-
include Visualization
|
12
|
+
include Analytica::Computation
|
13
|
+
include Analytica::Visualization
|
14
14
|
|
15
15
|
def initialize(datapoints=[])
|
16
16
|
enforce!(:numeric_array, datapoints)
|
@@ -22,5 +22,15 @@ module Analytica
|
|
22
22
|
enforce!(:numeric, object)
|
23
23
|
super object
|
24
24
|
end
|
25
|
+
|
26
|
+
def concat(other)
|
27
|
+
enforce!(:numeric_array, other)
|
28
|
+
super other
|
29
|
+
end
|
30
|
+
|
31
|
+
def +(other)
|
32
|
+
enforce!(:numeric_array, other)
|
33
|
+
super other
|
34
|
+
end
|
25
35
|
end
|
26
36
|
end
|
data/lib/computation.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'typestrict'
|
2
|
+
|
3
|
+
module Analytica
|
4
|
+
module Computation
|
5
|
+
include Strict
|
6
|
+
|
7
|
+
class InvalidInputException < Exception
|
8
|
+
def initialize(comp, size, params, constraints)
|
9
|
+
enforce!([:average_filter,
|
10
|
+
:moving_average], comp)
|
11
|
+
enforce_primitive!(Hash, params)
|
12
|
+
enforce!(:string_array, constraints)
|
13
|
+
|
14
|
+
@msg = "Unable to calculate #{comp.inspect} on DataSet size #{size} with input #{params.inspect}"
|
15
|
+
@msg += "\nConstraints:\n"
|
16
|
+
constraints.each do |c|
|
17
|
+
@msg += c+"\n"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
@msg
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def sum
|
27
|
+
sum = inject { |sum, x| sum + x }
|
28
|
+
sum ? sum : 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def mean
|
32
|
+
sum.to_f / size
|
33
|
+
end
|
34
|
+
|
35
|
+
def linear_mean(params)
|
36
|
+
enforce_map!({
|
37
|
+
:bias => [:last, :first],
|
38
|
+
:samples => :natural_number}, params)
|
39
|
+
|
40
|
+
data = self
|
41
|
+
data.reverse! if params[:bias] == :last
|
42
|
+
|
43
|
+
params[:samples] = [params[:samples], data.size].min
|
44
|
+
|
45
|
+
n = params[:samples] + 1
|
46
|
+
numerator = 0.0
|
47
|
+
denominator = 0.0
|
48
|
+
|
49
|
+
data.each do |sample|
|
50
|
+
n -= 1
|
51
|
+
n = n > 0 ? n : 0
|
52
|
+
|
53
|
+
numerator += n*sample
|
54
|
+
denominator += n
|
55
|
+
end
|
56
|
+
numerator / denominator
|
57
|
+
end
|
58
|
+
|
59
|
+
def exponential_mean(params)
|
60
|
+
enforce_map!({
|
61
|
+
:bias => [:last, :first],
|
62
|
+
:alpha => :numeric,
|
63
|
+
:samples => :integer}, params)
|
64
|
+
|
65
|
+
data = self
|
66
|
+
data.reverse! if params[:bias] == :last
|
67
|
+
|
68
|
+
params[:samples] = [params[:samples], data.size].min
|
69
|
+
|
70
|
+
ema = 0
|
71
|
+
counter = 0
|
72
|
+
alpha = params[:alpha]
|
73
|
+
|
74
|
+
data.each do |sample|
|
75
|
+
|
76
|
+
if counter > params[:samples]
|
77
|
+
break
|
78
|
+
end
|
79
|
+
|
80
|
+
if counter == 0
|
81
|
+
ema += sample
|
82
|
+
else
|
83
|
+
ema += sample * (1-alpha)**counter
|
84
|
+
end
|
85
|
+
|
86
|
+
counter += 1
|
87
|
+
end
|
88
|
+
alpha*ema
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def average_filter(params)
|
93
|
+
enforce_map!({
|
94
|
+
:decay => [:simple, :linear, :exponential],
|
95
|
+
:offset => :natural_number, # offset from latest data point
|
96
|
+
:samples => :natural_number}, params)
|
97
|
+
|
98
|
+
if !(params[:offset] <= size)
|
99
|
+
c = InvalidInputException.new(:simple_average,
|
100
|
+
size,
|
101
|
+
params,
|
102
|
+
[":offset <= :size"])
|
103
|
+
raise c, c.inspect, caller
|
104
|
+
end
|
105
|
+
|
106
|
+
if !(params[:offset] >= params[:samples])
|
107
|
+
c = InvalidInputException.new(:simple_average,
|
108
|
+
size,
|
109
|
+
params,
|
110
|
+
[":offset >= :samples"])
|
111
|
+
raise c, c.inspect, caller
|
112
|
+
end
|
113
|
+
|
114
|
+
i = size - params[:offset]
|
115
|
+
j = i + params[:samples]-1
|
116
|
+
|
117
|
+
d = DataSet.new(self[i..j])
|
118
|
+
|
119
|
+
case params[:decay]
|
120
|
+
when :simple
|
121
|
+
d.mean
|
122
|
+
when :linear
|
123
|
+
d.linear_mean(:bias => :last, :samples => d.size)
|
124
|
+
when :exponential
|
125
|
+
enforce_exists!(:alpha, params)
|
126
|
+
enforce!(:numeric, params[:alpha])
|
127
|
+
d.exponential_mean(:bias => :first, :samples => d.size, :alpha => params[:alpha])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
alias_method(:avg, :average_filter)
|
132
|
+
|
133
|
+
def moving_average(params)
|
134
|
+
enforce_map!({
|
135
|
+
:decay => [:simple, :linear, :exponential],
|
136
|
+
:samples => :natural_number}, params)
|
137
|
+
|
138
|
+
if !(size >= (2*params[:samples]-1))
|
139
|
+
c = InvalidInputException.new(:moving_average,
|
140
|
+
size,
|
141
|
+
params,
|
142
|
+
["size >= (2*:samples-1)"])
|
143
|
+
raise c, c.inspect, caller
|
144
|
+
end
|
145
|
+
|
146
|
+
d = DataSet.new
|
147
|
+
(1..params[:samples]).each do |offset|
|
148
|
+
d << average_filter(:decay => params[:decay], :alpha => params[:alpha], :offset => offset+params[:samples]-1, :samples => params[:samples])
|
149
|
+
end
|
150
|
+
d.reverse
|
151
|
+
end
|
152
|
+
|
153
|
+
def simple_moving_average(params)
|
154
|
+
enforce_map!({
|
155
|
+
:samples => :natural_number}, params)
|
156
|
+
moving_average(:decay => :simple, :samples => params[:samples])
|
157
|
+
end
|
158
|
+
|
159
|
+
alias_method :sma, :simple_moving_average
|
160
|
+
|
161
|
+
def linear_moving_average(params)
|
162
|
+
enforce_map!({
|
163
|
+
:samples => :natural_number}, params)
|
164
|
+
moving_average(:decay => :linear, :samples => params[:samples])
|
165
|
+
end
|
166
|
+
|
167
|
+
alias_method :lma, :linear_moving_average
|
168
|
+
|
169
|
+
def exponential_moving_average(params)
|
170
|
+
enforce_map!({
|
171
|
+
:samples => :integer,
|
172
|
+
:alpha=> :float}, params)
|
173
|
+
|
174
|
+
moving_average(:decay => :exponential, :samples => params[:samples], :alpha => params[:alpha])
|
175
|
+
end
|
176
|
+
|
177
|
+
alias_method :ema, :exponential_moving_average
|
178
|
+
|
179
|
+
def piecewise_derivative(n=1)
|
180
|
+
enforce!(:natural_number, n)
|
181
|
+
|
182
|
+
d = self
|
183
|
+
n.times do
|
184
|
+
d = d.inject([]) do |result, item|
|
185
|
+
if result.size == 0
|
186
|
+
result << item
|
187
|
+
else
|
188
|
+
d_y = (item - result.last).to_f
|
189
|
+
d_x = 1.0 #account for d_x eventually
|
190
|
+
deriv = d_y/d_x
|
191
|
+
result.pop
|
192
|
+
result << deriv
|
193
|
+
result << item unless (result.size) == (d.size-1)
|
194
|
+
result
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
DataSet.new(d)
|
199
|
+
end
|
200
|
+
|
201
|
+
alias_method :dydx, :piecewise_derivative
|
202
|
+
|
203
|
+
def savitzky_golay(n=1)
|
204
|
+
enforce!(:natural_number, n)
|
205
|
+
|
206
|
+
raise "savitzy_golay filter not yet implemented!"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'typestrict'
|
2
|
+
require 'gchart'
|
3
|
+
|
4
|
+
class GChart::Base
|
5
|
+
def to_html
|
6
|
+
"<img src=\"#{self.to_url}\" />"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Analytica
|
11
|
+
module Visualization
|
12
|
+
include Strict
|
13
|
+
|
14
|
+
def set_labels(labels)
|
15
|
+
enforce!(:string_array, labels)
|
16
|
+
|
17
|
+
@labels = labels
|
18
|
+
end
|
19
|
+
|
20
|
+
def datamax
|
21
|
+
(max > 0) ? max : 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_linegraph(params)
|
25
|
+
enforce_map!({
|
26
|
+
:width => :natural_number,
|
27
|
+
:height => :natural_number,
|
28
|
+
:background_color => :hex_color,
|
29
|
+
:color => :hex_color}, params)
|
30
|
+
|
31
|
+
GChart.line do |g|
|
32
|
+
g.data = self
|
33
|
+
g.extras = {
|
34
|
+
'chm' => 'N*cUSD0*,000000,0,-1,11',
|
35
|
+
'chbh' => '18,38',
|
36
|
+
'chds' => "0,#{datamax}"
|
37
|
+
}
|
38
|
+
g.size = "#{(params[:width]).to_i}x#{(params[:height]).to_i}"
|
39
|
+
g.entire_background = params[:background_color].to_s
|
40
|
+
g.colors = params[:color].to_s
|
41
|
+
g.axis(:bottom) {|a| a.labels = @labels}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_bargraph(params)
|
46
|
+
enforce_map!({
|
47
|
+
:width => :natural_number,
|
48
|
+
:height => :natural_number,
|
49
|
+
:orientation => [:vertical, :horizontal],
|
50
|
+
:background_color => :hex_color,
|
51
|
+
:color => :hex_color}, params)
|
52
|
+
|
53
|
+
GChart.bar do |g|
|
54
|
+
g.data = self
|
55
|
+
g.extras = {
|
56
|
+
'chm' => 'N*cUSD0*,000000,0,-1,11',
|
57
|
+
'chbh' => '45,30',
|
58
|
+
'chds' => "0,#{datamax}"
|
59
|
+
}
|
60
|
+
g.size = "#{(params[:width]).to_i}x#{(params[:height]).to_i}"
|
61
|
+
g.entire_background = params[:background_color].to_s
|
62
|
+
g.colors = params[:color].to_s
|
63
|
+
g.orientation = params[:orientation]
|
64
|
+
g.axis(:bottom) {|a| a.labels = @labels}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: analytica
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 7
|
10
|
+
version: 0.0.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Raeez Lorgat
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-08-
|
18
|
+
date: 2010-08-09 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -81,8 +81,8 @@ files:
|
|
81
81
|
- Rakefile
|
82
82
|
- bin/analytica
|
83
83
|
- lib/analytica.rb
|
84
|
-
- lib/
|
85
|
-
- lib/
|
84
|
+
- lib/computation.rb
|
85
|
+
- lib/visualization.rb
|
86
86
|
- test/test_analytica.rb
|
87
87
|
has_rdoc: true
|
88
88
|
homepage: http://www.raeez.com/anlaytica
|
data/lib/analytica_comp.rb
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
require 'typestrict'
|
2
|
-
|
3
|
-
module Computation
|
4
|
-
include Strict
|
5
|
-
|
6
|
-
def sum
|
7
|
-
sum = inject { |sum, x| sum + x }
|
8
|
-
sum ? sum : 0
|
9
|
-
end
|
10
|
-
|
11
|
-
def mean
|
12
|
-
sum.to_f / size
|
13
|
-
end
|
14
|
-
|
15
|
-
def linear_moving_average(params)
|
16
|
-
enforce_map!({
|
17
|
-
:bias => [:last, :first],
|
18
|
-
:samples => :integer}, params)
|
19
|
-
|
20
|
-
data = []
|
21
|
-
case params[:bias]
|
22
|
-
when :last
|
23
|
-
data = self.reverse
|
24
|
-
when :first
|
25
|
-
data = self
|
26
|
-
else
|
27
|
-
raise "Bias not legally set!"
|
28
|
-
end
|
29
|
-
|
30
|
-
params[:samples] = [params[:samples], data.size]
|
31
|
-
|
32
|
-
n = params[:samples] + 1
|
33
|
-
numerator = 0.0
|
34
|
-
denominator = 0.0
|
35
|
-
|
36
|
-
data.each do |sample|
|
37
|
-
n -= 1
|
38
|
-
n = n > 0 ? n : 0
|
39
|
-
|
40
|
-
numerator += n*sample
|
41
|
-
denominator += n
|
42
|
-
end
|
43
|
-
numerator / denominator
|
44
|
-
end
|
45
|
-
|
46
|
-
alias_method :lma, :linear_moving_average
|
47
|
-
|
48
|
-
def exponential_moving_average(params)
|
49
|
-
enforce_map!({
|
50
|
-
:decay => [:exponential, :linear],
|
51
|
-
:decay_bias => [:latest, :oldest],
|
52
|
-
:decay_coefficent => :float}, params)
|
53
|
-
|
54
|
-
raise "ema not yet implemented!"
|
55
|
-
|
56
|
-
if params[:decay_bias] == :latest
|
57
|
-
data = self
|
58
|
-
else
|
59
|
-
data = self.reverse
|
60
|
-
end
|
61
|
-
0.0
|
62
|
-
end
|
63
|
-
|
64
|
-
alias_method :ema, :exponential_moving_average
|
65
|
-
|
66
|
-
def piecewise_derivative(n=1)
|
67
|
-
enforce!(:natural_number, n)
|
68
|
-
|
69
|
-
d = self
|
70
|
-
n.times do
|
71
|
-
d = d.inject([]) do |result, item|
|
72
|
-
if result.size == 0
|
73
|
-
result << item
|
74
|
-
else
|
75
|
-
d_y = (item - result.last).to_f
|
76
|
-
d_x = 1.0 #account for d_x eventually
|
77
|
-
deriv = d_y/d_x
|
78
|
-
result.pop
|
79
|
-
result << deriv
|
80
|
-
result << item unless (result.size) == (d.size-1)
|
81
|
-
result
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
DataSet.new(d)
|
86
|
-
end
|
87
|
-
|
88
|
-
alias_method :dydx, :piecewise_derivative
|
89
|
-
|
90
|
-
def savitzky_golay(n=1)
|
91
|
-
enforce!(:natural_number, n)
|
92
|
-
|
93
|
-
raise "savitzy_golay filter not yet implemented!"
|
94
|
-
end
|
95
|
-
end
|
data/lib/analytica_viz.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'typestrict'
|
2
|
-
|
3
|
-
require 'gchart'
|
4
|
-
|
5
|
-
class GChart::Base
|
6
|
-
def to_html
|
7
|
-
"<img src=\"#{self.to_url}\" />"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
module Visualization
|
12
|
-
include Strict
|
13
|
-
|
14
|
-
def set_labels(labels)
|
15
|
-
enforce!(:string_array, labels)
|
16
|
-
|
17
|
-
@labels = labels
|
18
|
-
end
|
19
|
-
|
20
|
-
def datamax
|
21
|
-
(max > 0) ? max : 1
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_linegraph(params)
|
25
|
-
enforce_map!({
|
26
|
-
:width => :natural_number,
|
27
|
-
:height => :natural_number,
|
28
|
-
:background_color => :hex_color,
|
29
|
-
:color => :hex_color}, params)
|
30
|
-
|
31
|
-
GChart.line do |g|
|
32
|
-
g.data = self
|
33
|
-
g.extras = {
|
34
|
-
'chm' => 'N*cUSD0*,000000,0,-1,11',
|
35
|
-
'chbh' => '18,38',
|
36
|
-
'chds' => "0,#{datamax}"
|
37
|
-
}
|
38
|
-
g.size = "#{(params[:width]).to_i}x#{(params[:height]).to_i}"
|
39
|
-
g.entire_background = params[:background_color].to_s
|
40
|
-
g.colors = params[:color].to_s
|
41
|
-
g.axis(:bottom) {|a| a.labels = @labels}
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def to_bargraph(params)
|
46
|
-
enforce_map!({
|
47
|
-
:width => :natural_number,
|
48
|
-
:height => :natural_number,
|
49
|
-
:orientation => [:vertical, :horizontal],
|
50
|
-
:background_color => :hex_color,
|
51
|
-
:color => :hex_color}, params)
|
52
|
-
|
53
|
-
GChart.bar do |g|
|
54
|
-
g.data = self
|
55
|
-
g.extras = {
|
56
|
-
'chm' => 'N*cUSD0*,000000,0,-1,11',
|
57
|
-
'chbh' => '45,30',
|
58
|
-
'chds' => "0,#{datamax}"
|
59
|
-
}
|
60
|
-
g.size = "#{(params[:width]).to_i}x#{(params[:height]).to_i}"
|
61
|
-
g.entire_background = params[:background_color].to_s
|
62
|
-
g.colors = params[:color].to_s
|
63
|
-
g.orientation = params[:orientation]
|
64
|
-
g.axis(:bottom) {|a| a.labels = @labels}
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|