Builds a graph from a processed edge list and computes PageRank scores using `igraph::page_rank()`.
Usage
compute_pagerank(
edge_list_df,
vertices_df = NULL,
damping = 0.85,
algo = c("prpack", "arpack"),
eps = NULL,
niter = NULL,
from_col = "from",
to_col = "to",
vertex_col_name = "node_name",
reverse = FALSE,
weight_col = NULL,
weight_validation = c("error", "warning", "none"),
weight_expected_total = NULL,
weight_tolerance = sqrt(.Machine$double.eps),
pr_node_col = "node_name",
pr_value_col = "pagerank",
prior_df = NULL,
prior_url_col = "url",
prior_weight_col = "weight",
prior_transform = "none",
prior_alpha = 0,
prior_exclude_nodes = character(0),
prior_verbose = TRUE,
...
)Arguments
- edge_list_df
A data frame representing the processed edge list, typically with columns "from" and "to" (or as specified by `from_col`, `to_col`). It should contain only edges to be included in the graph. NAs in these columns will be omitted before graph construction.
- vertices_df
An optional single-column data frame of node names to define the set of vertices for the graph. If `NULL` (default), all unique non-NA nodes present in `edge_list_df` (after NA removal from edges) are used. The column name is specified by `vertex_col_name`.
- damping
The damping factor for PageRank. Default is 0.85.
- algo
Solver back-end passed to `igraph::page_rank()`. Either `"prpack"` (default; a fast, exact direct solver with no tunable convergence controls) or `"arpack"` (an iterative eigensolver that honors `eps` / `niter` and reports its iteration count). Supplying `eps` or `niter` while leaving `algo` at its default transparently switches to `"arpack"`, since PRPACK ignores those controls. See [pagerank_convergence] for the trade-offs.
- eps
Optional convergence tolerance (L1, the ARPACK `options$tol`). When supplied, the solver switches to `"arpack"` and iterates until the residual is at or below `eps`. `NULL` (default) uses the solver's own default.
- niter
Optional maximum iteration count (the ARPACK `options$maxiter`). When supplied, the solver switches to `"arpack"`. `NULL` (default) uses the solver's own default. As a rule of thumb, power-iteration PageRank needs about `log10(eps) / log10(damping)` iterations, so raise `niter` when you raise `damping` toward 1. `NULL` (default) uses the solver's own default.
- from_col
Name of the source node column in `edge_list_df`. Default "from".
- to_col
Name of the target node column in `edge_list_df`. Default "to".
- vertex_col_name
Name of the column in `vertices_df` containing node names. Default "node_name".
- reverse
Logical. If `TRUE`, edge orientation is flipped before the graph is built, so PageRank is computed on the transposed graph. This is the reverse / inverse PageRank (a.k.a. CheiRank): instead of inflow importance ("who points to me"), it measures outflow centrality ("does this page funnel authority outward"). Vertices, weights, and the teleport prior are unaffected by the flip; only edge direction is reversed. Default `FALSE`. See [pagerank()] for the higher-level wrapper and the caveats on combining `reverse = TRUE` with direction-sensitive features.
- weight_col
Optional name of a numeric column in `edge_list_df` containing edge weights. Higher weights make edges more likely to be followed in the random surfer model. If `NULL` (default), all edges have equal weight (unweighted PageRank).
- weight_validation
How invalid edge weights are handled when `weight_col` is supplied: `"error"` (default), `"warning"`, or `"none"`. Validation covers negative and non-finite values plus sources whose outgoing weights are all zero. See [validate_edge_weights()].
- weight_expected_total
Optional expected per-source weight total. Leave `NULL` (default) for ordinary raw edge weights. Set to `1` when `weight_col` contains pre-normalized transition probabilities.
- weight_tolerance
Non-negative tolerance used with `weight_expected_total`.
- pr_node_col
Name for the node column in the output PageRank data frame. Default "node_name".
- pr_value_col
Name for the PageRank value column in the output data frame. Default "pagerank".
- prior_df
Optional per-URL external-authority prior (TIPR). When supplied, a personalization/teleport vector is built via [align_prior_to_vertices()] from the final vertex set and passed to `igraph::page_rank(personalized = )`. The prior URLs must already share the vertex namespace (canonicalized + redirect-folded); [pagerank()] handles that. Default `NULL` (uniform teleport).
- prior_url_col, prior_weight_col
Column names in `prior_df`. Defaults `"url"` / `"weight"`.
- prior_transform, prior_alpha, prior_exclude_nodes, prior_verbose
Passed to [align_prior_to_vertices()] as `transform`, `alpha`, `exclude_nodes`, `verbose`. See that function for semantics.
- ...
Additional arguments passed to `igraph::page_rank()`.
Value
A data frame with two columns: one for node names (named by `pr_node_col`) and one for their PageRank scores (named by `pr_value_col`), which sum to 1 for non-empty graphs. Returns an empty data frame with correct column names if the graph is empty or has no nodes after processing.
For non-empty graphs the result carries a `"convergence"` attribute (a [pagerank_convergence] object) recording the solver used, iterations (when the solver exposes them), and the post-hoc L1 residual of the returned vector. Retrieve it with `attr(result, "convergence")`.
Examples
edges <- data.frame(
from = c("A", "B", "C"), to = c("B", "C", "A")
)
pr_results <- compute_pagerank(edges)
print(pr_results)
#> node_name pagerank
#> 1 A 0.3333333
#> 2 B 0.3333333
#> 3 C 0.3333333
if (nrow(pr_results) > 0) sum(pr_results$pagerank)
#> [1] 1
# With specified vertices (e.g., from drop_isolates)
vertices <- data.frame(
node_name = c("A", "B", "C", "D")
) # D is an isolate
pr_results_isolates_kept <- compute_pagerank(edges, vertices_df = vertices)
print(pr_results_isolates_kept)
#> node_name pagerank
#> 1 A 0.31746032
#> 2 B 0.31746032
#> 3 C 0.31746032
#> 4 D 0.04761905
if (nrow(pr_results_isolates_kept) > 0) {
sum(pr_results_isolates_kept$pagerank)
}
#> [1] 1
# Single node graph with self-loop
single_node_edges <- data.frame(
from = "A", to = "A"
)
compute_pagerank(single_node_edges)
#> node_name pagerank
#> 1 A 1
# Single node, no edges, defined by vertices_df
single_node_no_loop <- data.frame(
from = character(0), to = character(0)
)
compute_pagerank(
single_node_no_loop,
vertices_df = data.frame(node_name = "A")
)
#> node_name pagerank
#> 1 A 1
# Empty graph (no edges, no vertices defined)
empty_edges <- data.frame(
from = character(), to = character()
)
compute_pagerank(empty_edges)
#> [1] node_name pagerank
#> <0 rows> (or 0-length row.names)
# Edges with NAs (these edges will be dropped)
edges_with_na <- data.frame(
from = c("A", NA, "C"), to = c("B", "D", NA)
)
compute_pagerank(edges_with_na) # Should only process A->B
#> node_name pagerank
#> 1 A 0.3508772
#> 2 B 0.6491228
compute_pagerank(
edges_with_na,
vertices_df = data.frame(node_name = c("A", "B", "C", "D"))
)
#> node_name pagerank
#> 1 A 0.2061856
#> 2 B 0.3814433
#> 3 C 0.2061856
#> 4 D 0.2061856