Align a Per-URL Prior to a PageRank Vertex Set (TIPR)
Source:R/align_prior_to_vertices.R
align_prior_to_vertices.RdBuilds a personalization / teleport vector for
igraph::page_rank(personalized = ) from a per-URL external-authority
prior (e.g. Ahrefs referring domains), aligned to the final graph
vertex set. This is the core of TIPR ("topic/true internal PageRank"),
where the random surfer's teleport mass is distributed in proportion to
external authority instead of uniformly.
The prior URLs are expected to already share the vertex namespace (i.e.
canonicalized with the same rurl settings and folded through the
same redirect map as the edges). [pagerank()] performs that
canonicalization and redirect-fold before calling this function; call it
directly only when your prior URLs already match vertex_names.
Arguments
- vertex_names
Character vector of the graph's vertex names, in graph order (typically
igraph::V(graph)$name).- prior_df
A data frame with one row per URL carrying a raw authority weight (e.g. referring-domain counts). Multiple rows for the same URL are summed (raw counts are additive — summing happens before any transform).
- prior_url_col
Name of the URL column in
prior_df. Default"url".- prior_weight_col
Name of the numeric weight column in
prior_df. Default"weight". Contract: this must be an additive raw count. URLs that coalesce (duplicate rows here, redirect variants folded upstream by [pagerank()]) are combined by summation, which is only meaningful for counts — quantities that genuinely add when two URLs merge. This makes the prior source-agnostic: referring domains (the default metric), links-to-target, or dofollow-only referring domains from Ahrefs are interchangeable swaps, as are equivalent counts from other sources (SEMrush, GA4 entrances, …). Do not pass a calculated authority score (Ahrefs UR / DR, or any 0–100 rating): scores do not sum across redirect variants (the correct fold would bemax), and a per-URL score such as UR is itself a PageRank-flavored metric, so using it as the teleport prior for PageRank is circular.- transform
Character, how to shape the raw authority before it becomes teleport mass. Passed to [transform_weights()]; one of
"none"(default, faithful linear share),"log","percentile","minmax","zipf","rank_linear". The transform is applied only to vertices that actually carry authority; vertices with no prior contribute zero to the authority component.- alpha
Numeric in
[0, 1], the mixture weight between a uniform teleport and the authority-weighted teleport:p = alpha * uniform + (1 - alpha) * authority_share.alpha = 0(default) is pure authority teleport (pages with no external authority get no teleport mass, though they still receive rank via inlinks);alpha = 1reproduces standard uniform PageRank.alphais a smoothing knob, not a dead-node mechanism — isolate/self-loop handling owns dead nodes.- exclude_nodes
Character vector of vertex names that must receive zero teleport in both components (e.g. the synthetic
"__pr_nofollow_sink__"). Real pages — including robots-blocked or 404 self-loop nodes — should not be excluded.- verbose
Logical, whether to emit coverage diagnostics via
message()(vertices receiving authority, unmatched prior URLs and their dropped weight, and the realized uniform mass fraction). DefaultTRUE.
Value
A numeric vector the same length as vertex_names, in the same
order, summing to 1 (suitable for
igraph::page_rank(personalized = )).
Excluded vertices get exactly 0. If the prior matches no vertex and
alpha = 0, the function falls back to a uniform vector over the
non-excluded vertices and warns.
Details
Alignment proceeds as: sum raw weights per URL -> match onto
vertex_names (unmatched vertices get raw 0) -> apply transform
to the vertices that carry authority -> normalize to an authority share ->
mix with a uniform-over-real-vertices vector via alpha -> normalize to
sum 1. Because igraph re-normalizes the personalization vector
internally, only the relative weights matter; normalization here is
for interpretability and to make alpha and exclude_nodes behave
predictably.
Examples
v <- c("https://x/a", "https://x/b", "https://x/c", "__pr_nofollow_sink__")
prior <- data.frame(
url = c("https://x/a", "https://x/b"),
weight = c(900, 100)
)
# Pure linear authority share; sink excluded
align_prior_to_vertices(v, prior,
exclude_nodes = "__pr_nofollow_sink__",
verbose = FALSE
)
#> [1] 0.9 0.1 0.0 0.0
# Compress the dynamic range
align_prior_to_vertices(v, prior,
transform = "log",
exclude_nodes = "__pr_nofollow_sink__", verbose = FALSE
)
#> [1] 0.5958252 0.4041748 0.0000000 0.0000000
# Authority-tilted uniform (every real page keeps a baseline)
align_prior_to_vertices(v, prior,
alpha = 0.15,
exclude_nodes = "__pr_nofollow_sink__", verbose = FALSE
)
#> [1] 0.815 0.135 0.050 0.000