nom 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.
- 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
|