Typed Transforms and Graphs
Brian Muchmore
2026-07-14
Source:vignettes/Typed-Transforms-and-Graphs.Rmd
Typed-Transforms-and-Graphs.RmdThis vignette covers the two middle tiers —
precise_transform() and precise_graph() —
whose job is to keep the type of a relatedness matrix explicit
and honest at every step.
set.seed(1)
idx <- c(1:5, 61:65, 121:125)
cell_cycle <- as.matrix(data_cell_cycle[idx, -1])[, 1:50]
storage.mode(cell_cycle) <- "double"
rownames(cell_cycle) <- paste0(substr(data_cell_cycle$Cell_cycle[idx], 1, 2), "_", idx)
precise_transform() — explicit typed conversion
precise_dist() can return a mix of distances and
similarities. Some metrics are inherently similarities:
d <- precise_dist(cell_cycle, dists = c("euclidean", "cosine"), verbose = FALSE)
d[, c("distance", "type")]
#> # A tibble: 2 × 2
#> distance type
#> <chr> <chr>
#> 1 euclidean distance
#> 2 cosine similarityDistance is the package’s canonical comparison space, so
precise_transform(to = "distance") coerces every row to a
distance using a defensible per-type rule (for example a
cosine similarity s becomes
sqrt(2 * (1 - s))), asserting a zero diagonal as it
goes:
d <- precise_transform(d, to = "distance")
unique(d$type)
#> [1] "distance"Two things are deliberate here:
-
to = "similarity"is explicit. A distancedbecomes1 / (1 + d), while a native similarity remains unchanged. This is a practical change of representation, not a claim that conversion is lossless:precise_transform(d, to = "similarity") #> # A tibble: 2 × 6 #> distance metric matrix type time_taken_seconds history #> <chr> <chr> <list> <chr> <dbl> <list> #> 1 euclidean euclidean <dbl [15 × 15]> similarity 0.00300 <list [1]> #> 2 cosine cosine <dbl [15 × 15]> similarity 0.001000 <list [2]> Coercion never guesses. If a metric has no defensible distance conversion,
precise_transform()errors and names the offenders rather than fabricating a number.pd_convertible_to_distance()lists what can be converted.
normalize = "range01" (symmetry-preserving rescale to
[0, 1]) and diagonal = (write a fixed
diagonal) are optional and always run after coercion:
dn <- precise_transform(d, to = "distance", normalize = "range01")
range(dn$matrix[[1]])
#> [1] 0 1
precise_graph() — typed matrix to graph
precise_graph() projects a typed matrix into a
graph-derived similarity matrix with a zero diagonal. Which edges
survive is decided by the declared type — for a
distance the smallest pairwise values are the strongest links;
for a similarity the largest — never by inspecting the values
themselves.
There are four graph constructors:
g <- precise_graph(
d,
methods = c("knn", "mutual_knn", "threshold", "quantile_threshold"),
params = list(
knn = list(k = 3), # each node keeps its k strongest links (union)
mutual_knn = list(k = 3), # keep an edge only if mutual (intersection)
threshold = list(threshold = 0.5), # keep links past a cutoff
quantile_threshold = list(prop = 0.2) # keep the strongest prop of all pairs
),
verbose = FALSE
)
g[, c("distance", "graph_method", "type")]
#> # A tibble: 8 × 3
#> distance graph_method type
#> <chr> <chr> <chr>
#> 1 euclidean__knn knn similarity
#> 2 euclidean__mutual_knn mutual_knn similarity
#> 3 euclidean__threshold threshold similarity
#> 4 euclidean__quantile_threshold quantile_threshold similarity
#> 5 cosine__knn knn similarity
#> 6 cosine__mutual_knn mutual_knn similarity
#> 7 cosine__threshold threshold similarity
#> 8 cosine__quantile_threshold quantile_threshold similarityThe output is a typed tibble (type = "similarity" — an
adjacency is a similarity), one row per input matrix per method, ready
for precise_viz(views = "graph_layout"),
precise_graphml(), or further fusion.
mutual_knn reports the number of connected components in
its meta, which is a quick read on how fragmented the graph
is.
There are also graph normalizers for similarity/adjacency input.
laplacian returns the normalized adjacency
D^(-1/2) W D^(-1/2), and chua returns a
neighbourhood-overlap normalization based on the nonzero support of the
graph. These are weighted graph similarities, not new distances.
gn <- precise_graph(
g[g$graph_method == "knn", ],
methods = c("laplacian", "chua"),
verbose = FALSE
)
gn[, c("distance", "graph_method", "type")]
#> # A tibble: 4 × 3
#> distance graph_method type
#> <chr> <chr> <chr>
#> 1 euclidean__knn__laplacian laplacian similarity
#> 2 euclidean__knn__chua chua similarity
#> 3 cosine__knn__laplacian laplacian similarity
#> 4 cosine__knn__chua chua similarity
gn$meta[[1]]
#> $edges
#> [1] 31
#>
#> $components
#> [1] 1
#>
#> $normalization
#> [1] "D^(-1/2) W D^(-1/2)"
#>
#> $zero_degree_nodes
#> [1] 0