| salsa | R Documentation |
Computes Lempel & Moran's SALSA hub and authority scores over the same cleaned, redirect/canonical-folded, domain-filtered, deduplicated link graph that [pagerank()] builds, so node identities line up across the centrality measures. SALSA is a stochastic variant of HITS: it runs HITS-style mutual reinforcement as PageRank-style random walks on the bipartite hub/authority graph, yielding stationary-distribution scores instead of dominant eigenvectors.
salsa(
edge_list_df,
redirects_df = NULL,
clean_edge_urls = TRUE,
clean_redirect_urls = TRUE,
rurl_params = list(),
self_loops = c("drop", "keep"),
drop_isolates_flag = TRUE,
duplicate_edge_policy = c("collapse", "aggregate", "count_instances"),
edge_from_col = "from",
edge_to_col = "to",
redirect_from_col = "from",
redirect_to_col = "to",
duplicate_from_policy = c("strict", "first_wins", "last_wins", "most_frequent",
"prune_source", "resolve_if_consistent"),
loop_handling = c("error", "prune_loop", "break_arrow"),
canonicals_df = NULL,
canonical_from_col = "from",
canonical_to_col = "to",
clean_canonical_urls = TRUE,
canonical_duplicate_from_policy = c("strict", "first_wins", "last_wins",
"most_frequent", "prune_source", "resolve_if_consistent"),
canonical_loop_handling = c("error", "prune_loop", "break_arrow"),
canonical_conflict_policy = c("redirect_wins", "error", "canonical_wins"),
keep_domains = NULL,
exclude_domains = NULL,
keep_hosts = NULL,
exclude_hosts = NULL,
...
)
edge_list_df |
A data frame representing the edge list, typically with columns like "from" and "to". Edges are expected to be page-to-page hyperlinks: 'pagerank()' is graph-agnostic and treats every endpoint as a node, so resource links (images, CSS, JS, and other non-HTML references) must be filtered upstream or they collect authority as ordinary vertices. 'pagerank_screaming_frog()' does this at the crawl boundary via [sf_graph_eligible()] ('Hyperlink' only); a hand-built or non-SF edge list should apply the same hyperlink-only filter before scoring. |
redirects_df |
An optional data frame for redirect rules, typically with "from" and "to" columns. Defaults to NULL. |
clean_edge_urls |
Logical, whether to clean URLs in the edge list. Defaults to TRUE. |
clean_redirect_urls |
Logical, whether to clean URLs in the redirect list. Defaults to TRUE. Only effective if 'redirects_df' is provided. |
rurl_params |
A list of parameters to pass to 'rurl::clean_url'. Defaults to an empty list. 'protocol_handling' defaults to '"keep"' and 'case_handling' to '"lower_host"' for cross-project canonicalization consistency. If you set 'host_encoding' ('"idna"' or '"unicode"') to fold internationalized (IDN) hosts, that same value is also passed to the domain-filtering step so its comparisons stay consistent with the cleaned node keys. (Registrable-domain matching is encoding-independent, so this only matters if host-level filtering is involved.) |
self_loops |
A character string specifying how to handle self-loops. Either "drop" (default) or "keep". |
drop_isolates_flag |
Logical, whether to drop isolated nodes before PageRank computation. Defaults to TRUE. |
duplicate_edge_policy |
How repeated 'from -> to' rows are represented after URL cleaning, redirect/canonical folding, and domain filtering. One of:
|
edge_from_col, edge_to_col |
Names of from/to columns in 'edge_list_df'. |
redirect_from_col, redirect_to_col |
Names of from/to columns in 'redirects_df'. |
duplicate_from_policy |
How to handle conflicting redirects in 'redirects_df'. Passed through to [resolve_redirects()]. Default '"strict"' (error on conflicts). See [resolve_redirects()] for all available policies. |
loop_handling |
How to handle redirect cycles. Passed through to [resolve_redirects()]. Default '"error"'. See [resolve_redirects()] for all available policies. |
canonicals_df |
An optional data frame of declared 'rel=canonical' links, with 'from'/'to' columns (or as set by 'canonical_from_col' / 'canonical_to_col') pairing a source URL with the canonical it declares. Default 'NULL' (opt-in; the default preserves current behavior). Canonicals are a **distinct, advisory** signal from enforced 3xx 'redirects_df': they are tracked separately and audited via [audit_canonicals()] / [audit_fold()], then folded into the same composed map as redirects (see [build_fold_map()]). Self-canonicals drop as no-ops. |
canonical_from_col, canonical_to_col |
From/to columns in 'canonicals_df'. Default '"from"' / '"to"'. |
clean_canonical_urls |
Logical, whether to clean URLs in 'canonicals_df' using the same resolved 'rurl_params' profile as edge and redirect cleaning. Default 'TRUE'. Only effective when 'canonicals_df' is provided. |
canonical_duplicate_from_policy |
How to handle a canonical source that declares multiple distinct canonicals. Reuses the 'duplicate_from_policy' enum (see [resolve_redirects()]). Default '"strict"'. |
canonical_loop_handling |
How to handle cycles among declared canonicals. Reuses the 'loop_handling' enum. Default '"error"'. |
canonical_conflict_policy |
How to resolve a redirect-vs-canonical disagreement on the **same source** URL. One of '"redirect_wins"' (default; the 3xx wins and the canonical on a redirecting source is ignored and flagged), '"error"' (error on genuine disagreement), or '"canonical_wins"' (the declared canonical wins for that source, still flagged). See [build_fold_map()]. |
keep_domains |
Optional character vector of domains to keep. When provided, edges are filtered via [filter_links_by_domain()] so that only links where both endpoints belong to one of the specified domains are included. Useful for restricting to internal links. Default 'NULL' (no domain filtering). **Ordering:** filtering runs *after* redirect/canonical folding, so it scopes the post-fold (canonical) namespace, not the crawled input. If an out-of-scope canonical/redirect rewrites the crawled domain onto a different one, filtering on the crawled domain matches nothing (an empty graph). To scope the INPUT you crawled, run [filter_links_by_domain()] on the edge list *before* calling 'pagerank()'. |
exclude_domains |
Optional character vector of domains to exclude. Edges where either endpoint belongs to one of these domains are removed. Like 'keep_domains', this filters the post-fold namespace (see the ordering note above). Default 'NULL' (no exclusion). |
keep_hosts |
Optional character vector of exact hosts to keep (e.g. '"www.example.com"'), as opposed to registrable domains. Matched on the exact host using the same canonicalization profile as cleaning, so IDN folding ('host_encoding' in 'rurl_params') applies consistently. Default 'NULL'. |
exclude_hosts |
Optional character vector of exact hosts to exclude. Edges where either endpoint matches one of these hosts are removed. Ignore rules override keep rules. Default 'NULL'. |
... |
Additional arguments forwarded to [compute_salsa()]. |
## Relationship to the PageRank pipeline
'salsa()' reuses the exact identity-forming steps of [pagerank()] — URL canonicalization (the same resolved 'rurl' profile), the same composed redirect + canonical fold map, the same domain/host filtering, the same 'duplicate_edge_policy' deduplication, and the same self-loop / isolate handling. The resulting vertex set therefore matches 'pagerank()' run with the same arguments, so hub, authority, and PageRank can be joined on 'node_name' without re-canonicalizing.
The PageRank-specific, *forward-flow* modeling devices have **no SALSA analog and are intentionally not exposed**: nofollow evaporation, the indexability (noindex / robots.txt) transforms, the TIPR teleport prior, and the 'reverse' flag. SALSA already computes both directions of authority flow (hub is the outflow-oriented score, authority the inflow-oriented one).
## SALSA versus HITS, and the pagerankr adaptation
Where [hits()] takes the dominant eigenvectors of A^\top A and
A A^\top, SALSA replaces the mutual-reinforcement iteration with two
*stochastic* Markov chains on the bipartite hub/authority graph; their
stationary distributions are the scores, which on a connected graph reduce to
a simple in-/out-degree closed form (see [compute_salsa()]). Because the
chains are stochastic, SALSA is far less prone than HITS to the
"tightly-knit community" effect, where a dense cluster of mutually linking
pages dominates the top scores.
Lempel & Moran's original SALSA ran on a query-focused base set. 'salsa()' instead runs on the **full (or user-filtered) site graph** that 'pagerankr' assembles — a documented site-graph adaptation of the focused-subgraph algorithm. Treat the scores as site-wide structural centralities, not query-relevance scores. Coverage differs from PageRank: a node's 'hub' is 'NA' when it has no outlinks and its 'authority' is 'NA' when it has no inlinks (see [compute_salsa()]).
A data frame with one row per node and columns 'node_name', 'hub', and 'authority' (column names configurable via '...'). Hub and authority each sum to '1' over their non-'NA' entries.
Lempel, R. & Moran, S. (2001). SALSA: The Stochastic Approach for Link-Structure Analysis. *ACM Transactions on Information Systems*, 19(2), 131-160.
[compute_salsa()] for the computational core, [hits()] for the HITS analog, and [pagerank()] for the PageRank analog sharing this identity pipeline.
edges <- data.frame(
from = c("http://A.com/", "http://A.com/", "B.com"),
to = c("B.com", "C.com", "C.com")
)
salsa(edges)
# Hub vs authority: a pure outflow page tops hub, a pure inflow page tops
# authority.
s <- salsa(edges)
s[which.max(s$hub), ]
s[which.max(s$authority), ]
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.