Skip to contents

Runs [pagerank()] at each damping factor \(\alpha\) in `alphas` and returns a tidy data frame of per-URL scores alongside the convergence metadata for each solve. This makes the sensitivity of the ranking to \(\alpha\) directly inspectable on *your* graph, rather than relying on the field default of `0.85` (see the "Damping factor" section of [pagerank()] for why that default is only an empirical convention).

Usage

damping_sensitivity(edge_list_df, alphas = c(0.75, 0.8, 0.85, 0.9, 0.95), ...)

Arguments

edge_list_df

A data frame representing the edge list, passed to every [pagerank()] call. (Named for consistency with the rest of the package; it is an edge list, not a constructed graph object.)

alphas

Numeric vector of damping factors to sweep, each strictly between 0 and 1. Default `c(0.75, 0.80, 0.85, 0.90, 0.95)`. Duplicate values are dropped.

...

Additional arguments forwarded to [pagerank()] (e.g. `redirects_df`, `weight_col`, `algo`, `eps`, `niter`, `prior_df`). Passing `damping` here is an error, since `alphas` is what drives the damping factor.

Value

A tidy data frame with one row per (URL, \(\alpha\)) pair, sorted by `alpha` ascending then `score` descending, with columns:

`url`

Node / page identifier.

`alpha`

The damping factor used for this solve.

`score`

The page's PageRank score at this `alpha`.

`iters`

Iterations the solver used (ARPACK only; `NA` under PRPACK).

`iters_estimate`

Power-iteration iteration-count estimate at the convergence tolerance (solver-independent).

`residual`

Post-hoc L1 residual \(\|G x - x\|_1\) of the solve.

`converged`

Whether the residual met the tolerance.

A `"convergence"` attribute is attached: a compact one-row-per-`alpha` data frame (`alpha`, `algo`, `iters`, `iters_estimate`, `residual`, `tol`, `converged`, `n_nodes`) summarizing each solve.

Details

The helper is the empirical companion to the closed-form \(\alpha\)-derivative analysis of Boldi, Santini & Vigna (PageRank as a Function of the Damping Factor, WWW 2005): instead of differentiating the PageRank vector with respect to \(\alpha\) analytically, it samples the vector at a grid of \(\alpha\) values so you can see how much each page's score (and the overall ranking) actually moves. Pair it with [compare_pagerank()] to quantify the rank churn between any two \(\alpha\) values.

Each row also carries the convergence metadata for that \(\alpha\)'s solve. The empirical `iters` count is only reported by the ARPACK solver; under the default PRPACK direct solver it is `NA` (PRPACK exposes no iteration count). To populate it, forward `algo = "arpack"` (or an `eps` / `niter` control) through `...`. The solver-independent `iters_estimate` column is always populated: it is the power-iteration rule of thumb \(\lceil \log_{10}(\tau) / \log_{10}(\alpha) \rceil\) (Langville & Meyer, 2004) at the convergence tolerance \(\tau\), and shows how the required iteration count climbs as \(\alpha\) approaches 1 regardless of solver.

See also

[pagerank()] (the "Damping factor" section), [pagerank_convergence], [compare_pagerank()], [pagerank_grid()]

Examples

edges <- data.frame(
  from = c("A", "B", "C", "A", "D"),
  to = c("B", "C", "A", "C", "A")
)
sens <- damping_sensitivity(edges, clean_edge_urls = FALSE)
print(sens)
#>    url alpha     score iters iters_estimate     residual converged
#> 1    A  0.75 0.3769231    NA             25 0.000000e+00      TRUE
#> 2    C  0.75 0.3567308    NA             25 0.000000e+00      TRUE
#> 3    B  0.75 0.2038462    NA             25 0.000000e+00      TRUE
#> 4    D  0.75 0.0625000    NA             25 0.000000e+00      TRUE
#> 5    A  0.80 0.3820755    NA             31 5.551115e-17      TRUE
#> 6    C  0.80 0.3650943    NA             31 5.551115e-17      TRUE
#> 7    B  0.80 0.2028302    NA             31 5.551115e-17      TRUE
#> 8    D  0.80 0.0500000    NA             31 5.551115e-17      TRUE
#> 9    A  0.85 0.3869418    NA             43 1.110223e-16      TRUE
#> 10   C  0.85 0.3736080    NA             43 1.110223e-16      TRUE
#> 11   B  0.85 0.2019503    NA             43 1.110223e-16      TRUE
#> 12   D  0.85 0.0375000    NA             43 1.110223e-16      TRUE
#> 13   A  0.90 0.3915401    NA             66 8.673617e-17      TRUE
#> 14   C  0.90 0.3822668    NA             66 8.673617e-17      TRUE
#> 15   B  0.90 0.2011931    NA             66 8.673617e-17      TRUE
#> 16   D  0.90 0.0250000    NA             66 8.673617e-17      TRUE
#> 17   A  0.95 0.3958876    NA            135 1.405126e-16      TRUE
#> 18   C  0.95 0.3910659    NA            135 1.405126e-16      TRUE
#> 19   B  0.95 0.2005466    NA            135 1.405126e-16      TRUE
#> 20   D  0.95 0.0125000    NA            135 1.405126e-16      TRUE
attr(sens, "convergence")
#>   alpha   algo iters iters_estimate     residual   tol converged n_nodes
#> 1  0.75 prpack    NA             25 0.000000e+00 0.001      TRUE       4
#> 2  0.80 prpack    NA             31 5.551115e-17 0.001      TRUE       4
#> 3  0.85 prpack    NA             43 1.110223e-16 0.001      TRUE       4
#> 4  0.90 prpack    NA             66 8.673617e-17 0.001      TRUE       4
#> 5  0.95 prpack    NA            135 1.405126e-16 0.001      TRUE       4

# Populate the empirical iteration count by using the ARPACK solver.
sens_ar <- suppressMessages(
  damping_sensitivity(edges, algo = "arpack", clean_edge_urls = FALSE)
)
attr(sens_ar, "convergence")
#>   alpha   algo iters iters_estimate     residual   tol converged n_nodes
#> 1  0.75 arpack    13             25 2.012279e-16 0.001      TRUE       4
#> 2  0.80 arpack    13             31 7.147061e-16 0.001      TRUE       4
#> 3  0.85 arpack    14             43 2.567391e-16 0.001      TRUE       4
#> 4  0.90 arpack    14             66 2.220446e-16 0.001      TRUE       4
#> 5  0.95 arpack    14            135 1.821460e-16 0.001      TRUE       4