ts 1.0.0 → 1.0.1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/lib/ts.rb +68 -9
- data/test/test_ts.rb +15 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22f6b0505160ceb7fe7c019d4c101aacbd35039c
|
4
|
+
data.tar.gz: 76a7f282ebfd76d98e430708d0a78ada36b4ad59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cadb0aea96d5b5614455f618ca3759f6446e0dae53ece7259d851a2fbe7aa0bbef94675d2eb7ffaea53413f3468b2388aef59acae307c87c3beb72e3831dc9f0
|
7
|
+
data.tar.gz: d77c9c1418a81bf0415a33b9cc0d664c8cfa32137065ee2446f3d2cccbb28f105a24d1b3647f4c4502ed4a15b3fe0182eadae22dadef8575b7fc87060b5be3d6
|
data/.gitignore
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/lib/ts.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
#
|
7
7
|
class TS
|
8
8
|
|
9
|
-
Version = "1.0.
|
9
|
+
Version = "1.0.1"
|
10
10
|
|
11
11
|
include Enumerable
|
12
12
|
|
@@ -36,6 +36,33 @@ class TS
|
|
36
36
|
TS.new(@data.map { |v| yield *v })
|
37
37
|
end
|
38
38
|
|
39
|
+
# run a simple moving average, and return a new TS instance
|
40
|
+
# +size+ the size of the window
|
41
|
+
def sma size
|
42
|
+
buf = []
|
43
|
+
sum = 0
|
44
|
+
|
45
|
+
map { |t, v|
|
46
|
+
buf << v
|
47
|
+
sum += v
|
48
|
+
|
49
|
+
if buf.size > size
|
50
|
+
sum -= buf.shift
|
51
|
+
end
|
52
|
+
|
53
|
+
[t, sum / buf.size]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
# generate some statistics from the values of the series
|
58
|
+
# returns {
|
59
|
+
# :num => ...,
|
60
|
+
# :min => ...,
|
61
|
+
# :max => ...,
|
62
|
+
# :sum => ...,
|
63
|
+
# :mean => ...,
|
64
|
+
# :stddev => ...,
|
65
|
+
# }
|
39
66
|
def stats
|
40
67
|
return @stats if @stats
|
41
68
|
|
@@ -47,8 +74,8 @@ class TS
|
|
47
74
|
each { |time, val|
|
48
75
|
min = val if val < min
|
49
76
|
max = val if val > max
|
50
|
-
sum
|
51
|
-
sum2
|
77
|
+
sum += val
|
78
|
+
sum2 += val ** 2
|
52
79
|
}
|
53
80
|
|
54
81
|
@stats = {
|
@@ -104,10 +131,14 @@ class TS
|
|
104
131
|
TS.new(@data[0..idx-1])
|
105
132
|
end
|
106
133
|
|
134
|
+
# fetch the value at a given index
|
135
|
+
# +idx+ the array index of the data
|
107
136
|
def value_at idx
|
108
137
|
@data[idx].last
|
109
138
|
end
|
110
139
|
|
140
|
+
# fetch the time at a given index
|
141
|
+
# +idx+ the array index of the data
|
111
142
|
def time_at idx
|
112
143
|
@data[idx].first
|
113
144
|
end
|
@@ -118,15 +149,24 @@ class TS
|
|
118
149
|
bsearch time, 0, size - 1
|
119
150
|
end
|
120
151
|
|
152
|
+
# get the timestamp series
|
121
153
|
def timestamps
|
122
154
|
@data.transpose.first
|
123
155
|
end
|
124
156
|
|
157
|
+
# get the value series
|
125
158
|
def values
|
126
159
|
@data.transpose.last
|
127
160
|
end
|
128
161
|
|
129
|
-
# Run a regression
|
162
|
+
# Run a regression on the series. Useful for weak projections
|
163
|
+
# and testing if your project is accurate (r2 =~ 1)
|
164
|
+
#
|
165
|
+
# returns {
|
166
|
+
# :r2 => ...,
|
167
|
+
# :slope => ...,
|
168
|
+
# :y_intercept => ...
|
169
|
+
# }
|
130
170
|
def regression
|
131
171
|
return @regression if @regression
|
132
172
|
|
@@ -135,22 +175,40 @@ class TS
|
|
135
175
|
t_mean = times.reduce(:+) / size
|
136
176
|
v_mean = values.reduce(:+) / size
|
137
177
|
|
138
|
-
slope = (0..size - 1).inject(0) { |sum, n|
|
178
|
+
slope = (0..size - 1).inject(0.0) { |sum, n|
|
139
179
|
sum + (times[n] - t_mean) * (values[n] - v_mean)
|
140
|
-
} / times.inject { |sum, n|
|
180
|
+
} / times.inject(0.0) { |sum, n|
|
141
181
|
sum + (n - t_mean) ** 2
|
142
182
|
}
|
143
183
|
|
144
|
-
# now r2
|
145
184
|
r = slope * (stddev(times) / stddev(values))
|
146
185
|
|
147
186
|
@regression = {
|
148
|
-
:r2 => r
|
187
|
+
:r2 => r * r,
|
149
188
|
:slope => slope,
|
150
189
|
:y_intercept => v_mean - (slope * t_mean)
|
151
190
|
}
|
152
191
|
end
|
153
192
|
|
193
|
+
# Project the value at a given time using the regresion
|
194
|
+
#
|
195
|
+
# y = mx + b
|
196
|
+
#
|
197
|
+
# +time+ the timestamp of the value you wish to predict
|
198
|
+
def projected_value time
|
199
|
+
regression[:slope] * time + regression[:y_intercept]
|
200
|
+
end
|
201
|
+
|
202
|
+
# Estimate the time for a given value. Assumes a fairly linear
|
203
|
+
# model.
|
204
|
+
#
|
205
|
+
# x = (y - b) / m
|
206
|
+
#
|
207
|
+
# +value+ the timestamp of the value you wish to predict
|
208
|
+
def projected_time value
|
209
|
+
(value - regression[:y_intercept]) / regression[:slope]
|
210
|
+
end
|
211
|
+
|
154
212
|
private
|
155
213
|
|
156
214
|
# Find the nearest index for a given time (fuzzy search)
|
@@ -169,6 +227,7 @@ class TS
|
|
169
227
|
end
|
170
228
|
end
|
171
229
|
|
230
|
+
# calculate the std deviation of the 1d data set
|
172
231
|
def stddev data
|
173
232
|
sum = 0.0
|
174
233
|
sum2 = 0.0
|
@@ -176,7 +235,7 @@ class TS
|
|
176
235
|
sum += v
|
177
236
|
sum2 += v ** 2
|
178
237
|
}
|
179
|
-
Math.sqrt(sum2 / data.size - (sum / data.size) ** 2)
|
238
|
+
Math.sqrt((sum2 / data.size) - ((sum / data.size) ** 2))
|
180
239
|
end
|
181
240
|
|
182
241
|
end
|
data/test/test_ts.rb
CHANGED
@@ -50,11 +50,25 @@ class TSTest < Test::Unit::TestCase
|
|
50
50
|
def test_regression
|
51
51
|
assert_in_delta 0.001, @ts.regression[:slope], 0.001
|
52
52
|
assert_in_delta 1.0, @ts.regression[:r2], 0.01
|
53
|
-
assert_in_delta
|
53
|
+
assert_in_delta 0.0, @ts.regression[:y_intercept], 0.1
|
54
54
|
end
|
55
55
|
|
56
56
|
def test_collect
|
57
57
|
assert @ts.map { |t, v| [t, v * 2] }.stats[:min] == 2
|
58
58
|
end
|
59
59
|
|
60
|
+
def test_sma
|
61
|
+
assert_equal [2000, 2], @ts.data[1]
|
62
|
+
assert_equal [2000, 1.5], @ts.sma(7).data[1]
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_projection
|
66
|
+
assert_equal 5000, @ts.projected_value(5000000)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_projection_time
|
70
|
+
assert_equal 5000000, @ts.projected_time(5000)
|
71
|
+
assert_equal -1000, @ts.projected_time(-1)
|
72
|
+
end
|
73
|
+
|
60
74
|
end
|