knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(pagerankr)
The pagerankr package provides an SEO-focused toolkit for calculating,
comparing, and analyzing PageRank scores from web crawl data. It handles
the full pipeline from raw crawl exports (edge lists, redirect reports,
nofollow annotations, indexability status) to actionable insights.
The package offers:
pagerank() -- end-to-end wrapper for quick calculationsAll data manipulation uses base R. igraph powers the PageRank algorithm
and graph-based redirect resolution. rurl handles URL canonicalization.
# install.packages("devtools") devtools::install_gitlab("bart-turczynski/pagerankr")
library(pagerankr)
edges <- data.frame( from = c( "example.com/home", "example.com/about", "example.com/blog", "example.com/blog" ), to = c( "example.com/about", "example.com/home", "example.com/home", "example.com/about" ) ) pr <- pagerank(edges) print(pr)
pagerankr can score a Screaming Frog crawl directly when you export:
The package keeps those contracts separate. Internal: All is node-only and is
never treated as an edge list. The link export preserves raw observations, but
only Type == "Hyperlink" rows with valid source and destination become graph
edges. Resource, canonical, hreflang, and other link-row types stay available
through import diagnostics.
bundle <- screaming_frog_bundle( internal = "internal_all.csv", links = "all_outlinks.csv", link_export_kind = "all_outlinks" ) pr <- pagerank_screaming_frog(bundle) summary(bundle) attr(pr, "screaming_frog_import") attr(pr, "transition_audit")
By default, all graph-eligible Hyperlink edges are scored. HTML/rendered origin filtering, placement selection, and placement-derived weights are opt-in scoring policy:
pagerank_screaming_frog( bundle, accepted_placements = c("nav", "content"), link_origins = c("html", "html_rendered"), placement_weights = c(nav = 2, content = 1) )
Placement is not a Screaming Frog concept: accepted_placements and
placement_weights are pagerank() arguments, and the wrapper only supplies
the bundle's normalized placement column. Any crawler that reports link regions
can drive the same weighting by naming its own column, using the shared
vocabulary "content", "nav", "header", "footer", "aside":
pagerank( edges, placement_col = "region", placement_weights = c( content = 1, nav = 0.1, header = 0.1, footer = 0.1, aside = 0.1 ) )
Note that regions are downweighted, not dropped. Filtering nav out changes the graph's shape — pages reachable only through nav become teleport-only — whereas a small weight leaves the topology intact and merely stops nav dominating, which on a real crawl it usually does.
For large crawls, the import adapters read only the columns used by the stable
contract. If an export is degraded, missing optional columns become typed NA
values and are reported in diagnostics; missing required columns error early.
The bundle provenance includes detected column aliases and contract version, so
production pipelines should pin the expected contract_version.
edges <- data.frame( from = c("A", "B", "C"), to = c("B", "C", "D") ) redirects <- data.frame( from = c("B", "C"), to = c("B_new", "C_new") ) # resolve_redirects() applies redirect rules to an edge list resolved <- resolve_redirects(edges, redirects) print(resolved)
Redirect chains are resolved transitively: if A redirects to B and B redirects to C, then A resolves to C.
Crawl data often has the same URL redirecting to different targets.
Use duplicate_from_policy to control the behavior:
redirects_conflict <- data.frame( from = c("old", "old", "old"), to = c("target_A", "target_B", "target_B") ) # "most_frequent" picks the most common target edges_simple <- data.frame(from = "X", to = "old") resolve_redirects(edges_simple, redirects_conflict, duplicate_from_policy = "most_frequent" )
Available policies: strict (default, errors on conflict), first_wins,
last_wins, most_frequent, prune_source, resolve_if_consistent.
Redirect cycles (A -> B -> C -> A) are detected via strongly connected
components. Use loop_handling to control behavior:
edges_loop <- data.frame(from = "X", to = "A") redirects_loop <- data.frame( from = c("A", "B", "C", "D"), to = c("B", "C", "A", "E") ) # "prune_loop" removes cycle edges; linear redirects still work resolved <- resolve_redirects(edges_loop, redirects_loop, loop_handling = "prune_loop" ) print(resolved)
Available policies: error (default), prune_loop, break_arrow
(keeps the highest in-degree node as sink).
Use resolve_links() to apply redirects and deduplicate without
computing PageRank -- useful for inspecting the resolved graph:
edges <- data.frame( from = c("A", "A", "B"), to = c("B", "B", "C") ) redirects <- data.frame(from = "B", to = "B_final") resolve_links(edges, redirects, clean_urls = FALSE)
pagerankr provides three explicit propagation policies for links marked
nofollow:
"evaporate" (the default) keeps each nofollow link as an outgoing slot.
It consumes its weighted share of the source page's budget, but that share
reaches a sink rather than the target."drop" removes nofollow links before allocating the outgoing budget. They
consume no slots, and followed links divide the full budget."keep" follows the links normally, so their targets receive their shares.These are package modeling choices; they are not claims about a search engine's current implementation.
edges <- data.frame( from = c("Hub", "Hub", "Hub"), to = c("A", "B", "C"), nofollow = c(FALSE, FALSE, TRUE) ) # "evaporate": PR splits 3 ways, C's share vanishes pr_evap <- pagerank(edges, nofollow_col = "nofollow", nofollow_action = "evaporate", clean_edge_urls = FALSE ) print(pr_evap) # "drop": nofollow edges removed, PR splits only among followed links pr_drop <- pagerank(edges, nofollow_col = "nofollow", nofollow_action = "drop", clean_edge_urls = FALSE ) print(pr_drop)
pagerankr assumes that the ranked corpus consists of indexed documents.
Under this indexed-corpus assumption, a noindex page is outside the ranked
corpus. It may receive authority through inlinks, but cannot redistribute that
authority within the indexed graph. The package therefore routes its outgoing
budget to the shared waste sink — the same mechanism used for robots-blocked
and response-dead (4xx/5xx) pages. This is independent of nofollow_action,
which governs only real rel=nofollow edges.
This is strictly a pagerankr PageRank assumption. It does not mean that
Google or another search engine defines noindex as an explicit nofollow
directive. A noindex page can remain visible in the result to make its received
authority auditable; hiding it would be an optional reporting choice, not a
change required for correct propagation under this model.
edges <- data.frame( from = c("Home", "NoindexPage", "NoindexPage"), to = c("NoindexPage", "A", "B") ) idx_status <- data.frame( url = "NoindexPage", indexability_status = "noindex" ) pr <- pagerank(edges, indexability_df = idx_status, clean_edge_urls = FALSE ) print(pr)
A robots.txt-blocked page receives inbound PageRank but cannot pass it
outbound (Google can't see the links). Its outgoing budget routes to the
shared waste sink; robots_blocked_action controls only whether the page is
shown or removed:
edges <- data.frame( from = c("Home", "Blocked", "A"), to = c("Blocked", "A", "Home") ) idx_status <- data.frame( url = "Blocked", indexability_status = "Blocked by robots.txt" ) # "show" (default): blocked page is shown with the authority it collects pr_show <- pagerank(edges, indexability_df = idx_status, robots_blocked_action = "show", clean_edge_urls = FALSE ) print(pr_show) # "vanish": blocked page removed from results (its own mass booked as hidden) pr_vanish <- pagerank(edges, indexability_df = idx_status, robots_blocked_action = "vanish", clean_edge_urls = FALSE ) print(pr_vanish)
The page_state column (present whenever indexability_df or status_df is
supplied) tags each visible page as live, noindex, robots_blocked, or
response_dead, so the wasted-mass class is attributable per URL.
Pass a weight_col to use link weights (e.g., number of links, link
position scores) in the PageRank calculation:
edges <- data.frame( from = c("A", "A", "B"), to = c("B", "C", "C"), weight = c(3, 1, 1) ) pr <- pagerank(edges, weight_col = "weight", clean_edge_urls = FALSE) print(pr)
Duplicate from -> to rows are a separate modeling choice. By default,
pagerank() uses duplicate_edge_policy = "collapse": repeated links from one
source page to the same destination become one binary destination edge, matching
the common textbook PageRank convention and preserving legacy results. Use
duplicate_edge_policy = "aggregate" when duplicate rows carry additive
weights that should be summed, or "count_instances" when each repeated link
slot should increase the transition probability to that target.
filter_links_by_domain() scopes edge lists by domain, useful for
separating internal vs. external links:
edges <- data.frame( from = c( "example.com/a", "example.com/b", "other.com/c" ), to = c( "example.com/b", "other.com/d", "example.com/a" ) ) # Keep only internal links internal <- filter_links_by_domain(edges, keep_domains = "example.com") print(internal)
hits() computes Kleinberg's HITS hub and authority scores over the same
cleaned, redirect/canonical-folded, domain-filtered, deduplicated link graph as
pagerank(). Because both share the identity pipeline, you can join hub,
authority, and PageRank on node_name directly.
The two scores reinforce each other: a good authority is pointed to by good
hubs, and a good hub points to good authorities. Formally, with adjacency
matrix A, authority is the dominant eigenvector of A^T A and hub is the
dominant eigenvector of A A^T.
edges <- data.frame( from = c("A", "A", "B"), to = c("B", "C", "C") ) # A only points out (pure hub); C is only pointed to (pure authority). hits(edges, clean_edge_urls = FALSE)
Scores are scaled so each maximum is 1 (scale = TRUE, the conventional HITS
reporting). HITS already captures both directions of authority flow, so there
is no reverse flag, and the PageRank-only forward-flow devices (nofollow
evaporation, indexability transforms, the TIPR teleport prior) are not exposed.
Whole-graph caveat. Kleinberg's original HITS ran on a small, query-focused
base set of pages. hits() runs on the full (or domain-filtered) site graph
pagerankr assembles, so treat the scores as site-wide structural
centralities rather than query-relevance scores.
salsa() computes Lempel & Moran's (2001) SALSA hub and authority scores over
the same identity pipeline as pagerank() and hits(), so all three join on
node_name. SALSA is the stochastic cousin of HITS: it replaces the
mutual-reinforcement iteration with two random walks on the bipartite
hub/authority graph, so the scores are stationary distributions rather than the
dominant eigenvectors HITS returns. On a connected graph this collapses to a
degree-based closed form — authority = d_in / W, hub = d_out / W, where W
is the edge count — so no eigenvector iteration is needed.
edges <- data.frame( from = c("A", "A", "B"), to = c("B", "C", "C") ) # A only points out (pure hub); C is only pointed to (pure authority). salsa(edges, clean_edge_urls = FALSE)
Each side is a probability distribution that sums to 1. When the graph splits
into several weakly connected components, each component's scores are
renormalized within the component and then reweighted by that component's share
of the side (Lempel & Moran 2001, Proposition 6) — required so scores stay
comparable across orphan page clusters.
Coverage differs from PageRank. The hub side holds only pages with outlinks
and the authority side only pages with inlinks, so hub is NA for a pure
sink and authority is NA for a pure source (an isolate is NA for both).
v1 is unweighted; like hits() the forward-flow devices (nofollow,
indexability, the TIPR prior, reverse) are not exposed. As with hits(),
these are site-wide structural centralities, a documented site-graph adaptation
of the original focused-subgraph algorithm.
edges <- data.frame( from = c("A", "B", "C", "D"), to = c("B", "C", "D", "A") ) pr_85 <- pagerank(edges, damping = 0.85, clean_edge_urls = FALSE) pr_90 <- pagerank(edges, damping = 0.90, clean_edge_urls = FALSE) diff <- compare_pagerank(pr_85, pr_90, label_a = "d=0.85", label_b = "d=0.90") print(diff) cat("\nCorrelation summary:\n") print(attr(diff, "summary"))
Run PageRank across multiple parameter combinations to find the most informative model:
edges <- data.frame( from = c("A", "A", "B", "C"), to = c("B", "C", "C", "A") ) grid <- auto_grid(damping = c(0.75, 0.85, 0.95)) results <- pagerank_grid(edges, params_grid = grid, clean_edge_urls = FALSE) # Analyze distribution metrics across the grid analysis <- analyze_pagerank_grid(results) print(analysis)
Standalone metrics for any PageRank vector:
pr <- pagerank(edges, clean_edge_urls = FALSE) cat("Gini coefficient:", pr_gini(pr$pagerank), "\n") cat("Entropy:", pr_entropy(pr$pagerank), "\n") cat("Top-1 share:", pr_top_k_share(pr$pagerank, k = 1), "\n")
A common SEO workflow is asking "what happens to PageRank if I add these links, remove those links, or implement these redirects?"
site_links <- data.frame( from = c("Home", "Home", "About", "Blog"), to = c("About", "Blog", "Home", "Home") ) impact <- simulate_changes( site_links, add_links_df = data.frame( from = "Blog", to = "About" ), clean_edge_urls = FALSE ) print(impact)
impact_remove <- simulate_changes( site_links, remove_links_df = data.frame( from = "Home", to = "Blog" ), clean_edge_urls = FALSE ) print(impact_remove)
redirect_urls_df models a URL-level change: retiring a live page behind a
redirect. It uses retire semantics — the source's own outbound links are
stripped before folding (an honest 301 has no body), so the target inherits the
source's inbound authority only, never its outlinks. The source then leaves
the proposed graph. A row for a source also overrides any existing redirect
for it, so repointing an old redirect is a single change.
# OldPage is live: Blog links to it, and it links out to Home. Retire it # behind a 301 to About. Its inbound authority (from Blog) passes to About; # its outlink to Home is dropped; OldPage leaves the proposed graph. extended_links <- rbind(site_links, data.frame( from = c("Blog", "OldPage"), to = c("OldPage", "Home") )) impact_redirect <- simulate_changes( extended_links, redirect_urls_df = data.frame( from = "OldPage", to = "About" ), clean_edge_urls = FALSE ) print(impact_redirect)
The output carries a node_status column ("normal", or "new-target" for a
page the changeset introduces) and a change manifest attribute describing what
was applied:
attr(impact_redirect, "manifest")$redirects_applied
Screaming Frog users get the same verbs through
simulate_changes_screaming_frog(bundle, ...), which reuses the bundle's
placement, nofollow, redirect, and canonical handling automatically.
impact_all <- simulate_changes( site_links, add_links_df = data.frame( from = "Blog", to = "About" ), remove_links_df = data.frame( from = "Home", to = "Blog" ), clean_edge_urls = FALSE ) print(impact_all)
All pagerank() parameters (damping, nofollow, indexability, etc.) can
be passed through simulate_changes() via ....
pagerank() delegates to igraph's PageRank implementation. Two solvers are
available via the algo argument:
"prpack" (default) — exact solver; ignores eps and niter."arpack" — iterative Arnoldi solver; required whenever you supply eps or
niter. Passing either argument automatically switches the solver.Key arguments:
| Argument | Meaning |
|---|---|
| algo | "prpack" (default) or "arpack" |
| eps | L1 convergence tolerance (maps to ARPACK options$tol) |
| niter | Max iterations (maps to ARPACK options$maxiter) |
Every result carries a "convergence" attribute — a pagerank_convergence
object — that records the solver used, iteration count, and final L1 residual.
Inspect it with attr(result, "convergence") or just print() the result.
Rule of thumb for iteration budget: ceiling(log10(eps) / log10(damping))
iterations are needed to reach tolerance eps at damping factor damping.
damping_sensitivity() sweeps a vector of damping factors and returns one
PageRank result per alpha:
damping_sensitivity(edge_list_df, alphas = c(0.75, 0.80, 0.85, 0.90, 0.95), ...)
pagerank_stability() wraps the sweep into a ready-to-read stability report:
pagerank_stability( edge_list_df, alphas = c(0.75, 0.80, 0.85, 0.90, 0.95), reference = 0.85, top_k = 10, ... )
It returns a data frame with one row per alpha and two comparison columns relative to the reference run:
spearman_rho — rank-order correlation across all URLs.top_k_overlap — fraction of the top-k URLs shared with the reference.The raw per-(url, alpha) scores are available via attr(stab, "sensitivity").
toy_edges <- data.frame( from = c("A", "A", "B", "C", "C", "D", "E", "F"), to = c("B", "C", "D", "D", "E", "F", "A", "A") ) # Stability report: how sensitive are ranks to damping? stab <- pagerank_stability( toy_edges, alphas = c(0.75, 0.80, 0.85, 0.90, 0.95), reference = 0.85, top_k = 4, clean_edge_urls = FALSE ) print(stab) # Drill into raw per-(url, alpha) scores head(attr(stab, "sensitivity"))
At alpha values close to the reference the spearman_rho and top_k_overlap
columns will be near 1; larger deviations flag URLs whose importance is
damping-sensitive.
Standard PageRank treats every page as an equally likely teleport target. Topic-Sensitive PageRank (Haveliwala 2002, adapted here to a site graph) replaces that uniform prior with a cluster-biased one: the random surfer teleports only to pages that belong to a predefined topic cluster. The result is a family of per-topic authority scores — each score answers "how important is this page for visitors who care about topic X?" — plus a blended aggregate that weight-averages the individual topic vectors.
topic_sensitive_pagerank() is pure orchestration: it runs pagerank()
once per topic with the appropriate seeded prior, then blends the results.
Topic membership is supplied by the caller; it is never inferred from
content.
Signature
topic_sensitive_pagerank( edge_list_df, topics, topic_weights = NULL, topic_url_col = "url", topic_weight_col = "weight", ... )
topics is a named list. Each element is either a character vector of
seed URLs (equal-weight prior) or a data frame with URL and weight columns
(custom prior).
library(pagerankr) # Small synthetic site: 7 pages, two rough topic clusters edges <- data.frame( from = c("Home", "Home", "Blog", "Blog", "Shop", "Shop", "Docs"), to = c("Blog", "Shop", "Post1", "Post2", "Item1", "Item2", "Guide1") ) # Define two topics as named lists of seed URL vectors topics <- list( content = c("Blog", "Post1", "Post2"), commerce = c("Shop", "Item1", "Item2") ) tspr <- topic_sensitive_pagerank( edge_list_df = edges, topics = topics, clean_edge_urls = FALSE ) print(tspr)
The returned data frame has one row per node and three score columns:
content, commerce, and blended.
# Per-topic scores tspr[, c("node_name", "content", "commerce")] # Blended (equal topic weights by default) tspr[, c("node_name", "blended")]
topic_feeder_pagerank() answers the complementary question: which
pages outside a cluster send the most PageRank authority into it?
It operates on the reverse graph and seeds the teleport prior on the cluster
itself. Use it when you want to find upstream link-building opportunities.
See vignette("topic_feeder_pagerank") for a full walkthrough.
trustrank() is the unipolar variant: a single "trusted" seed set
replaces the topic list, and there is no blending step. See
vignette("trustrank").
GA4 event data captures how real users move through your site. The three functions in this section let you convert those signals into PageRank inputs without requiring a BigQuery connection — a synthetic events data frame is sufficient for local development.
ga4_page_transitions()ga4_page_transitions() walks each user-session in chronological order and
emits one row per consecutive page-view pair. The result is a transition
signal (observed navigation behavior), not a link-click signal, so it
reflects where users actually go rather than where crawlable anchor tags point.
library(pagerankr) # Minimal synthetic GA4 events data frame (no BigQuery required) events <- data.frame( user_pseudo_id = c("u1", "u1", "u1", "u2", "u2"), ga_session_id = c(1L, 1L, 1L, 2L, 2L), page_location = c("/home", "/blog", "/contact", "/home", "/pricing"), event_timestamp = c(1000L, 2000L, 3000L, 1000L, 2000L) ) transitions <- ga4_page_transitions( events, user_id_col = "user_pseudo_id", session_id_col = "ga_session_id", page_col = "page_location", timestamp_col = "event_timestamp" ) print(transitions) # Returns a data frame with columns: from, to, n
smooth_transitions()Observed transition counts are often sparse — many source pages have very few
sessions, making raw empirical shares unreliable. smooth_transitions() applies
per-source Dirichlet smoothing toward a structural (crawl-graph) prior:
lambda_i = n_i / (n_i + k)
where n_i is the total outgoing transition count from source i and k
controls how quickly low-traffic sources lean on the prior. Sources with fewer
than min_support total outgoing transitions receive full prior weight
(lambda_i = 0).
# Build a minimal structural prior from the same pages structural <- data.frame( from = c("/home", "/home", "/blog"), to = c("/blog", "/pricing", "/contact"), n = c(50L, 30L, 40L) ) smoothed <- smooth_transitions( empirical_df = transitions, structural_df = structural, k = 10, min_support = 5, count_col = "n", from_col = "from", to_col = "to", prob_col = "prob" ) print(smoothed) # empirical_df augmented with a `prob` column: smoothed per-source # transition probabilities (each source sums to 1)
ga4_entrance_teleport()ga4_entrance_teleport() turns landing-page entrance counts into a prior_df
suitable for pagerank(prior_df = ...). This provides a proxy for external
authority: pages users enter from search or direct traffic receive a higher
reset probability during the random-surfer teleport step.
Note: entrance counts are not equivalent to backlink authority — they reflect observed user entry points, not link equity from external domains.
entrances <- data.frame( page_location = c("/home", "/blog", "/pricing", "/contact"), entrances = c(120L, 80L, 40L, 10L) ) prior <- ga4_entrance_teleport( entrances_df = entrances, url_col = "page_location", entrances_col = "entrances", vertex_names = NULL ) print(prior) # Returns: url + weight data frame, ready for pagerank(prior_df = .)
Combine all three steps: raw events → transition counts → smoothing → PageRank.
# 1. Extract transitions from synthetic events trans <- ga4_page_transitions( events, user_id_col = "user_pseudo_id", session_id_col = "ga_session_id", page_col = "page_location", timestamp_col = "event_timestamp" ) # 2. Smooth against the structural prior sm <- smooth_transitions( empirical_df = trans, structural_df = structural, k = 10, min_support = 2, prob_col = "prob" ) # 3. Run PageRank using smoothed probabilities as edge weights pr <- pagerank( sm, edge_from_col = "from", edge_to_col = "to", weight_col = "prob", clean_edge_urls = FALSE ) print(pr)
clean_edge_urls = FALSE is used throughout because the synthetic URLs are
already normalized; set it to TRUE when feeding real GA4 page_location
values that may contain query strings or fragments.
| Function | Purpose |
|---|---|
| pagerank() | End-to-end: clean, resolve, compute |
| hits() | End-to-end HITS hub + authority on the same graph |
| compute_hits() | Low-level igraph HITS wrapper |
| salsa() | End-to-end SALSA hub + authority on the same graph |
| compute_salsa() | Low-level SALSA computational core |
| resolve_links() | Resolve redirects + deduplicate (no PR) |
| simulate_changes() | What-if: compare baseline vs. proposed |
| compare_pagerank() | Diff two PR results with rank shifts |
| pagerank_grid() | Run PR across parameter combinations |
| auto_grid() | Generate parameter grid |
| analyze_pagerank_grid() | Distribution metrics across a grid |
| filter_links_by_domain() | Scope edges by domain |
| resolve_redirects() | Apply redirects to an edge list |
| clean_url_columns() | Canonicalize URLs |
| get_unique_edges() | Deduplicate, handle self-loops |
| drop_isolates() | Include/exclude disconnected nodes |
| compute_pagerank() | Low-level igraph PR wrapper |
| pr_gini() | Gini coefficient of PR distribution |
| pr_entropy() | Entropy of PR distribution |
| pr_top_k_share() | Top-k concentration of PR |
| damping_sensitivity() | Sweep PageRank across a range of damping factors |
| pagerank_stability() | Alpha-stability report: rank correlation across damping grid |
| topic_sensitive_pagerank() | Per-topic personalized PageRank with blended scores |
| topic_feeder_pagerank() | Reverse-graph seeded PageRank: find pages that feed a target cluster |
| trustrank() | TrustRank: seed-biased PageRank from a trusted seed set |
| seed_prior() | Build a teleport prior concentrated on seed URLs (shared by trustrank and topic_feeder_pagerank) |
| align_prior_to_vertices() | Align a prior/teleport weight data frame to the graph vertex set |
| ga4_entrance_teleport() | Entrance/landing-page counts as a teleport (reset) vector |
| ga4_page_transitions() | Consecutive-page-view transition counts from a GA4 BigQuery export |
| smooth_transitions() | Shrink sparse empirical transition shares toward structural prior |
| transform_edge_weights() | Per-source grouped edge weight transforms (emits transition_probability) |
| transform_weights() | Apply rank/log/zipf/percentile transforms to a numeric vector |
| validate_edge_weights() | Validate per-source weight totals and warn on anomalies |
| screaming_frog_bundle() | Compose Screaming Frog Internal:All + All Inlinks/Outlinks into a bundle |
| screaming_frog_internal() | Import Screaming Frog Internal: All export |
| screaming_frog_links() | Import Screaming Frog All Inlinks / All Outlinks export |
| pagerank_screaming_frog() | Score a screaming_frog_bundle via pagerank() |
| resolve_canonical_urls() | Resolve a URL vector through rel=canonical folds |
| resolve_canonicals() | Apply rel=canonical folds to an edge list |
| resolve_folded_urls() | Resolve a URL vector through redirects plus canonicals |
| resolve_redirect_urls() | Resolve a URL vector through a redirect map |
| build_fold_map() | Build a URL fold map from redirects and/or canonicals |
| audit_canonicals() | Diagnose rel=canonical fold coverage and conflicts |
| audit_fold() | Diagnose the redirect+canonical URL fold map |
| audit_redirects() | Diagnose redirect chains, loops, and conflicts |
| aggregate_edges() | Aggregate duplicate edges after URL folding with per-column semantics |
| export_graph() | Export graph + PageRank in graphml / dot / edgelist / pajek formats |
| launch_pagerank_explorer() | Launch the interactive Shiny PageRank explorer |
pagerankr wraps igraph's PageRank and
HITS engines and implements a body of link-analysis research directly — PageRank
(Brin & Page), HITS (Kleinberg), SALSA (Lempel & Moran), TrustRank (Gyöngyi,
Garcia-Molina & Pedersen), and Topic-Sensitive PageRank (Haveliwala). URL
handling is delegated to the sibling rurl package.
The full list of credits — prior art, dependencies, the research this code
implements, and the data sources it serves — is in
ACKNOWLEDGMENTS.md.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.