Skip to contents

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