analytica 0.0.6 → 0.0.7
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/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
|