JuliaStats / Distances.jl

A Julia package for evaluating distances (metrics) between vectors.
Other
433 stars 98 forks source link

Custom metric gives error #257

Closed alior101 closed 9 months ago

alior101 commented 1 year ago

Trying a simple custom metric (which is just euclidian distan)


struct MyMetric <: SemiMetric end

function (::MyMetric)(a::AbstractVector, b::AbstractVector)
    sum(abs.(a - b))
end

R = kmeans(vecs, clusters_num; maxiter=400, display=:iter, distance=MyMetric())

I'm getting

ERROR: MethodError: no method matching (::MyMetric)(::Float64, ::Float64)
Stacktrace:
 [1] result_type(f::MyMetric, a::Type, b::Type)
   @ Distances ~/.julia/packages/Distances/yhVAl/src/generic.jl:36
 [2] result_type(dist::MyMetric, a::Matrix{Float64}, b::LinearAlgebra.Adjoint{Float64, Matrix{Float64}})
   @ Distances ~/.julia/packages/Distances/yhVAl/src/generic.jl:35
 [3] pairwise(metric::MyMetric, a::Matrix{Float64}, b::LinearAlgebra.Adjoint{Float64, Matrix{Float64}}; dims::Int64)
   @ Distances ~/.julia/packages/Distances/yhVAl/src/generic.jl:320
 [4] _kmeans!(X::LinearAlgebra.Adjoint{Float64, Matrix{Float64}}, weights::Nothing, centers::Matrix{Float64}, maxiter::Int64, tol::Float64, displevel::Int64, distance::MyMetric, rng::Random._GLOBAL_RNG)
   @ Clustering ~/.julia/packages/Clustering/8WUOQ/src/kmeans.jl:141
 [5] kmeans!(X::LinearAlgebra.Adjoint{Float64, Matrix{Float64}}, centers::Matrix{Float64}; weights::Nothing, maxiter::Int64, tol::Float64, display::Symbol, distance::MyMetric, rng::Random._GLOBAL_RNG)
   @ Clustering ~/.julia/packages/Clustering/8WUOQ/src/kmeans.jl:71
 [6] kmeans!
   @ ~/.julia/packages/Clustering/8WUOQ/src/kmeans.jl:46 [inlined]
 [7] kmeans(X::LinearAlgebra.Adjoint{Float64, Matrix{Float64}}, k::Int64; weights::Nothing, init::Symbol, maxiter::Int64, tol::Float64, display::Symbol, distance::MyMetric, rng::Random._GLOBAL_RNG)
   @ Clustering ~/.julia/packages/Clustering/8WUOQ/src/kmeans.jl:114

Any idea what am I missing ?

alior101 commented 1 year ago

Ok, I figured this out alone.. Documenting for anyone elese exepriencing this Turns out that the distance function types must match

function (::MyMetric)(a::Float64, b::Float64)
    sum(abs.(a - b))
end

fixed it

dkarrasch commented 1 year ago

As it says (I understand that we are not very good spelling out the requirements upfront): you need to define another method that deals with the "scalar" case:

(::MyMetric)(a::Float64, b::Float64) = abs(a - b)

This is required for the determination of the target eltype.

Note that your example is the squared Euclidean distance. Which means that integer vectors yield an integer distance. If you want the Euclidean distance (just to demonstrate the reasoning), you should take the square root in your vector-method, and perhaps define

(::MyMetric)(a::Float64, b::Float64) = float(a - b)
KwatMDPhD commented 9 months ago

This issue should be closed. The above answers are good! Thank you.

I did (::MyMetric)(::Real, ::Real) = NaN.