Skip to contents

The reverse-graph sibling of [topic_sensitive_pagerank()]. Where Topic-Sensitive PageRank answers *"given I care about this cluster, which pages are most **authoritative** for it?"* (authority flows downstream **from** the seed cluster), `topic_feeder_pagerank()` answers the inverse question: *"which pages **feed / power** this cluster?"* — i.e. the strongest internal hubs whose outlinks point **into** the target pages.

It is personalized PageRank with the teleport prior concentrated on the **target cluster**, run on the **transposed** link graph (`reverse = TRUE`). Mass teleports onto the cluster and then walks *backward* along links, so it accumulates on the pages that funnel authority toward the cluster. The further (in out-link hops) a page is from the cluster, the less feeder credit it earns — the PageRank damping factor is exactly that attenuation.

Like [trustrank()] and [topic_sensitive_pagerank()], this introduces **no new solver**: it builds a `prior_df` from the seed set ([feeder_seed_prior()]) and hands it to [pagerank()] with `reverse = TRUE`. The caller supplies the cluster; there is no topic inference.

Usage

feeder_seed_prior(
  seeds,
  seed_weight = NULL,
  seed_url_col = "url",
  seed_weight_col = "weight"
)

topic_feeder_pagerank(
  edge_list_df,
  seeds,
  seed_weight = NULL,
  seed_url_col = "url",
  seed_weight_col = "weight",
  ...
)

Arguments

seeds

The target cluster. Either a character vector of cluster URLs (each gets equal seed weight unless `seed_weight` is given), or a data frame with a URL column and a numeric weight column (see `seed_url_col` / `seed_weight_col`) for unequal cluster emphasis.

seed_weight

Optional numeric weight for a character-vector `seeds`: either one value per seed or a single value recycled to all seeds. Ignored when `seeds` is a data frame. Default `NULL` (every cluster page weight `1`, a uniform distribution over the cluster).

seed_url_col, seed_weight_col

Column names used when `seeds` is a data frame. Defaults `"url"` / `"weight"`. Ignored for a character vector.

edge_list_df

A data frame representing the edge list, typically with columns like "from" and "to".

...

Additional arguments forwarded to [pagerank()] (e.g. `redirects_df`, `canonicals_df`, `rurl_params`, `weight_col`, `prior_transform`, `prior_alpha`, `damping`). Passing `prior_df`, `prior_url_col`, `prior_weight_col`, or `reverse` is an error.

Value

[feeder_seed_prior()] returns a data frame with `url` and `weight` columns, suitable as the `prior_df` argument to [pagerank()].

[topic_feeder_pagerank()] returns the [pagerank()] result data frame (`node_name`, `pagerank`, and the `prior_weight` column the prior path adds), sorted by `pagerank` descending, carrying the usual `"transition_audit"` attribute. The audit's model configuration records `reverse = TRUE`.

Details

## Why this is not recoverable from forward PageRank

In PageRank, authority flows **along** link direction: linking *to* an important page does not make the linker important. So "the pages that feed cluster X" is **not** a re-reading of forward (or Topic-Sensitive) PageRank scores — it is the reversed-graph notion. Forward PageRank and [topic_sensitive_pagerank()] rank pages by **inflow** (important because important pages point at them); `topic_feeder_pagerank()` ranks by **outflow toward the cluster** (important because *it* points at the cluster).

## How to read the result

The seed (cluster) pages carry the teleport mass directly, so they appear in `prior_weight` with a positive value and tend to score highly *by construction* — that is teleport, not a feeder signal. **The feeders are the high-`pagerank` pages whose `prior_weight` is `0`** (pages outside the cluster that nonetheless accumulate reverse-walk mass). Rank by `pagerank` and read off the top non-seed rows, or filter `prior_weight == 0`.

## Relationship to neighboring tools

[pagerank()] (forward)

Global inflow authority. Feeder PageRank is its transpose, biased to a cluster.

[topic_sensitive_pagerank()] (G2)

The forward-graph sibling: same personalization plumbing, opposite flow direction. G2 finds a cluster's *authorities*; this finds its *feeders*. They are complementary, not substitutes.

Inverse PageRank (`pagerank(reverse = TRUE)`)

The **global**, unseeded outflow centrality — "which pages funnel authority outward anywhere on the site". Feeder PageRank adds the cluster bias: not "good hub in general" but "good hub *for this cluster*". With no `seeds` you would just call `pagerank(reverse = TRUE)` directly.

HITS hubs ([hits()])

Also an outflow notion, but a co-computed eigenvector pair (hub <-> authority) with no teleport prior and no damped-surfer / dangling handling. Feeder PageRank is the random-surfer-model, cluster-seedable counterpart that stays inside the [pagerank()] graph-preparation contract (redirects, canonicals, duplicate-edge policy, weights).

Seed weights are an **additive feeder budget**: if two seed URLs fold onto the same vertex (redirect / canonical variants) their weights sum, exactly as the [pagerank()] / [align_prior_to_vertices()] prior contract specifies.

Everything [pagerank()] accepts flows through `...`: redirects, canonicals, URL cleaning, domain/host filtering, edge weights, duplicate-edge policy, and the prior-shaping knobs (`prior_transform`, `prior_alpha`). Because this owns both the prior and the graph orientation, passing `prior_df`, `prior_url_col`, `prior_weight_col`, or `reverse` is an error. Note that the direction-sensitive forward-flow devices that [pagerank()] already rejects under `reverse = TRUE` (`nofollow_action = "evaporate"`, `indexability_df`) are likewise unavailable here.

See also

[topic_sensitive_pagerank()], [trustrank()], [pagerank()], [align_prior_to_vertices()], [hits()]

Examples

edges <- data.frame(
  from = c("/hub", "/hub", "/feeder", "/blog", "/ai", "/news"),
  to = c("/ai", "/ai-demo", "/ai", "/ai", "/ai-demo", "/sports")
)

# Which pages feed the AI-Agent cluster?
fr <- topic_feeder_pagerank(
  edges,
  seeds = c("/ai", "/ai-demo"),
  clean_edge_urls = FALSE
)
#> TIPR prior aligned: 2/7 real vertices carry authority; transform='none', alpha=0 (uniform mass ~0.0%). 0 prior URL(s) (sum weight 0) did not fold onto any vertex and were dropped.

# Feeders are the top-scoring rows OUTSIDE the cluster (prior_weight == 0).
fr[fr$prior_weight == 0, c("node_name", "pagerank")]
#>   node_name  pagerank
#> 3      /hub 0.2040628
#> 4     /blog 0.0994152
#> 5   /feeder 0.0994152
#> 6     /news 0.0000000
#> 7   /sports 0.0000000

# Build the cluster prior explicitly and run it yourself, if you prefer:
prior <- feeder_seed_prior(c("/ai", "/ai-demo"))
identical_run <- pagerank(
  edges, prior_df = prior, reverse = TRUE, clean_edge_urls = FALSE
)
#> TIPR prior aligned: 2/7 real vertices carry authority; transform='none', alpha=0 (uniform mass ~0.0%). 0 prior URL(s) (sum weight 0) did not fold onto any vertex and were dropped.