2020-11-09 01:16:28 +01:00
|
|
|
|
@doc """
|
2020-12-30 14:39:18 +01:00
|
|
|
|
pairwise(dist::StringDistance, xs::AbstractVector, ys::AbstractVector = xs; preprocess = nothing)
|
2020-11-09 01:16:28 +01:00
|
|
|
|
|
2020-11-10 04:04:35 +01:00
|
|
|
|
Compute distances between all pairs of elements in `xs` and `ys` according to the
|
2020-12-30 14:39:18 +01:00
|
|
|
|
`StringDistance` `dist`. Returns a matrix R such that `R[i, j]` corrresponds to the distance between `xs[i]` and `ys[j]`.
|
2020-11-09 01:16:28 +01:00
|
|
|
|
|
2020-11-14 20:40:44 +01:00
|
|
|
|
For AbstractQGramDistances preprocessing will be used either if `preprocess` is set
|
2020-11-10 04:04:35 +01:00
|
|
|
|
to true or if there are more than 5 elements in `xs`. Set `preprocess` to
|
2020-11-09 01:16:28 +01:00
|
|
|
|
false if no preprocessing should be used, regardless of length.
|
|
|
|
|
|
|
|
|
|
Both symmetric and asymmetric versions are available.
|
|
|
|
|
|
|
|
|
|
### Examples
|
|
|
|
|
```julia-repl
|
|
|
|
|
julia> using StringDistances
|
|
|
|
|
julia> iter = ["New York", "Princeton"]
|
2020-12-30 14:39:18 +01:00
|
|
|
|
julia> pairwise(Levenshtein(), iter)
|
2020-11-09 01:16:28 +01:00
|
|
|
|
2×2 Array{Float64,2}:
|
|
|
|
|
0.0 9.0
|
|
|
|
|
9.0 0.0
|
|
|
|
|
julia> iter2 = ["San Francisco"]
|
2020-12-30 14:39:18 +01:00
|
|
|
|
julia> pairwise(Levenshtein(), iter, iter2)
|
2020-11-09 01:16:28 +01:00
|
|
|
|
2×1 Array{Float64,2}:
|
|
|
|
|
12.0
|
|
|
|
|
10.0
|
|
|
|
|
```
|
|
|
|
|
"""
|
|
|
|
|
Distances.pairwise
|
|
|
|
|
|
2020-12-30 14:39:18 +01:00
|
|
|
|
function Distances.pairwise(dist::StringDistance, xs::AbstractVector, ys::AbstractVector = xs; preprocess = nothing)
|
2020-11-10 04:04:35 +01:00
|
|
|
|
T = result_type(dist, eltype(xs), eltype(ys))
|
|
|
|
|
if Missing <: Union{eltype(xs), eltype(ys)}
|
|
|
|
|
T = Union{T, Missing}
|
|
|
|
|
end
|
|
|
|
|
R = Matrix{T}(undef, length(xs), length(ys))
|
|
|
|
|
pairwise!(R, dist, xs, ys; preprocess = preprocess)
|
2020-11-09 02:05:14 +01:00
|
|
|
|
end
|
2020-11-09 01:16:28 +01:00
|
|
|
|
|
2020-11-09 02:05:14 +01:00
|
|
|
|
@doc """
|
2020-12-30 14:39:18 +01:00
|
|
|
|
pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector, ys::AbstractVector = xs; preprocess = nothing)
|
2020-11-09 01:16:28 +01:00
|
|
|
|
|
2020-11-10 04:04:35 +01:00
|
|
|
|
Compute distances between all pairs of elements in `xs` and `ys` according to the
|
2020-12-30 14:39:18 +01:00
|
|
|
|
`StringDistance` `dist` and write the result in `R`. `R[i, j]` corrresponds to the distance between `xs[i]` and `ys[j]`.
|
2020-11-09 01:16:28 +01:00
|
|
|
|
|
2020-11-14 20:40:44 +01:00
|
|
|
|
For AbstractQGramDistances preprocessing will be used either if `preprocess` is set
|
2020-11-10 04:04:35 +01:00
|
|
|
|
to true or if there are more than 5 elements in `xs`. Set `preprocess` to
|
2020-11-09 02:05:14 +01:00
|
|
|
|
false if no preprocessing should be used, regardless of length.
|
|
|
|
|
"""
|
|
|
|
|
Distances.pairwise!
|
2020-11-09 01:16:28 +01:00
|
|
|
|
|
2020-12-30 14:39:18 +01:00
|
|
|
|
function Distances.pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector, ys::AbstractVector = xs; preprocess = nothing)
|
2020-11-10 04:04:35 +01:00
|
|
|
|
length(xs) == size(R, 1) || throw(DimensionMismatch("inconsistent length"))
|
|
|
|
|
length(ys) == size(R, 2) || throw(DimensionMismatch("inconsistent length"))
|
2020-12-30 14:39:18 +01:00
|
|
|
|
((xs === ys) & (dist isa SemiMetric)) ?
|
2020-11-10 04:04:35 +01:00
|
|
|
|
_symmetric_pairwise!(R, dist, xs; preprocess = preprocess) :
|
2020-12-30 14:39:18 +01:00
|
|
|
|
_asymmetric_pairwise!(R, dist, xs, ys; preprocess = preprocess)
|
2020-11-09 01:16:28 +01:00
|
|
|
|
end
|
|
|
|
|
|
2020-11-10 04:04:35 +01:00
|
|
|
|
function _symmetric_pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector; preprocess = nothing)
|
|
|
|
|
objs = _preprocess(xs, dist, preprocess)
|
2020-11-09 01:16:28 +01:00
|
|
|
|
for i in 1:length(objs)
|
2020-11-10 04:04:35 +01:00
|
|
|
|
# handle missing
|
|
|
|
|
R[i, i] = objs[i] != objs[i]
|
2020-11-09 01:16:28 +01:00
|
|
|
|
Threads.@threads for j in (i+1):length(objs)
|
|
|
|
|
R[i, j] = R[j, i] = evaluate(dist, objs[i], objs[j])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return R
|
|
|
|
|
end
|
|
|
|
|
|
2020-11-10 04:04:35 +01:00
|
|
|
|
function _asymmetric_pairwise!(R::AbstractMatrix, dist::StringDistance, xs::AbstractVector, ys::AbstractVector; preprocess = nothing)
|
|
|
|
|
objsxs = _preprocess(xs, dist, preprocess)
|
2020-12-30 14:39:18 +01:00
|
|
|
|
objsys = xs === ys ? objsxs : _preprocess(ys, dist, preprocess)
|
2020-11-10 04:04:35 +01:00
|
|
|
|
for i in 1:length(objsxs)
|
|
|
|
|
Threads.@threads for j in 1:length(objsys)
|
|
|
|
|
R[i, j] = evaluate(dist, objsxs[i], objsys[j])
|
2020-11-09 01:16:28 +01:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return R
|
2020-11-10 16:12:28 +01:00
|
|
|
|
end
|
2020-12-30 14:39:18 +01:00
|
|
|
|
|
|
|
|
|
function _preprocess(xs, dist::AbstractQGramDistance, preprocess)
|
|
|
|
|
if preprocess === nothing ? length(xs) >= 5 : preprocess
|
|
|
|
|
return map(x -> x === missing ? x : QGramSortedVector(x, dist.q), xs)
|
|
|
|
|
else
|
|
|
|
|
return xs
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
_preprocess(xs, dist::StringDistance, preprocess) = xs
|