The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

TrustRank: seed-biased PageRank in pagerankr

Bart Turczynski

2026-09-17

What TrustRank is

TrustRank (Gyöngyi, Garcia-Molina & Pedersen, 2004) is ordinary PageRank with one change: instead of the random surfer teleporting to a uniform random page, it teleports back into a set of trusted seed pages. Trust then propagates outward along links and attenuates with distance — the further a page is from the trusted core, the less trust reaches it. The PageRank damping factor is exactly that attenuation mechanism, so no extra machinery is needed.

The original motivation was web spam: pick a small set of human-vetted “good” pages, and let trust flow from them so that link farms — which good pages rarely link to — stay low. The same mechanic is useful on a single site for any “authority radiates from these known-good pages” question: your editorial hubs, your hand-picked cornerstone content, or a manually curated quality set.

This is seed-biased PageRank, not a spam classifier. pagerankr supplies the biased-propagation core; which pages are trustworthy is your call.

It reuses the personalization path — no new solver

A trusted-seed set is just a teleport (personalization) prior. pagerankr already builds personalized PageRank from a per-URL prior via pagerank(prior_df = ...) (the TIPR path). TrustRank is therefore two small helpers over that engine:

A worked example

edges <- data.frame(
  from = c("/", "/", "/hub", "/hub", "/good", "/spam", "/spam"),
  to = c("/hub", "/good", "/good", "/deep", "/hub", "/sink", "/good")
)

Here / and /hub are our trusted editorial core. /spam links into the good neighborhood (a classic spam tactic) but nothing trusted links to it, and it feeds an off-topic /sink.

Build the seed prior explicitly

prior <- seed_prior(c("/", "/hub"))
prior
#>    url weight
#> 1    /      1
#> 2 /hub      1

Equal weights reproduce TrustRank’s uniform distribution over the trusted set. Run it through pagerank() like any other prior:

pr_manual <- pagerank(edges, prior_df = prior, clean_edge_urls = FALSE)
#> TIPR prior aligned: 2/6 real vertices carry authority; transform='none', alpha=0 (uniform mass ~0.0%). 0 prior URL(s) (sum weight 0) did not fold onto any vertex and were dropped.
pr_manual[order(-pr_manual$pagerank), c("node_name", "pagerank")]
#>   node_name  pagerank
#> 4      /hub 0.4238061
#> 3     /good 0.2445263
#> 2     /deep 0.1801176
#> 1         / 0.1515500
#> 5     /sink 0.0000000
#> 6     /spam 0.0000000

Or use the convenience wrapper

tr <- trustrank(edges, c("/", "/hub"), clean_edge_urls = FALSE)
#> TIPR prior aligned: 2/6 real vertices carry authority; transform='none', alpha=0 (uniform mass ~0.0%). 0 prior URL(s) (sum weight 0) did not fold onto any vertex and were dropped.
tr[order(-tr$pagerank), c("node_name", "pagerank", "prior_weight")]
#>   node_name  pagerank prior_weight
#> 4      /hub 0.4238061          0.5
#> 3     /good 0.2445263          0.0
#> 2     /deep 0.1801176          0.0
#> 1         / 0.1515500          0.5
#> 5     /sink 0.0000000          0.0
#> 6     /spam 0.0000000          0.0

trustrank() is identical to the manual call — it just builds the prior for you. Two things to read in the output:

Compare against uniform PageRank

uni <- pagerank(edges, clean_edge_urls = FALSE)
compare_pagerank(uni, tr)[, c("node_name", "pagerank_a", "pagerank_b", "delta")]
#>   node_name pagerank_a pagerank_b       delta
#> 1      /hub 0.31375833  0.4238061  0.11004777
#> 2     /sink 0.09527563  0.0000000 -0.09527563
#> 3         / 0.06686009  0.1515500  0.08468988
#> 4     /spam 0.06686009  0.0000000 -0.06686009
#> 5     /deep 0.20020738  0.1801176 -0.02008979
#> 6     /good 0.25703846  0.2445263 -0.01251213

pagerank_a is uniform PageRank, pagerank_b is TrustRank. The trusted core and what it links to gain; the untrusted region loses.

Graded trust and a teleport floor

Trust need not be all-or-nothing. Give some seeds more weight than others:

trustrank(
  edges,
  data.frame(url = c("/", "/hub"), weight = c(3, 1)),
  clean_edge_urls = FALSE
)[, c("node_name", "pagerank", "prior_weight")]
#> TIPR prior aligned: 2/6 real vertices carry authority; transform='none', alpha=0 (uniform mass ~0.0%). 0 prior URL(s) (sum weight 0) did not fold onto any vertex and were dropped.
#>   node_name  pagerank prior_weight
#> 1         / 0.2142415         0.75
#> 2     /deep 0.1595946         0.00
#> 3     /good 0.2506472         0.00
#> 4      /hub 0.3755166         0.25
#> 5     /sink 0.0000000         0.00
#> 6     /spam 0.0000000         0.00

By default untrusted, seed-unreachable pages receive no teleport mass at all. To give every page a small floor (a blend of trust teleport and uniform teleport), pass prior_alpha:

trustrank(
  edges, c("/", "/hub"),
  prior_alpha = 0.15, clean_edge_urls = FALSE
)[, c("node_name", "pagerank", "prior_weight")]
#> TIPR prior aligned: 2/6 real vertices carry authority; transform='none', alpha=0.15 (uniform mass ~15.0%). 0 prior URL(s) (sum weight 0) did not fold onto any vertex and were dropped.
#>   node_name    pagerank prior_weight
#> 1         / 0.141586436        0.450
#> 2     /deep 0.182481103        0.025
#> 3     /good 0.245998351        0.025
#> 4      /hub 0.410859270        0.450
#> 5     /sink 0.011208926        0.025
#> 6     /spam 0.007865913        0.025

prior_alpha = 0 (the default) is pure trust teleport; prior_alpha = 1 reproduces uniform PageRank.

Notes

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.