Simulate the PageRank Impact of Link and Redirect Changes
Source:R/simulate_changes.R
simulate_changes.RdCompares PageRank before and after proposed changes to the link graph, at both the edge level (adding/removing links) and the URL level (retiring a page behind a redirect, or repointing an existing redirect). The whole graph is recomputed and a before/after table is returned; interpretation is left to the caller. This is a faithful recompute primitive, not a ranking or target-optimization engine.
Usage
simulate_changes(
edge_list_df,
add_links_df = NULL,
remove_links_df = NULL,
redirect_urls_df = NULL,
remove_urls = NULL,
redirects_df = NULL,
on_unknown_target = c("warn", "error", "allow"),
...,
edge_from_col = "from",
edge_to_col = "to",
redirect_from_col = "from",
redirect_to_col = "to",
label_baseline = "baseline",
label_proposed = "proposed"
)Arguments
- edge_list_df
A data frame representing the current link edge list.
- add_links_df
Optional data frame of links to add. Must have the same from/to column names as
edge_list_df. Columns present inedge_list_dfbut absent here are padded withNAon the added rows, so weighted / annotated edge lists keep their schema. DefaultNULL.- remove_links_df
Optional data frame of links to remove. Matching is by exact from+to pair. Must have the same from/to column names as
edge_list_df. DefaultNULL.- redirect_urls_df
Optional two-column
from/todata frame of URL-level redirects to model. Each row retires thefromURL and sends its inbound authority totoat 100% pass-through. Retire semantics: the live 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. A row for sourceAoverrides any prior redirect forA– whether from an earlier row or from the baseline crawl's real 3xx – so "change A into a redirect to C" is a single override. A duplicate source mapping to two distinct targets in one changeset is an error (strict). DefaultNULL.- remove_urls
Optional character vector of URLs to model as removed (turned into HTTP 404s). Each removed URL keeps its inbound links – other pages still point at it – but now they flow into a dead page: authority arrives and evaporates to the shared waste sink rather than redistributing across the site (dangle) or self-amplifying (self-loop). The page's own outbound links are dropped. The node stays in the output holding the mass it absorbed once, flagged
"removed-dead"innode_statusso its residual score is never misread as earned authority. Under the hood this forces astatus_dfentry (HTTP404) into the proposed model only; 4xx and 5xx are one class (no split). A URL appearing in bothremove_urlsandredirect_urls_dfis an error (a page cannot be both a 301 and a 404). To also model cleaning up the inbound links, compose withremove_links_df. DefaultNULL.- redirects_df
Optional data frame of existing redirects (baseline). Default
NULL.- on_unknown_target
How to treat a redirect or link target that is not a node in the current graph (it may be a legitimate new page, modeled as a new node that carries inbound authority with no outlinks yet). One of
"warn"(default, warn and proceed),"error", or"allow"(proceed silently).- ...
Additional arguments passed to both
pagerank()calls (e.g.,clean_edge_urls,damping,nofollow_col,indexability_df, etc.).- edge_from_col
Name of the from column in edge list data frames. Default
"from".- edge_to_col
Name of the to column in edge list data frames. Default
"to".- redirect_from_col
Name of the source column in
redirect_urls_dfandredirects_df. Default"from".- redirect_to_col
Name of the target column in
redirect_urls_dfandredirects_df. Default"to".- label_baseline
Label for the baseline model in the comparison output. Default
"baseline".- label_proposed
Label for the proposed model in the comparison output. Default
"proposed".
Value
The output of compare_pagerank (per-node deltas,
percentage changes, and rank changes between baseline and proposed) with an
added node_status column: "normal" for a node present and
live in both models, "new-target" for a node introduced by the
changeset (present in the proposed model, absent from the baseline), or
"removed-dead" for a node retired via remove_urls (its
proposed score is residual absorbed mass on the way to the waste sink, not
earned authority).
node_status describes a node's role in this before/after
comparison, which is a different axis from the page_state column
pagerank attaches to describe a page's health/index
state (live / noindex / robots_blocked /
response_dead). The two are deliberately not merged into one
vocabulary: new-target has no health analogue, and
removed-dead is the single value bridging both axes — a node whose
comparison role is "removed" because its proposed health state is
response_dead (a forced 404). Attributes:
- summary
Aggregate statistics from
compare_pagerank().- proposed
The full proposed
pagerank()result, including itstransition_auditattribute, so the evaporated-mass cost of a removal is surfaced by default.- manifest
A named list describing the changeset: redirects applied, which sources overrode a prior redirect, URLs removed, link add/remove counts, and any unknown targets.
See also
simulate_changes_screaming_frog for the Screaming Frog
bundle entry point.
Examples
# Current site links
edges <- data.frame(
from = c("Home", "Home", "About", "Blog"),
to = c("About", "Blog", "Home", "Home")
)
# Propose adding a link from Blog to About
new_links <- data.frame(
from = "Blog", to = "About"
)
result <- simulate_changes(edges,
add_links_df = new_links,
clean_edge_urls = FALSE
)
print(result)
#> node_name pagerank_baseline pagerank_proposed delta pct_change
#> 1 About 0.2567568 0.3333333 0.07657658 29.824561
#> 2 Home 0.4864865 0.4327485 -0.05373795 -11.046134
#> 3 Blog 0.2567568 0.2339181 -0.02283863 -8.895045
#> rank_baseline rank_proposed rank_delta node_status
#> 1 2 2 0 normal
#> 2 1 1 0 normal
#> 3 2 3 -1 normal
attr(result, "summary")
#> $spearman_rho
#> [1] 0.8660254
#>
#> $mean_abs_delta
#> [1] 0.05105105
#>
#> $nodes_gained
#> [1] 0
#>
#> $nodes_lost
#> [1] 0
#>
# Retire the About page behind a redirect to Home
retire <- data.frame(from = "About", to = "Home")
simulate_changes(edges, redirect_urls_df = retire, clean_edge_urls = FALSE)
#> node_name pagerank_baseline pagerank_proposed delta pct_change
#> 1 Blog 0.2567568 0.5 0.24324324 94.736842
#> 2 Home 0.4864865 0.5 0.01351351 2.777778
#> 3 About 0.2567568 NA NA NA
#> rank_baseline rank_proposed rank_delta node_status
#> 1 2 1 1 normal
#> 2 1 1 0 normal
#> 3 2 NA NA normal
# Model the About page 404-ing: inbound authority flows in and evaporates
simulate_changes(edges, remove_urls = "About", clean_edge_urls = FALSE)
#> node_name pagerank_baseline pagerank_proposed delta pct_change
#> 1 Home 0.4864865 0.21722114 -0.26926535 -55.34899
#> 2 About 0.2567568 0.09231898 -0.16443777 -64.04419
#> 3 Blog 0.2567568 0.16731898 -0.08943777 -34.83366
#> rank_baseline rank_proposed rank_delta node_status
#> 1 1 1 0 normal
#> 2 2 3 -1 removed-dead
#> 3 2 2 0 normal