Skip to content

Commit

Permalink
add: downsample.go
Browse files Browse the repository at this point in the history
  • Loading branch information
n4mine committed Jul 25, 2019
0 parents commit e9f4b21
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## downsample
94 changes: 94 additions & 0 deletions downsample.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package downsample

import (
"math"
"sort"
)

func (ps Points) Downsample(newStep int) Points {
if len(ps) == 0 {
return []Point{}
}

if !sort.IsSorted(ps) {
sort.Sort(ps)
}

if len(ps) == 1 {
return []Point{
Point{ps[0].Timestamp, ps[0].Value},
}
}

start := ps[0].Timestamp
end := ps[len(ps)-1].Timestamp

// 不应该出现这种case
if start > end {
return []Point{}
}

// 不应该出现这种case
if start == end {
return []Point{
Point{ps[0].Timestamp, ps[0].Value},
}
}

res := make([]Point, 0)
for t1 := start; t1 <= end; t1 += int64(newStep) {
r := ps.avg(t1, t1+int64(newStep), end)
res = append(res, Point{t1, r})
}

return res
}

func (ps Points) avg(t1, t2 int64, max int64) float64 {
l := len(ps)
var (
count int = 0
sum float64
)
idx := sort.Search(l, func(i int) bool { return ps[i].Timestamp >= t1 })
if idx == l {
return math.NaN()
}

if t2 > max {
for _, p := range ps {
if p.Timestamp >= ps[idx].Timestamp {
if v := float64(p.Value); !math.IsNaN(v) {
sum += v
count++
}
}
}
} else {
for _, p := range ps {
if p.Timestamp >= ps[idx].Timestamp && p.Timestamp < t2 {
if v := float64(p.Value); !math.IsNaN(v) {
sum += v
count++
}
}
}
}

if count == 0 {
return math.NaN()
}

return sum / float64(count)
}

type Points []Point

type Point struct {
Timestamp int64
Value float64
}

func (ps Points) Less(i, j int) bool { return ps[i].Timestamp < ps[j].Timestamp }
func (ps Points) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
func (ps Points) Len() int { return len(ps) }
137 changes: 137 additions & 0 deletions downsample_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package downsample

import (
"encoding/json"
"math"
"reflect"
"testing"
)

func Test_Downsample(t *testing.T) {
type args struct {
step int
}
tests := []struct {
name string
rps Points
args args
want Points
}{
{"only 1 point",
[]Point{
Point{0, 0.1},
},
args{15},
[]Point{
Point{0, 0.1},
},
},
{"not enough points",
[]Point{
Point{0, 0.5},
Point{10, 0.3},
},
args{15},
[]Point{
Point{0, 0.4},
},
},
{"boundary",
[]Point{
Point{0, 0.5},
Point{15, 0.3},
},
args{15},
[]Point{
Point{0, 0.5},
Point{15, 0.3},
},
},
{"normal",
[]Point{
Point{10, 0.5},
Point{20, 0.3},
Point{30, 0.4},
},
args{15},
[]Point{
Point{10, 0.4},
Point{25, 0.4},
},
},
{"NaN#1",
[]Point{
Point{10, 0.5},
Point{20, math.NaN()},
Point{30, 0.4},
},
args{20},
[]Point{
Point{10, 0.5},
Point{30, 0.4},
},
},
{"NaN#2",
[]Point{
Point{10, 0.5},
Point{20, math.NaN()},
Point{30, math.NaN()},
Point{40, 0.6},
Point{50, 0.8},
},
args{20},
[]Point{
Point{10, 0.5},
Point{30, 0.6},
Point{50, 0.8},
},
},
{"should not be happen",
[]Point{
Point{10, 0.5},
Point{20, 0.3},
Point{30, 0.4},
},
args{5},
[]Point{
Point{10, 0.5},
Point{15, math.NaN()},
Point{20, 0.3},
Point{25, math.NaN()},
Point{30, 0.4},
},
},
{"except#1",
[]Point{
Point{10, 0.5},
Point{10, 0.3},
},
args{30},
[]Point{
Point{10, 0.5},
},
},
{"unsorted",
[]Point{
Point{10, 0.5},
Point{0, 0.3},
},
args{20},
[]Point{
Point{0, 0.4},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.rps.Downsample(tt.args.step)
// math.NaN() != math.NaN()
// so reflect.DeepEqual is not work when value is math.NaN()
gotBytes, _ := json.Marshal(got)
wantBytes, _ := json.Marshal(tt.want)
if !reflect.DeepEqual(gotBytes, wantBytes) {
t.Errorf("Points.aggrByStep() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit e9f4b21

Please sign in to comment.