fix trend calculation if days are missing by linear interpolation
This commit is contained in:
parent
79282317b1
commit
b1c5b729d1
76
hdiet.go
76
hdiet.go
|
@ -102,14 +102,21 @@ type yRange struct {
|
||||||
max float64
|
max float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func process(file string) ([]point, error) {
|
func readFile(file string) (map[time.Time]float64, time.Time, time.Time) {
|
||||||
|
optPanic := func(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic("process:" + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
weights, err := goutil.ReadFile(file)
|
weights, err := goutil.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, time.Time{}, time.Time{}
|
||||||
}
|
}
|
||||||
lines := strings.Split(weights, "\n")
|
lines := strings.Split(weights, "\n")
|
||||||
trend := 0.0
|
var firstDay time.Time
|
||||||
s := []point{}
|
firstDayIsSet := false
|
||||||
|
var lastDay time.Time
|
||||||
|
dateToWeight := make(map[time.Time]float64)
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
|
@ -117,18 +124,58 @@ func process(file string) ([]point, error) {
|
||||||
}
|
}
|
||||||
splitline := strings.Split(line, "\t")
|
splitline := strings.Split(line, "\t")
|
||||||
if len(splitline) < 2 {
|
if len(splitline) < 2 {
|
||||||
return nil, errors.New("process - invalid line in weight file: " + line)
|
optPanic(errors.New("invalid line in weight file " + file + ": " + line))
|
||||||
|
}
|
||||||
|
date, err := time.Parse(dateLayout, splitline[0])
|
||||||
|
optPanic(err)
|
||||||
|
weight, err := strconv.ParseFloat(splitline[1], 64)
|
||||||
|
optPanic(err)
|
||||||
|
if !firstDayIsSet {
|
||||||
|
firstDay = date
|
||||||
|
firstDayIsSet = true
|
||||||
|
}
|
||||||
|
lastDay = date
|
||||||
|
dateToWeight[date] = weight
|
||||||
|
}
|
||||||
|
return dateToWeight, firstDay, lastDay
|
||||||
|
}
|
||||||
|
|
||||||
|
func process(file string) []point {
|
||||||
|
dateToWeight, firstDay, lastDay := readFile(file)
|
||||||
|
if dateToWeight == nil {
|
||||||
|
return []point{}
|
||||||
|
}
|
||||||
|
trend := 0.0
|
||||||
|
points := []point{}
|
||||||
|
for d := firstDay; d.Before(lastDay) || d == lastDay; d = d.AddDate(0, 0, 1) {
|
||||||
|
var weight float64
|
||||||
|
if w, ok := dateToWeight[d]; ok {
|
||||||
|
weight = w
|
||||||
|
} else {
|
||||||
|
prevPoint := points[len(points)-1]
|
||||||
|
prevDay := prevPoint.date
|
||||||
|
prevWeight := prevPoint.weight
|
||||||
|
var nextWeight float64
|
||||||
|
var nextDay time.Time
|
||||||
|
for nextDay = d.AddDate(0, 0, 1); nextDay.Before(lastDay) || nextDay == lastDay; nextDay = nextDay.AddDate(0, 0, 1) {
|
||||||
|
if w, ok := dateToWeight[nextDay]; ok {
|
||||||
|
nextWeight = w
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
days := nextDay.Sub(prevDay).Hours() / 24
|
||||||
|
changePerDay := (nextWeight - prevWeight) / days
|
||||||
|
weight = prevWeight + changePerDay
|
||||||
}
|
}
|
||||||
date, _ := time.Parse(dateLayout, splitline[0])
|
|
||||||
weight, _ := strconv.ParseFloat(splitline[1], 64)
|
|
||||||
if trend == 0.0 {
|
if trend == 0.0 {
|
||||||
trend = weight
|
trend = weight
|
||||||
} else {
|
} else {
|
||||||
trend = math.Floor((trend+0.1*(weight-trend))*100) / 100
|
trend = math.Floor((trend+0.1*(weight-trend))*100) / 100
|
||||||
}
|
}
|
||||||
s = append(s, point{date, weight, trend})
|
points = append(points, point{d, weight, trend})
|
||||||
|
|
||||||
}
|
}
|
||||||
return s, nil
|
return points
|
||||||
}
|
}
|
||||||
|
|
||||||
type showFlags struct {
|
type showFlags struct {
|
||||||
|
@ -138,11 +185,8 @@ type showFlags struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func show(args showFlags) error {
|
func show(args showFlags) error {
|
||||||
points, err := process(logfile)
|
points := process(logfile)
|
||||||
if err != nil {
|
dietpoints := process(dietfile)
|
||||||
return err
|
|
||||||
}
|
|
||||||
dietpoints, _ := process(dietfile)
|
|
||||||
var to time.Time
|
var to time.Time
|
||||||
var from time.Time
|
var from time.Time
|
||||||
if args.to.date != nil {
|
if args.to.date != nil {
|
||||||
|
@ -170,7 +214,7 @@ func show(args showFlags) error {
|
||||||
YRange.min = math.Floor(YRange.min) - 1.0
|
YRange.min = math.Floor(YRange.min) - 1.0
|
||||||
YRange.max = math.Ceil(YRange.max) + 1.0
|
YRange.max = math.Ceil(YRange.max) + 1.0
|
||||||
graph := graph(xticks(xRange), s1, s2, s3, YRange)
|
graph := graph(xticks(xRange), s1, s2, s3, YRange)
|
||||||
err = writeGraphToFile(graph)
|
err := writeGraphToFile(graph)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -194,7 +238,7 @@ func showCommand() (goutil.CommandFlagsInit, goutil.CommandFunc) {
|
||||||
func writeDietFile(from time.Time, initial float64, goal float64, change float64) error {
|
func writeDietFile(from time.Time, initial float64, goal float64, change float64) error {
|
||||||
date := from
|
date := from
|
||||||
weight := initial
|
weight := initial
|
||||||
s := "#Date\tTarget\n"
|
s := ""
|
||||||
s += fmt.Sprintf("%s\t%.1f\n", date.Format(dateLayout), weight)
|
s += fmt.Sprintf("%s\t%.1f\n", date.Format(dateLayout), weight)
|
||||||
for weight > goal {
|
for weight > goal {
|
||||||
date = date.AddDate(0, 0, 1)
|
date = date.AddDate(0, 0, 1)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user