Skip to content

Instantly share code, notes, and snippets.

@unixpickle
Created August 23, 2017 00:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unixpickle/e002210247344aaa025a0601bf355bd8 to your computer and use it in GitHub Desktop.
Save unixpickle/e002210247344aaa025a0601bf355bd8 to your computer and use it in GitHub Desktop.
Rolling cosine distance
// Test a rolling formula for computing the cosine
// distance between a vector and a repeated mean of chunks
// of that vector.
package main
import (
"fmt"
"math"
"math/rand"
"github.com/unixpickle/num-analysis/linalg"
)
func main() {
var vecs []linalg.Vector
for i := 0; i < 10; i++ {
vec := make(linalg.Vector, 3)
for j := range vec {
vec[j] = rand.NormFloat64()
}
vecs = append(vecs, vec)
}
fmt.Println("Exact:", exactCosineDist(vecs))
fmt.Println("Calculated:", runningCosineDist(vecs))
}
func exactCosineDist(vecs []linalg.Vector) float64 {
mean := vectorMean(vecs)
var repeated linalg.Vector
var joined linalg.Vector
for _, vec := range vecs {
repeated = append(repeated, mean...)
joined = append(joined, vec...)
}
return repeated.Dot(joined) / (joined.Mag() * repeated.Mag())
}
func runningCosineDist(vecs []linalg.Vector) float64 {
s := vectorMean(vecs).Scale(float64(len(vecs)))
s2 := vectorSecondMoment(vecs).Scale(float64(len(vecs)))
return math.Sqrt(s.Dot(s) / (float64(len(vecs)) * sum(s2)))
}
func vectorMean(vecs []linalg.Vector) linalg.Vector {
sum := vecs[0].Copy()
for _, v := range vecs[1:] {
sum.Add(v)
}
return sum.Scale(1 / float64(len(vecs)))
}
func vectorSecondMoment(vecs []linalg.Vector) linalg.Vector {
squares := make([]linalg.Vector, len(vecs))
for i, v := range vecs {
squares[i] = v.Copy()
for j, x := range squares[i] {
squares[i][j] *= x
}
}
return vectorMean(squares)
}
func sum(vec linalg.Vector) float64 {
var sum float64
for _, x := range vec {
sum += x
}
return sum
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment