Packages

A collection of useful mathematical functions in Elixir with a slant towards statistics, linear algebra and machine learning

Current section

Files

Jump to
numerix lib distance.ex
Raw

lib/distance.ex

defmodule Numerix.Distance do
@moduledoc """
Distance functions between two vectors.
"""
use Numerix.Tensor
import Numerix.LinearAlgebra
alias Numerix.{Common, Correlation, Statistics}
@doc """
Mean squared error, the average of the squares of the errors
betwen two vectors, i.e. the difference between predicted
and actual values.
"""
@spec mse(Common.vector(), Common.vector()) :: Common.maybe_float()
def mse(x = %Tensor{}, y = %Tensor{}) do
p = pow(x - y, 2)
Statistics.mean(p.items)
end
def mse(vector1, vector2) do
x = Tensor.new(vector1)
y = Tensor.new(vector2)
mse(x, y)
end
@doc """
Root mean square error of two vectors, or simply the
square root of mean squared error of the same set of
values. It is a measure of the differences between
predicted and actual values.
"""
@spec rmse(Common.vector(), Common.vector()) :: Common.maybe_float()
def rmse(vector1, vector2) do
:math.sqrt(mse(vector1, vector2))
end
@doc """
The Pearson's distance between two vectors.
"""
@spec pearson(Common.vector(), Common.vector()) :: Common.maybe_float()
def pearson(vector1, vector2) do
case Correlation.pearson(vector1, vector2) do
nil -> nil
correlation -> 1.0 - correlation
end
end
@doc """
The Minkowski distance between two vectors.
"""
@spec minkowski(Common.vector(), Common.vector(), integer) :: Common.maybe_float()
def minkowski(x, y, p \\ 3)
def minkowski(x = %Tensor{}, y = %Tensor{}, p) do
norm(p, x - y)
end
def minkowski(vector1, vector2, p) do
x = Tensor.new(vector1)
y = Tensor.new(vector2)
minkowski(x, y, p)
end
@doc """
The Euclidean distance between two vectors.
"""
@spec euclidean(Common.vector(), Common.vector()) :: Common.maybe_float()
def euclidean(x = %Tensor{}, y = %Tensor{}) do
l2_norm(x - y)
end
def euclidean(vector1, vector2) do
x = Tensor.new(vector1)
y = Tensor.new(vector2)
euclidean(x, y)
end
@doc """
The Manhattan distance between two vectors.
"""
@spec manhattan(Common.vector(), Common.vector()) :: Common.maybe_float()
def manhattan(x = %Tensor{}, y = %Tensor{}) do
l1_norm(x - y)
end
def manhattan(vector1, vector2) do
x = Tensor.new(vector1)
y = Tensor.new(vector2)
manhattan(x, y)
end
@doc """
The Jaccard distance (1 - Jaccard index) between two vectors.
"""
@spec jaccard(Common.vector(), Common.vector()) :: Common.maybe_float()
def jaccard(%Tensor{items: []}, %Tensor{items: []}), do: 0.0
def jaccard(%Tensor{items: []}, _), do: nil
def jaccard(_, %Tensor{items: []}), do: nil
def jaccard([], []), do: 0.0
def jaccard([], _), do: nil
def jaccard(_, []), do: nil
def jaccard(vector1, vector2) do
vector1
|> Stream.zip(vector2)
|> Enum.reduce({0, 0}, fn {x, y}, {intersection, union} ->
case {x, y} do
{x, y} when x == 0 or y == 0 ->
{intersection, union}
{x, y} when x == y ->
{intersection + 1, union + 1}
_ ->
{intersection, union + 1}
end
end)
|> to_jaccard_distance
end
defp to_jaccard_distance({_intersection, union}) when union == 0, do: 1.0
defp to_jaccard_distance({intersection, union}) do
1 - intersection / union
end
end