BIDistances provides a common interface for calculating,
comparing, and examining distances, dissimilarities, divergences, and
selected similarities. The package connects four tasks that are usually
treated separately:
DistanceMatrix().DistanceProperties().DistanceDistributions().p = 2.Additional domain-specific functionality includes a Gene Ontology-derived TF-IDF distance, time-series distances, nearest-neighbour distances, toroidal Euclidean distances, and mixed-data constructions.
DistanceMatrix() interface for native
BIDistances methods and all predefined methods supplied by
parallelDist, with optional access to
philentropy and manydist.philentropy is
installed. This count excludes aliases, user-defined C++
functions, direct-only functions, and the configurable mixed-data
constructions in manydist.parallelDist, an internal RcppParallel
multicore backend, or optional OpenCL kernels.p = 2 OpenCL route.DistanceDistributions() for comparing candidate
distance distributions and identifying multimodality under the goal of
finding distance-based cluster structures. Distance vectors above 72,000
values use the integrated batch dip-test backend, while smaller vectors
retain diptest::dip.test().Tfidf_Distance() for deriving distances between genes
from a gene-by-Gene-Ontology-term feature matrix.DistanceProperties() for documenting the mathematical
properties and conditions of native methods.| Access layer | Canonical named routes | Counting rule |
|---|---|---|
Package-defined DistanceMatrix() routes |
19 | 12 native routes, six correlation routes, and squared Euclidean |
Predefined parallelDist methods |
41 | The user-defined custom route is excluded |
| Core installation | 60 | Package-defined routes plus parallelDist |
Additional philentropy identifiers |
34 | Exact overlaps and aliases already resolved by
DistanceMatrix() are excluded |
Numerical-data total with philentropy |
94 | Canonical names only |
manydist adds preset-based and custom mixed-type
distance constructions. These are not included in the total because
their specifications are configurable rather than a fixed list of
independent methods. Direct-only functions such as
TWED_Distance() and
ToroidalEuclidean_Distance() are also outside the
dispatcher count.
From CRAN:
install.packages("BIDistances")From GitHub:
install.packages("remotes")
remotes::install_github("Mthrun/BIDistances")System requirements are R >= 3.5.0, GNU make, and a suitable C++
toolchain. An OpenCL library and the R package OpenCL are
optional and are required only for GPU acceleration. Pandoc is required
when rebuilding the vignette.
library(BIDistances)
data("Hepta")
Data = Hepta$Data
D_euclidean = DistanceMatrix(
Data,
method = "euclidean"
)
D_minkowski = DistanceMatrix(
Data,
method = "minkowski",
p = 3
)
D_cosine = DistanceMatrix(
Data,
method = "Cosine_Distance"
)
D_euclidean[1:5, 1:5]DistanceMatrix() first checks for a native
BIDistances route. Other method names are evaluated by
parallelDist; methods unavailable there can be obtained
from philentropy when it is installed. Mixed numerical and
categorical data can be handled through manydist.
For finite p >= 1, Minkowski_Distance()
computes
\[ d_{p,w}(x,y) = \left(\sum_k w_k |x_k-y_k|^p\right)^{1/p}. \]
Data = as.matrix(iris[1:30, 1:4])
Weights = c(1, 2, 0.5, 1)
D_parallel = Minkowski_Distance(
Data = Data,
p = 3,
Weights = Weights,
backend = "parallelDist",
threads = 2
)
D_multicore = Minkowski_Distance(
Data = Data,
p = 3,
Weights = Weights,
backend = "multicore",
threads = 2
)
max(abs(D_parallel - D_multicore))The optional OpenCL route uses separate kernels for general
p and delegates p = 2 to the established
weighted Euclidean GPU implementation:
# Requires an installed OpenCL implementation and a usable device.
D_opencl = Minkowski_Distance(
Data = Data,
p = 3,
Weights = Weights,
backend = "opencl",
Mem = 2
)Use backend = "auto" to try OpenCL and fall back to the
internal multicore backend with a warning. For p = Inf,
unit weights are required and the maximum or Chebyshev distance is
returned. Values 0 < p < 1 are available through
Fractional_Distance() but are generally not metrics.
The same weighted route is available through
DistanceMatrix():
D_dispatch = DistanceMatrix(
Data,
method = "Minkowski_Distance",
p = 3,
Weights = Weights,
backend = "multicore",
threads = 2
)The memory planner can be inspected before a large OpenCL calculation:
MemoryPlan = calculateMemoryDemandGPU(
n = 70000,
d = 784,
mem = 6
)
MemoryPlanThe Euclidean-related entry points have deliberately different roles:
| Entry point | Role |
|---|---|
DistanceMatrix(Data, method = "euclidean") |
Euclidean distance through parallelDist |
EuclideanMulticore_Distance(Data) |
Internal unweighted multicore Euclidean implementation |
EuclideanGPU_Distance(Data, Weights, backend = ...) |
Weighted Euclidean CPU/OpenCL implementation |
Minkowski_Distance(Data, p = 2, ...) |
Unified weighted Minkowski interface using the optimized Euclidean OpenCL route |
ToroidalEuclidean_Distance(Points, Lines, Columns) |
Euclidean distance on a two-dimensional torus |
library(BIDistances)
data("Hepta")
set.seed(42)
Selection = DistanceDistributions(
Data = Hepta$Data,
DistanceMethods = c(
"euclidean",
"manhattan",
"minkowski",
"chord"
),
CosineNonParallel = TRUE,
CorrelationDist = TRUE,
PlotIt = FALSE
)
Selection$DistanceChoice
Selection$SelectionStatistics[, c(
"Distance", "DipStatistic", "DipPValue", "BimodalityAmplitude", "Status"
)]
if (!is.null(Selection$DistanceMatrix)) {
Selection$DistanceMatrix[1:5, 1:5]
}
Selection$ggobjectThe workflow ranks candidate distributions using
multimodality-related information under the explicit goal of finding
distance-based cluster structures. SelectionStatistics
reports both the Hartigan dip statistic and its p-value. For at most
72,000 pairwise values the established diptest::dip.test()
path is retained; larger candidate columns are processed together by the
integrated C++ batch with explicit asymptotic calibration. The result is
exploratory: multimodality supports this structural goal, but it is not
a universal proof of cluster validity or biological relevance.
library(BIDistances)
data("Hearingloss_N109")
Result = Tfidf_Distance(
Hearingloss_N109$FeatureMatrix_Gene2Term,
tf_fun = mean
)
Distance = Result$Distance
TfidfWeights = Result$TfidfWeights
Distance[1:5, 1:5]
TfidfWeights[1:5]Rows represent genes and columns represent GO terms. The function returns pairwise absolute differences between scalar TF-IDF weights together with those weights. Because different annotation rows can receive the same scalar weight, the construction is a pseudometric on the original rows.
library(BIDistances)
TimeSeries = cbind(
SeriesA = c(0, 1, 2, 1, 0),
SeriesB = c(0, 1, 1.5, 1, 0),
SeriesC = c(2, 1, 0, 1, 2)
)
D_msmd = DistanceMatrix(
TimeSeries,
method = "MSMD_Distance",
ParameterC = 1
)
D_msmdFor MSMD_Distance and DTW_Distance, every
column is one time series and row order is the temporal order. No
separate time variable should be supplied. TWED_Distance()
requires explicit timestamps and is therefore called directly rather
than through DistanceMatrix().
[1:n,1:d] numerical matrix with n cases, d variables[1:d,1:d] numerical matrix of distancesMost methods compare the rows of Data. The documented
exceptions are:
DTW_Distance and MSMD_Distance, which
compare time series stored in columns.EndresSchindelin_Distance, which compares columns as
samples of variables.TWED_Distance, which compares two time series directly
and requires explicit time vectors.The word distance is used as a broad software convention. Not every returned quantity is a mathematical metric. The complete machine-readable classification is available through:
Properties = DistanceProperties()
Properties[, c(
"Function",
"Classification",
"Conditions"
)]Examples include:
p >= 1
with strictly positive weights and can become a pseudometric when zero
weights remove distinguishing coordinates.Fractional_Distance() with 0 < p < 1
generally violates the triangle inequality.Mahalanobis_Distance() returns squared Mahalanobis
dissimilarities; applying sqrt() yields the metric
form.Cosine_Distance() returns
1 - cosine similarity and is not a metric in general.Tfidf_Distance() is a pseudometric on the original
gene-annotation rows.Kullback_Leibler_div() returns a divergence, including
when symmetrized.See ?distance-conventions and
?DistanceProperties for details. The registry documents
mathematical conditions; it is not a runtime proof for arbitrary
user-supplied data.
DistanceMatrix() routesCanonical package methods available through
DistanceMatrix() are:
Cosine_DistanceEndresSchindelin_DistanceFractional_DistanceGini_DistanceJaccard_DistanceMahalanobis_DistanceMinkowski_DistanceSharedNeighbor_DistanceTfidf_DistanceWasserstein_DistanceDTW_DistanceMSMD_DistanceTWED_Distance() is intentionally excluded from the
dispatcher because its explicit time vectors cannot be inferred from the
standard matrix interface.
Some functions require suggested packages only when they are used:
OpenCL for the GPU backend.philentropy for additional probability-distribution
methods.manydist for mixed numerical and categorical data.dtw and proxy for
DTW_Distance().ineq for Gini_Distance().transport for Wasserstein_Distance().ScatterDensity for sample-based Endres-Schindelin
calculations.The full manual is available at CRAN.
The package vignette can be opened from R with:
vignette("BIDistances")Citation information is available with:
citation("BIDistances")[Thrun, 2021] Thrun, M. C.: The Exploitation of Distance Distributions for Clustering, International Journal of Computational Intelligence and Applications, Vol. 20(3), 2150016, doi:10.1142/S1469026821500164, 2021.
[Stier & Thrun, 2023] Stier, Q., & Thrun, M. C.: Deriving Homogeneous Subsets from Gene Sets by Exploiting the Gene Ontology, Informatica, Vol. 34(2), pp. 357-386, doi:10.15388/23-INFOR517, 2023.