Skip to content

Commit 0938766

Browse files
committed
renamed Options structs to shortname Opts and applyAV works on both ab and ba mps
1 parent e76fb63 commit 0938766

File tree

5 files changed

+91
-50
lines changed

5 files changed

+91
-50
lines changed

analyze.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package matrixprofile
22

3-
// AnalyzeOptions contains all the parameters needed for basic features to discover from
3+
// AnalyzeOpts contains all the parameters needed for basic features to discover from
44
// a matrix profile. This is currently limited to motif, discord, and segmentation discovery.
5-
type AnalyzeOptions struct {
5+
type AnalyzeOpts struct {
66
KMotifs int // the top k motifs to find
77
RMotifs float64 // the max radius to find motifs
88
KDiscords int // the top k discords to find
99
OutputFilename string // relative or absolute filepath for the visualization output
1010
}
1111

1212
// NewAnalyzeOpts creates a default set of parameters to analyze the matrix profile.
13-
func NewAnalyzeOpts() *AnalyzeOptions {
14-
return &AnalyzeOptions{
13+
func NewAnalyzeOpts() *AnalyzeOpts {
14+
return &AnalyzeOpts{
1515
KMotifs: 3,
1616
RMotifs: 2,
1717
KDiscords: 3,

kmp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ func (k KMP) columnWiseCumSum(D [][]float64) {
267267
}
268268

269269
// Analyze has not been implemented yet
270-
func (k KMP) Analyze(mo *MPOptions, ao *AnalyzeOptions) error {
270+
func (k KMP) Analyze(mo *MPOpts, ao *AnalyzeOpts) error {
271271
return errors.New("Analyze for KMP has not been implemented yet.")
272272
}
273273

matrixprofile.go

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type MatrixProfile struct {
4141
MPB []float64 `json:"mp_ba"` // matrix profile for the BA join
4242
IdxB []int `json:"pi_ba"` // matrix profile index for the BA join
4343
AV av.AV `json:"annotation_vector"` // type of annotation vector which defaults to all ones
44-
Opts *MPOptions `json:"options"` // options used for the computation
44+
Opts *MPOpts `json:"options"` // options used for the computation
4545
}
4646

4747
// New creates a matrix profile struct with a given timeseries length n and
@@ -83,21 +83,19 @@ func New(a, b []float64, w int) (*MatrixProfile, error) {
8383
return &mp, nil
8484
}
8585

86-
// ApplyAV applies an annotation vector to the current matrix profile. Annotation vector
87-
// values must be between 0 and 1.
88-
func (mp MatrixProfile) ApplyAV() ([]float64, error) {
89-
avec, err := av.Create(mp.AV, mp.B, mp.W)
86+
func applySingleAV(mp, ts []float64, w int, a av.AV) ([]float64, error) {
87+
avec, err := av.Create(a, ts, w)
9088
if err != nil {
9189
return nil, err
9290
}
9391

94-
if len(avec) != len(mp.MP) {
95-
return nil, fmt.Errorf("annotation vector length, %d, does not match matrix profile length, %d", len(avec), len(mp.MP))
92+
if len(avec) != len(mp) {
93+
return nil, fmt.Errorf("annotation vector length, %d, does not match matrix profile length, %d", len(avec), len(mp))
9694
}
9795

9896
// find the maximum matrix profile value
9997
maxMP := 0.0
100-
for _, val := range mp.MP {
98+
for _, val := range mp {
10199
if val > maxMP {
102100
maxMP = val
103101
}
@@ -112,14 +110,35 @@ func (mp MatrixProfile) ApplyAV() ([]float64, error) {
112110

113111
// applies the matrix profile correction. 1 results in no change to the matrix profile and
114112
// 0 results in lifting the current matrix profile value by the maximum matrix profile value
115-
out := make([]float64, len(mp.MP))
113+
out := make([]float64, len(mp))
116114
for idx, val := range avec {
117-
out[idx] = mp.MP[idx] + (1-val)*maxMP
115+
out[idx] = mp[idx] + (1-val)*maxMP
118116
}
119117

120118
return out, nil
121119
}
122120

121+
// ApplyAV applies an annotation vector to the current matrix profile. Annotation vector
122+
// values must be between 0 and 1.
123+
func (mp MatrixProfile) ApplyAV() ([]float64, []float64, error) {
124+
var err error
125+
var abmp, bamp []float64
126+
127+
abmp, err = applySingleAV(mp.MP, mp.A, mp.W, mp.AV)
128+
if err != nil {
129+
return nil, nil, err
130+
}
131+
132+
if mp.MPB != nil {
133+
bamp, err = applySingleAV(mp.MPB, mp.B, mp.W, mp.AV)
134+
}
135+
136+
if err != nil {
137+
return nil, nil, err
138+
}
139+
return abmp, bamp, nil
140+
}
141+
123142
// Save will save the current matrix profile struct to disk
124143
func (mp MatrixProfile) Save(filepath, format string) error {
125144
var err error
@@ -191,25 +210,47 @@ func (m *mpVals) Pop() interface{} {
191210
return x
192211
}
193212

213+
type MPDistOpts struct {
214+
AV av.AV
215+
Opts *MPOpts
216+
}
217+
218+
func NewMPDistOpts() *MPDistOpts {
219+
return &MPDistOpts{
220+
AV: av.Default,
221+
Opts: NewMPOpts(),
222+
}
223+
}
224+
194225
// MPDist computes the matrix profile distance measure between a and b with a
195226
// subsequence window of m.
196-
func MPDist(a, b []float64, m int, o *MPOptions) (float64, error) {
197-
mp, err := New(a, b, m)
227+
func MPDist(a, b []float64, w int, o *MPDistOpts) (float64, error) {
228+
if o == nil {
229+
o = NewMPDistOpts()
230+
}
231+
232+
mp, err := New(a, b, w)
198233
if err != nil {
199234
return 0, err
200235
}
201236

202-
if err = mp.Compute(o); err != nil {
237+
if err = mp.Compute(o.Opts); err != nil {
203238
return 0, nil
204239
}
240+
241+
mpab, mpba, err := mp.ApplyAV()
242+
if err != nil {
243+
return 0, nil
244+
}
245+
205246
thresh := 0.05
206247
k := int(thresh * float64(len(a)+len(b)))
207-
mpABBASize := len(mp.MP) + len(mp.MPB)
248+
mpABBASize := len(mpab) + len(mpba)
208249

209250
if k < mpABBASize {
210251
var lowestMPs mpVals
211252
heap.Init(&lowestMPs)
212-
for _, d := range mp.MP {
253+
for _, d := range mpab {
213254
// since this is a max heap and correlations go from 0-1 we need high correlations
214255
// to stay in the heap with the poorest correlation at the root.
215256
if !mp.Opts.Euclidean {
@@ -225,7 +266,7 @@ func MPDist(a, b []float64, m int, o *MPOptions) (float64, error) {
225266
}
226267
}
227268

228-
for _, d := range mp.MPB {
269+
for _, d := range mpba {
229270
// since this is a max heap and correlations go from 0-1 we need high correlations
230271
// to stay in the heap with the poorest correlation at the root.
231272
if !mp.Opts.Euclidean {
@@ -289,22 +330,22 @@ const (
289330
AlgoMPX Algo = "mpx"
290331
)
291332

292-
// MPOptions are parameters to vary the algorithm to compute the matrix profile.
293-
type MPOptions struct {
333+
// MPOpts are parameters to vary the algorithm to compute the matrix profile.
334+
type MPOpts struct {
294335
Algorithm Algo `json:"algorithm"` // choose which algorithm to compute the matrix profile
295336
Sample float64 `json:"sample_pct"` // only applicable to algorithm STAMP
296337
Parallelism int `json:"parallelism"`
297338
Euclidean bool `json:"euclidean"` // defaults to using euclidean distance instead of pearson correlation for matrix profile
298339
RemapNegCorr bool `json:"remap_negative_correlation"` // defaults to no remapping. This is used so that highly negatively correlated sequences will show a low distance as well.
299340
}
300341

301-
// NewMPOpts returns a default MPOptions
302-
func NewMPOpts() *MPOptions {
342+
// NewMPOpts returns a default MPOpts
343+
func NewMPOpts() *MPOpts {
303344
p := runtime.NumCPU() * 2
304345
if p < 1 {
305346
p = 1
306347
}
307-
return &MPOptions{
348+
return &MPOpts{
308349
Algorithm: AlgoMPX,
309350
Sample: 1.0,
310351
Parallelism: p,
@@ -313,7 +354,7 @@ func NewMPOpts() *MPOptions {
313354
}
314355

315356
// Compute calculate the matrixprofile given a set of input options.
316-
func (mp *MatrixProfile) Compute(o *MPOptions) error {
357+
func (mp *MatrixProfile) Compute(o *MPOpts) error {
317358
if o == nil {
318359
o = NewMPOpts()
319360
}
@@ -1137,7 +1178,7 @@ func (mp MatrixProfile) mpxbaBatch(idx int, mua, siga, dfa, dga, mub, sigb, dfb,
11371178
// Analyze performs the matrix profile computation and discovers various features
11381179
// from the profile such as motifs, discords, and segmentation. The results are
11391180
// visualized and saved into an output file.
1140-
func (mp MatrixProfile) Analyze(mo *MPOptions, ao *AnalyzeOptions) error {
1181+
func (mp MatrixProfile) Analyze(mo *MPOpts, ao *AnalyzeOpts) error {
11411182
var err error
11421183

11431184
if err = mp.Compute(mo); err != nil {
@@ -1174,7 +1215,7 @@ func (mp MatrixProfile) DiscoverMotifs(k int, r float64) ([]MotifGroup, error) {
11741215

11751216
motifs := make([]MotifGroup, k)
11761217

1177-
mpCurrent, err := mp.ApplyAV()
1218+
mpCurrent, _, err := mp.ApplyAV()
11781219
if err != nil {
11791220
return nil, err
11801221
}
@@ -1265,7 +1306,7 @@ func (mp MatrixProfile) DiscoverMotifs(k int, r float64) ([]MotifGroup, error) {
12651306
// matrix profile. Each discovery of a discord will apply an exclusion zone around
12661307
// the found index so that new discords can be discovered.
12671308
func (mp MatrixProfile) DiscoverDiscords(k int, exclusionZone int) ([]int, error) {
1268-
mpCurrent, err := mp.ApplyAV()
1309+
mpCurrent, _, err := mp.ApplyAV()
12691310
if err != nil {
12701311
return nil, err
12711312
}

matrixprofile_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func TestApplyAV(t *testing.T) {
4545
mprof := []float64{4, 6, 10, 2, 1, 0, 1, 2, 0, 0, 1, 2, 6}
4646

4747
testdata := []struct {
48-
b []float64
48+
a []float64
4949
w int
5050
av av.AV
5151
expectedMP []float64
@@ -55,23 +55,23 @@ func TestApplyAV(t *testing.T) {
5555

5656
var mp MatrixProfile
5757
var err error
58-
var out []float64
58+
var outab []float64
5959
for _, d := range testdata {
6060
newMP := make([]float64, len(mprof))
6161
copy(newMP, mprof)
62-
mp = MatrixProfile{B: d.b, W: d.w, MP: newMP, AV: d.av}
63-
out, err = mp.ApplyAV()
62+
mp = MatrixProfile{A: d.a, W: d.w, MP: newMP, AV: d.av}
63+
outab, _, err = mp.ApplyAV()
6464
if err != nil {
6565
t.Fatal(err)
6666
}
6767

68-
if len(out) != len(d.expectedMP) {
69-
t.Errorf("Expected %d elements, but got %d, %+v", len(d.expectedMP), len(out), d)
68+
if len(outab) != len(d.expectedMP) {
69+
t.Errorf("Expected %d elements, but got %d, %+v", len(d.expectedMP), len(outab), d)
7070
break
7171
}
72-
for i := 0; i < len(out); i++ {
73-
if math.Abs(float64(out[i]-d.expectedMP[i])) > 1e-7 {
74-
t.Errorf("Expected %v,\nbut got\n%v for %+v", d.expectedMP, out, d)
72+
for i := 0; i < len(outab); i++ {
73+
if math.Abs(float64(outab[i]-d.expectedMP[i])) > 1e-7 {
74+
t.Errorf("Expected %v,\nbut got\n%v for %+v", d.expectedMP, outab, d)
7575
break
7676
}
7777
}

pmp.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type PMP struct {
1919
PMP [][]float64 `json:"pmp"` // pan matrix profile
2020
PIdx [][]int `json:"ppi"` // pan matrix profile index
2121
PWindows []int `json:"windows"` // pan matrix windows used and is aligned with PMP and PIdx
22-
Opts *PMPOptions `json:"options"` // options used for the computation
22+
Opts *PMPOpts `json:"options"` // options used for the computation
2323
}
2424

2525
// NewPMP creates a new Pan matrix profile
@@ -88,27 +88,27 @@ func (p *PMP) Load(filepath, format string) error {
8888
return err
8989
}
9090

91-
// PMPOptions are parameters to vary the algorithm to compute the pan matrix profile.
92-
type PMPOptions struct {
93-
LowerM int `json:"lower_m"` // used for pan matrix profile
94-
UpperM int `json:"upper_m"` // used for pan matrix profile
95-
MPOpts *MPOptions `json:"mp_options"`
91+
// PMPOpts are parameters to vary the algorithm to compute the pan matrix profile.
92+
type PMPOpts struct {
93+
LowerM int `json:"lower_m"` // used for pan matrix profile
94+
UpperM int `json:"upper_m"` // used for pan matrix profile
95+
MPOpts *MPOpts `json:"mp_options"`
9696
}
9797

98-
// NewPMPOpts returns a default PMPOptions
99-
func NewPMPOpts(l, u int) *PMPOptions {
98+
// NewPMPOpts returns a default PMPOpts
99+
func NewPMPOpts(l, u int) *PMPOpts {
100100
if l > u {
101101
u = l
102102
}
103-
return &PMPOptions{
103+
return &PMPOpts{
104104
LowerM: l,
105105
UpperM: u,
106106
MPOpts: NewMPOpts(),
107107
}
108108
}
109109

110110
// Compute calculate the pan matrixprofile given a set of input options.
111-
func (p *PMP) Compute(o *PMPOptions) error {
111+
func (p *PMP) Compute(o *PMPOpts) error {
112112
if o == nil {
113113
return errors.New("Must provide PMP compute options")
114114
}
@@ -161,7 +161,7 @@ func (p *PMP) pmp() error {
161161
}
162162

163163
// Analyze has not been implemented yet
164-
func (p PMP) Analyze(co *MPOptions, ao *AnalyzeOptions) error {
164+
func (p PMP) Analyze(co *MPOpts, ao *AnalyzeOpts) error {
165165
return errors.New("Analyze for PMP has not been implemented yet.")
166166
}
167167

0 commit comments

Comments
 (0)