nom 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +41 -38
- data/bin/nom +2 -1
- data/lib/nom/config.rb +50 -58
- data/lib/nom/food_entry.rb +18 -16
- data/lib/nom/helpers.rb +42 -0
- data/lib/nom/nom.rb +326 -321
- data/lib/nom/weight_database.rb +91 -89
- metadata +2 -1
data/lib/nom/weight_database.rb
CHANGED
@@ -1,120 +1,122 @@
|
|
1
1
|
require "yaml"
|
2
2
|
require "fileutils"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
4
|
+
module Nom
|
5
|
+
class WeightDatabase
|
6
|
+
def initialize(file)
|
7
|
+
@interpolated = {}
|
8
|
+
@weights = {}
|
9
|
+
@moving_averages = {}
|
10
|
+
|
11
|
+
FileUtils.touch(file)
|
12
|
+
IO.readlines(file).each do |line|
|
13
|
+
date, weight = line.split(" ", 2)
|
14
|
+
date = Date.parse(date)
|
15
|
+
@weights[date] = weight.to_f
|
16
|
+
@interpolated[date] = false
|
17
|
+
end
|
16
18
|
end
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def interpolate_gaps!
|
21
|
+
@weights.keys.each_cons(2) do |a, b|
|
22
|
+
(a+1).upto(b-1) do |d|
|
23
|
+
@weights[d] = @weights[a] + (@weights[a]-@weights[b])/(a-b)*(d-a)
|
24
|
+
@interpolated[d] = true
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
26
|
-
end
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
def precompute_moving_average!(alpha, beta, goal, rate)
|
30
|
+
trend = dampened_rate(@weights[first], goal, rate)/7.0
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
@moving_averages[first] = at(first)
|
33
|
+
(first+1).upto(last).each do |d|
|
34
|
+
@moving_averages[d] = alpha*at(d) + (1-alpha)*(@moving_averages[d-1]+trend)
|
35
|
+
trend = beta*(@moving_averages[d]-@moving_averages[d-1]) + (1-beta)*trend
|
36
|
+
end
|
35
37
|
end
|
36
|
-
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
def predict_weights!(rate, goal, tail)
|
40
|
+
d = (last)
|
41
|
+
loop do
|
42
|
+
if (@weights[d] - goal).abs < 0.1 and d > Date.today
|
43
|
+
tail -= 1
|
44
|
+
end
|
45
|
+
if tail == 0
|
46
|
+
break
|
47
|
+
end
|
48
|
+
|
49
|
+
d += 1
|
50
|
+
prev_weight = @moving_averages[d-1] || @weights[d-1]
|
51
|
+
@weights[d] = prev_weight+dampened_rate(prev_weight, goal, rate)/7.0
|
52
|
+
@interpolated[d] = true
|
46
53
|
end
|
47
|
-
|
48
|
-
d += 1
|
49
|
-
prev_weight = @moving_averages[d-1] || @weights[d-1]
|
50
|
-
@weights[d] = prev_weight+dampened_rate(prev_weight, goal, rate)/7.0
|
51
|
-
@interpolated[d] = true
|
52
54
|
end
|
53
|
-
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
def dampened_rate weight, goal, rate
|
57
|
+
r = (goal-weight).to_f
|
58
|
+
if r.abs > 1
|
59
|
+
r/r.abs*rate
|
60
|
+
else
|
61
|
+
r*rate
|
62
|
+
end
|
61
63
|
end
|
62
|
-
end
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
def real? date
|
66
|
+
@weights[date] and not @interpolated[date]
|
67
|
+
end
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
def at date
|
70
|
+
@weights[date]
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
|
73
|
+
def moving_average_at date
|
74
|
+
@moving_averages[date]
|
75
|
+
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
def rate_at date, goal, rate
|
78
|
+
if date > last_real
|
79
|
+
dampened_rate(@weights[date], goal, rate)
|
80
|
+
else
|
81
|
+
dampened_rate(@moving_averages[date], goal, rate)
|
82
|
+
end
|
81
83
|
end
|
82
|
-
end
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
|
85
|
+
def empty?
|
86
|
+
@weights.empty?
|
87
|
+
end
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
def first
|
90
|
+
@weights.keys.min
|
91
|
+
end
|
91
92
|
|
92
|
-
|
93
|
-
|
94
|
-
|
93
|
+
def last
|
94
|
+
@weights.keys.max
|
95
|
+
end
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
97
|
+
def last_real
|
98
|
+
@interpolated.select{|d, i| not i}.keys.max
|
99
|
+
end
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
101
|
+
def truncate date
|
102
|
+
@weights.delete_if{|d, w| d < date}
|
103
|
+
end
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
def min
|
106
|
+
@weights.values.min
|
107
|
+
end
|
107
108
|
|
108
|
-
|
109
|
-
|
110
|
-
|
109
|
+
def max
|
110
|
+
@weights.values.max
|
111
|
+
end
|
111
112
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
def find_gap days
|
114
|
+
gap = @weights.keys.reverse.each_cons(2).find{|a,b| a-b > days}
|
115
|
+
if gap
|
116
|
+
gap.reverse
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
118
120
|
end
|
119
121
|
end
|
120
122
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Morr
|
@@ -39,6 +39,7 @@ files:
|
|
39
39
|
- bin/nom
|
40
40
|
- lib/nom/config.rb
|
41
41
|
- lib/nom/food_entry.rb
|
42
|
+
- lib/nom/helpers.rb
|
42
43
|
- lib/nom/nom.plt.erb
|
43
44
|
- lib/nom/nom.rb
|
44
45
|
- lib/nom/weight_database.rb
|