mirror of
https://github.com/sjwhitworth/golearn.git
synced 2025-05-03 22:17:14 +08:00
Merge pull request #20 from sjwhitworth/feature/ifesdjeen/distances2
Remeake of my previous distances branch
This commit is contained in:
commit
3850153945
31
metrics/pairwise/chebyshev.go
Normal file
31
metrics/pairwise/chebyshev.go
Normal file
@ -0,0 +1,31 @@
|
||||
package pairwise
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/gonum/matrix/mat64"
|
||||
)
|
||||
|
||||
type Chebyshev struct{}
|
||||
|
||||
func NewChebyshev() *Chebyshev {
|
||||
return &Chebyshev{}
|
||||
}
|
||||
|
||||
func (self *Chebyshev) Distance(vectorX *mat64.Dense, vectorY *mat64.Dense) float64 {
|
||||
r1, c1 := vectorX.Dims()
|
||||
r2, c2 := vectorY.Dims()
|
||||
if r1 != r2 || c1 != c2 {
|
||||
panic(mat64.ErrShape)
|
||||
}
|
||||
|
||||
max := float64(0)
|
||||
|
||||
for i := 0; i < r1; i++ {
|
||||
for j := 0; j < c1; j++ {
|
||||
max = math.Max(max, math.Abs(vectorX.At(i, j)-vectorY.At(i, j)))
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
42
metrics/pairwise/chebyshev_test.go
Normal file
42
metrics/pairwise/chebyshev_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package pairwise
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gonum/matrix/mat64"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestChebyshev(t *testing.T) {
|
||||
var vectorX, vectorY *mat64.Dense
|
||||
chebyshev := NewChebyshev()
|
||||
|
||||
Convey("Given two vectors", t, func() {
|
||||
vectorX = mat64.NewDense(4, 1, []float64{1, 2, 3, 4})
|
||||
vectorY = mat64.NewDense(4, 1, []float64{-5, -6, 7, 8})
|
||||
|
||||
Convey("When calculating distance with two vectors", func() {
|
||||
result := chebyshev.Distance(vectorX, vectorY)
|
||||
|
||||
Convey("The result should be 8", func() {
|
||||
So(result, ShouldEqual, 8)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When calculating distance with row vectors", func() {
|
||||
vectorX.TCopy(vectorX)
|
||||
vectorY.TCopy(vectorY)
|
||||
result := chebyshev.Distance(vectorX, vectorY)
|
||||
|
||||
Convey("The result should be 8", func() {
|
||||
So(result, ShouldEqual, 8)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When calculating distance with different dimention matrices", func() {
|
||||
vectorX.TCopy(vectorX)
|
||||
So(func() { chebyshev.Distance(vectorX, vectorY) }, ShouldPanicWith, mat64.ErrShape)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
45
metrics/pairwise/cranberra.go
Normal file
45
metrics/pairwise/cranberra.go
Normal file
@ -0,0 +1,45 @@
|
||||
package pairwise
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/gonum/matrix/mat64"
|
||||
)
|
||||
|
||||
type Cranberra struct{}
|
||||
|
||||
func NewCranberra() *Cranberra {
|
||||
return &Cranberra{}
|
||||
}
|
||||
|
||||
func cranberraDistanceStep(num float64, denom float64) float64 {
|
||||
if num == .0 && denom == .0 {
|
||||
return .0
|
||||
} else {
|
||||
return num / denom
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Cranberra) Distance(vectorX *mat64.Dense, vectorY *mat64.Dense) float64 {
|
||||
r1, c1 := vectorX.Dims()
|
||||
r2, c2 := vectorY.Dims()
|
||||
if r1 != r2 || c1 != c2 {
|
||||
panic(mat64.ErrShape)
|
||||
}
|
||||
|
||||
sum := .0
|
||||
|
||||
for i := 0; i < r1; i++ {
|
||||
for j := 0; j < c1; j++ {
|
||||
p1 := vectorX.At(i, j)
|
||||
p2 := vectorY.At(i, j)
|
||||
|
||||
num := math.Abs(p1 - p2)
|
||||
denom := math.Abs(p1) + math.Abs(p2)
|
||||
|
||||
sum += cranberraDistanceStep(num, denom)
|
||||
}
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
51
metrics/pairwise/cranberra_test.go
Normal file
51
metrics/pairwise/cranberra_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
package pairwise
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gonum/matrix/mat64"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestCranberrra(t *testing.T) {
|
||||
var vectorX, vectorY *mat64.Dense
|
||||
cranberra := NewCranberra()
|
||||
|
||||
Convey("Given two vectors that are same", t, func() {
|
||||
vec := mat64.NewDense(7, 1, []float64{0, 1, -2, 3.4, 5, -6.7, 89})
|
||||
distance := cranberra.Distance(vec, vec)
|
||||
|
||||
Convey("The result should be 0", func() {
|
||||
So(distance, ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given two vectors", t, func() {
|
||||
vectorX = mat64.NewDense(5, 1, []float64{1, 2, 3, 4, 9})
|
||||
vectorY = mat64.NewDense(5, 1, []float64{-5, -6, 7, 4, 3})
|
||||
|
||||
Convey("When calculating distance with two vectors", func() {
|
||||
result := cranberra.Distance(vectorX, vectorY)
|
||||
|
||||
Convey("The result should be 2.9", func() {
|
||||
So(result, ShouldEqual, 2.9)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When calculating distance with row vectors", func() {
|
||||
vectorX.TCopy(vectorX)
|
||||
vectorY.TCopy(vectorY)
|
||||
result := cranberra.Distance(vectorX, vectorY)
|
||||
|
||||
Convey("The result should be 2.9", func() {
|
||||
So(result, ShouldEqual, 2.9)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When calculating distance with different dimention matrices", func() {
|
||||
vectorX.TCopy(vectorX)
|
||||
So(func() { cranberra.Distance(vectorX, vectorY) }, ShouldPanicWith, mat64.ErrShape)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
@ -12,15 +12,14 @@ func NewEuclidean() *Euclidean {
|
||||
return &Euclidean{}
|
||||
}
|
||||
|
||||
// Compute usual inner product in the sense of euclidean.
|
||||
// Compute Eucledian inner product.
|
||||
func (self *Euclidean) InnerProduct(vectorX *mat64.Dense, vectorY *mat64.Dense) float64 {
|
||||
result := vectorX.Dot(vectorY)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Compute usual distance in the sense of euclidean.
|
||||
// Also known as L2 distance.
|
||||
// Compute Euclidean distance (also known as L2 distance).
|
||||
func (self *Euclidean) Distance(vectorX *mat64.Dense, vectorY *mat64.Dense) float64 {
|
||||
subVector := mat64.NewDense(0, 0, nil)
|
||||
subVector.Sub(vectorX, vectorY)
|
||||
|
@ -15,26 +15,18 @@ func NewManhattan() *Manhattan {
|
||||
// Manhattan distance, also known as L1 distance.
|
||||
// Compute sum of absolute values of elements.
|
||||
func (self *Manhattan) Distance(vectorX *mat64.Dense, vectorY *mat64.Dense) float64 {
|
||||
var length int
|
||||
subVector := mat64.NewDense(0, 0, nil)
|
||||
subVector.Sub(vectorX, vectorY)
|
||||
|
||||
r, c := subVector.Dims()
|
||||
|
||||
if r == 1 {
|
||||
// Force transpose to column vector
|
||||
subVector.TCopy(subVector)
|
||||
length = c
|
||||
} else if c == 1 {
|
||||
length = r
|
||||
} else {
|
||||
r1, c1 := vectorX.Dims()
|
||||
r2, c2 := vectorY.Dims()
|
||||
if r1 != r2 || c1 != c2 {
|
||||
panic(mat64.ErrShape)
|
||||
}
|
||||
|
||||
result := .0
|
||||
for i := 0; i < length; i++ {
|
||||
result += math.Abs(subVector.At(i, 0))
|
||||
}
|
||||
|
||||
for i := 0; i < r1; i++ {
|
||||
for j := 0; j < c1; j++ {
|
||||
result += math.Abs(vectorX.At(i, j) - vectorY.At(i, j))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -11,6 +11,15 @@ func TestManhattan(t *testing.T) {
|
||||
var vectorX, vectorY *mat64.Dense
|
||||
manhattan := NewManhattan()
|
||||
|
||||
Convey("Given two vectors that are same", t, func() {
|
||||
vec := mat64.NewDense(7, 1, []float64{0, 1, -2, 3.4, 5, -6.7, 89})
|
||||
distance := manhattan.Distance(vec, vec)
|
||||
|
||||
Convey("The result should be 0", func() {
|
||||
So(distance, ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given two vectors", t, func() {
|
||||
vectorX = mat64.NewDense(3, 1, []float64{2, 2, 3})
|
||||
vectorY = mat64.NewDense(3, 1, []float64{1, 4, 5})
|
||||
@ -33,7 +42,7 @@ func TestManhattan(t *testing.T) {
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When calculating distance with row and column vectors", func() {
|
||||
Convey("When calculating distance with different dimention matrices", func() {
|
||||
vectorX.TCopy(vectorX)
|
||||
So(func() { manhattan.Distance(vectorX, vectorY) }, ShouldPanicWith, mat64.ErrShape)
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user