Computes the Stochastic Approach for Link-Structure Analysis (SALSA; Lempel & Moran 2001) hub and authority scores from a processed edge list. SALSA combines HITS-style mutual reinforcement with PageRank-style stochastic random walks on the bipartite hub/authority graph. This is the low-level computational core; the high-level [salsa()] wrapper runs the URL-cleaning, redirect/canonical folding, domain filtering, deduplication, and isolate-handling identity pipeline first.
Usage
compute_salsa(
edge_list_df,
vertices_df = NULL,
from_col = "from",
to_col = "to",
vertex_col_name = "node_name",
pr_node_col = "node_name",
hub_col = "hub",
authority_col = "authority"
)Arguments
- edge_list_df
A data frame representing the processed edge list, with source/target columns (see `from_col`, `to_col`). NAs in those columns are omitted before graph construction.
- vertices_df
An optional single-column data frame of node names defining the vertex set (e.g. to retain isolates). If `NULL` (default), the vertices are inferred from `edge_list_df`. The column name is given by `vertex_col_name`.
- from_col, to_col
Names of the source/target columns in `edge_list_df`. Defaults `"from"` / `"to"`.
- vertex_col_name
Name of the node column in `vertices_df`. Default `"node_name"`.
- pr_node_col
Name for the node column in the output. Default `"node_name"` (kept consistent with [compute_pagerank()]).
Names for the hub and authority score columns in the output. Defaults `"hub"` / `"authority"`.
Value
A data frame with three columns: the node name (named by `pr_node_col`) and the hub and authority scores (named by `hub_col` / `authority_col`). Hub and authority each sum to `1` over their non-`NA` entries. Returns an empty (zero-row) data frame with those columns when the graph has no vertices.
Details
## The two SALSA Markov chains
SALSA builds an undirected bipartite graph \(\hat{G}\): each crawl-graph edge \(u \rightarrow v\) contributes a hub-node \(u_h\) and an authority-node \(v_a\) joined by an edge. Two coupled random walks run on it. The **authority** chain alternates authority \(\rightarrow\) hub \(\rightarrow\) authority (one step = two traversals); the **hub** chain alternates the other way. Unlike HITS — whose scores are the dominant eigenvectors of \(A^\top A\) and \(A A^\top\) — each SALSA chain is *stochastic*, so its stationary distribution is the score vector.
## Closed form (no iteration)
Lempel & Moran (2001, Proposition 6) show the stationary distributions have a degree-based closed form, so **no eigenvector iteration is needed**. On a single connected component the authority score of a node is \(d_{in}(i) / W\) and the hub score is \(d_{out}(i) / W\), where \(W\) is the edge count. When the support graph splits into several weakly connected components, each component's scores are renormalized within the component and then reweighted by the component's share of the relevant side (Proposition 6):
$$\tilde{\pi}_j = \frac{|A_{c(j)}|}{|A|} \times \frac{d_{in}(j)}{W_{c(j)}}$$
for authorities (and symmetrically for hubs with \(d_{out}\) and \(|H_c|\)), where \(A\) is the set of all authorities (in-degree \(> 0\)), \(A_{c(j)}\) the authorities in \(j\)'s component, and \(W_{c(j)}\) the edges in that component. **This component reweighting is required for correctness:** without it, cross-component score comparisons are invalid — a common failure mode on site crawls with orphan page clusters. Each side's scores sum to `1`.
## Coverage and one-sided vertices
The hub side contains only nodes with out-degree \(> 0\); the authority side only nodes with in-degree \(> 0\). A node's `hub` is `NA` when its out-degree is `0`, and its `authority` is `NA` when its in-degree is `0` (a pure sink has `NA` hub; a pure source has `NA` authority; an isolate has both `NA`). SALSA coverage therefore differs from PageRank coverage on the same graph — this is expected, not a bug.
## Weighting
v1 is **unweighted**: the closed form assumes uniform edge weights, so scores are driven by in-/out-degree on the deduplicated simple graph. A weighted extension is deferred.
References
Lempel, R. & Moran, S. (2001). SALSA: The Stochastic Approach for Link-Structure Analysis. *ACM Transactions on Information Systems*, 19(2), 131-160.
See also
[salsa()] for the full identity pipeline; [compute_hits()] for the HITS analogue; [compute_pagerank()] for the PageRank analogue.
Examples
edges <- data.frame(
from = c("A", "A", "B"), to = c("B", "C", "C")
)
compute_salsa(edges)
#> node_name hub authority
#> 1 A 0.6666667 NA
#> 2 B 0.3333333 0.3333333
#> 3 C NA 0.6666667
# Retain an isolate via vertices_df (NA hub and NA authority)
verts <- data.frame(node_name = c("A", "B", "C", "D"))
compute_salsa(edges, vertices_df = verts)
#> node_name hub authority
#> 1 A 0.6666667 NA
#> 2 B 0.3333333 0.3333333
#> 3 C NA 0.6666667
#> 4 D NA NA