fast_dtw.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package dtw
  2. import (
  3. "container/list"
  4. "math"
  5. )
  6. type T = float64
  7. type TimeSeries = []T
  8. /*
  9. FastDTW
  10. https://github.com/Citing/fastDTW
  11. */
  12. func FastDTW(seriesX TimeSeries, seriesY TimeSeries, radius int) (distance T, path [][2]int) {
  13. var minTSsize = radius + 2
  14. if len(seriesX) < minTSsize || len(seriesY) < minTSsize {
  15. distance, path = DTW(seriesX, seriesY, nil)
  16. } else {
  17. var shrunkX = reduceByHalf(seriesX)
  18. var shrunkY = reduceByHalf(seriesY)
  19. var _, lowResPath = FastDTW(shrunkX, shrunkY, radius)
  20. var window = expandResWindow(lowResPath, len(seriesX), len(seriesY), radius)
  21. distance, path = DTW(seriesX, seriesY, window)
  22. }
  23. return
  24. }
  25. func DTW(seriesX TimeSeries, seriesY TimeSeries, window [][2]int) (T, [][2]int) {
  26. if window == nil {
  27. window = make([][2]int, (len(seriesX)+1)*(len(seriesY)+1))
  28. k := 0
  29. for i := 0; i <= len(seriesX); i++ {
  30. for j := 0; j <= len(seriesY); j++ {
  31. window[k] = [2]int{i, j}
  32. k++
  33. }
  34. }
  35. } else {
  36. for k := 0; k < len(window); k++ {
  37. window[k] = [2]int{window[k][0] + 1, window[k][1] + 1}
  38. }
  39. }
  40. type p struct {
  41. dist T
  42. neighborX int
  43. neighborY int
  44. }
  45. var D = make(map[[2]int]p)
  46. for _, v := range window {
  47. // T type!
  48. D[v] = p{math.MaxFloat64, 0, 0}
  49. }
  50. D[[2]int{0, 0}] = p{0, 0, 0}
  51. for i := 1; i <= len(seriesX); i++ {
  52. for j := 1; j <= len(seriesY); j++ {
  53. v := [2]int{i, j}
  54. dt := math.Abs(seriesX[v[0]-1] - seriesY[v[1]-1])
  55. D[v] = func(p1 p, p2 p, p3 p) p {
  56. var tmp p
  57. if p1.dist < p2.dist {
  58. tmp = p1
  59. } else {
  60. tmp = p2
  61. }
  62. if p3.dist < tmp.dist {
  63. tmp = p3
  64. }
  65. return tmp
  66. }(p{D[[2]int{v[0] - 1, v[1]}].dist + dt, v[0] - 1, v[1]}, p{D[[2]int{v[0], v[1] - 1}].dist + dt, v[0], v[1] - 1}, p{D[[2]int{v[0] - 1, v[1] - 1}].dist + dt, v[0] - 1, v[1] - 1})
  67. }
  68. }
  69. var path_ list.List
  70. for i, j := len(seriesX), len(seriesY); i != 0 || j != 0; {
  71. path_.PushBack([2]int{i - 1, j - 1})
  72. i, j = D[[2]int{i, j}].neighborX, D[[2]int{i, j}].neighborY
  73. }
  74. path := make([][2]int, path_.Len())
  75. for i, j, k := len(seriesX), len(seriesY), 0; i != 0 || j != 0; k++ {
  76. path[k] = ([2]int{i - 1, j - 1})
  77. i, j = D[[2]int{i, j}].neighborX, D[[2]int{i, j}].neighborY
  78. }
  79. distance := D[[2]int{len(seriesX), len(seriesY)}].dist
  80. return distance, path
  81. }
  82. func reduceByHalf(series TimeSeries) TimeSeries {
  83. shrunk := make([]T, len(series)/2)
  84. for i := 0; i < len(shrunk); i++ {
  85. shrunk[i] = (series[2*i] + series[2*i+1]) / 2
  86. }
  87. return shrunk
  88. }
  89. func expandResWindow(path [][2]int, X int, Y int, radius int) [][2]int {
  90. var window_ = make(map[[2]int]int)
  91. for k := 0; k < len(path); k++ {
  92. for i := 0; i <= 2*radius; i++ {
  93. for j := 0; j <= 2*radius; j++ {
  94. x := 2 * (path[k][0] - radius + i)
  95. y := 2 * (path[k][1] - radius + j)
  96. window_[[2]int{x, y}] = 1
  97. window_[[2]int{x + 1, y}] = 1
  98. window_[[2]int{x, y + 1}] = 1
  99. window_[[2]int{x + 1, y + 1}] = 1
  100. }
  101. }
  102. }
  103. var window__ = make(map[[2]int]int)
  104. start_j := 0
  105. for i := 0; i < X; i++ {
  106. new_start_j := -2
  107. for j := start_j; j < Y; j++ {
  108. if window_[[2]int{i, j}] == 1 {
  109. window__[[2]int{i, j}] = 1
  110. if new_start_j == -2 {
  111. new_start_j = j
  112. }
  113. } else if new_start_j != -2 {
  114. break
  115. }
  116. start_j = new_start_j
  117. }
  118. }
  119. var window = make([][2]int, len(window__))
  120. start_j = 0
  121. k := 0
  122. for i := 0; i < X; i++ {
  123. new_start_j := -2
  124. for j := start_j; j < Y; j++ {
  125. if window_[[2]int{i, j}] == 1 {
  126. window[k] = [2]int{i, j}
  127. k++
  128. if new_start_j == -2 {
  129. new_start_j = j
  130. }
  131. } else if new_start_j != -2 {
  132. break
  133. }
  134. start_j = new_start_j
  135. }
  136. }
  137. return window
  138. }