centrality_series: Time-varying vertex centrality

View source: R/centrality.R

centrality_seriesR Documentation

Time-varying vertex centrality

Description

Centrality for every vertex at every time point. Ask for several measures in one call and they arrive stacked in a single tidy frame, one row per vertex, time point and measure.

Each value measures the network as it stands in one time bin, so the result is a trajectory of ordinary centrality. Order within a bin is not used: every tie active in the bin counts as present. Centrality computed from time-respecting paths across the whole period is path_centrality().

Usage

centrality_series(
  dn,
  measure = "degree",
  sessions = c("bounded", "collapse", "separate"),
  sample = NULL,
  damping = 0.85,
  mode = c("all", "out", "in"),
  start = NULL,
  end = NULL,
  step = NULL,
  window = NULL,
  exponent = 1,
  prestige = "indegree",
  rescale = FALSE,
  lambda = 1,
  plot = FALSE
)

Arguments

dn

A temporal network from dynet().

measure

One or more of "degree", "strength", "prestige", "closeness", "betweenness", "eigenvector", "pagerank", "hub", "authority", "coreness", "constraint", "power", "harary", "information", "load", "flow_betweenness", or "diffusion". The deprecated names "indegree" and "outdegree", which warn with class dynet_deprecated and are replaced by measure = "degree" with mode = "in" or mode = "out". Defaults to "degree". Any other name raises an error of class dynet_unknown_measure; the directed-only measures "prestige", "hub", "authority" and the two deprecated names "indegree" and "outdegree" raise dynet_needs_directed on an undirected network.

sessions

How to treat sessions: "bounded" (the default) keeps paths inside a session, "collapse" ignores sessions, "separate" reports each session on its own rows. "separate" on a network built without a session column raises an error of class dynet_no_sessions.

sample

Deprecated. "instant" is equivalent to window = 0; "window" uses the current positive/default window.

damping

Damping factor for PageRank; a single number strictly between zero and one, 0.85 by default.

mode

Which edges count on a directed network: "all" both directions, "out" outgoing only, "in" incoming only. Defaults to "all"; any other string raises an error of class dynet_bad_input. Name several at once – mode = c("all", "in", "out") – to get degree, in-degree and out-degree from a single call; the extra directions are then labelled degree_in and degree_out in the measure column, while a call naming one direction keeps the plain measure name. Applies to "degree", "strength", "closeness", "coreness", "harary", "eigenvector" and "diffusion"; the remaining measures have a single directional definition and ignore it. Ignored entirely on an undirected network. In-degree is therefore mode = "in". The old "indegree" and "outdegree" measure names remain as deprecated aliases.

start, end

First and last time at which to measure. Default to the observed range. A network built from dates may be addressed with dates.

step

How often to measure. Defaults to the interval the network was built with.

window

How much time each measurement covers. Defaults to step, which tiles the period into disjoint bins. A larger value slides an overlapping window; 0 samples the network at each point in time. "all" measures the whole observed period as one window, closed on the right so an event at the final instant is inside it; it cannot be combined with step, and under sessions = "separate" or discontinuous observation it gives one window per session or observed component.

exponent

Attenuation factor for Bonacich "power", a single finite number, 1 by default. Positive rewards being connected to well-connected others; negative rewards the opposite, which is the bargaining reading.

prestige

Prestige definition, "indegree" by default. "indegree" counts distinct active incoming dyads. "indegree.rownorm" first gives every active sender one unit split equally across its distinct outgoing dyads, then sums the received mass. "indegree.rowcolnorm" balances a total-support binary adjacency to doubly stochastic form; feasible scores are necessarily uniform. "domain" counts the distinct other vertices with a directed path into each vertex in the active snapshot. "domain.proximity" discounts that incoming domain fraction by its mean directed hop distance. "eigenvector" uses the unique nonnegative Perron ray of the transposed binary adjacency. "eigenvector.rownorm" first divides every nonzero binary sender row by its outgoing-dyad count and then solves the same certified incoming Perron equation. "eigenvector.colnorm" instead divides every nonzero binary receiver column by its incoming-dyad count before solving. "eigenvector.rowcolnorm" first certifies total support and balances binary adjacency to doubly stochastic form. Prestige is directed and snapshot-only.

rescale

Whether to divide prestige by its total independently inside every reported time/session block; FALSE by default. Zero-total count/proximity definitions return NaN; structurally undefined spectral definitions return NA. This argument requires measure = "prestige", and raises dynet_bad_input otherwise.

lambda

Nonnegative multiplier for "diffusion", 1 by default. Diffusion degree is the sum of the selected degree of a vertex and all of its one-step neighbours, multiplied by lambda.

plot

Whether to draw the result as well as return it. Drawing is a side effect in the manner of graphics::hist(): the verb still returns its tidy table, invisibly when it has drawn, so plot = TRUE saves the wrapping plot() call without changing what comes back. Use plot() on the result when the figure needs arguments of its own.

Details

step and window are separate on purpose. step is how often you look; window is how much of the timeline each look takes in. Setting them equal partitions the period; setting window larger than step is a rolling window, which keeps the resolution of the smaller step while smoothing over the noise of a sparse bin. The arguments match tsna::tSnaStats(), where they are called time.interval and aggregate.dur.

Snapshot "degree" counts distinct active binary dyads, so duplicate, split and overlapping spells do not multiply it; mode = "all" on a directed snapshot is in-degree plus out-degree. "strength" is the same margin taken over spell weights rather than over binary dyads. In a positive window each spell contributes its weight in proportion to the share of its duration that falls inside the window, weight * overlap / duration, so a spell straddling two tiled windows splits its weight between them and the pieces add back to the whole. A point contact has no duration to split and contributes its full weight to the window holding it. With window = 0 every active spell contributes its full weight at that instant. The share uses the spell's recorded duration, so the part of a spell outside the observation period is not reassigned to observed windows. snapshots() and the network plots keep full weights per bin, so their weight column is not the input to this strength.

Snapshot "closeness" is not Freeman's 1 / \sum_z d_{sz}, which is undefined once a snapshot is disconnected – and a time bin almost always is. It is the reciprocal of the mean geodesic distance to the vertices a vertex can actually reach: with R_s the reachable nonself set,

C(s) = |R_s| / \sum_{z \in R_s} d_{sz},

which is zero for an isolate and equals Freeman's normalised closeness (n - 1) / \sum_z d_{sz} on a connected snapshot. "harary" is the reciprocal of eccentricity, zero for a vertex that cannot reach everything.

"eigenvector", "hub" and "authority" are certified the way eigenvector prestige is: a snapshot whose spectral radius is zero (no cycle) or whose Perron root is repeated (components of equal weight) has no single answer, and every vertex of that block is NA under a warning of class dynet_eigen_undefined. "eigenvector" is uniquely determined when the Perron eigenvalue has a one-dimensional eigenspace; strong connectivity is a sufficient condition. Disconnected snapshots with equally dominant components can have more than one correct eigenvector, so read the result as a within-snapshot ranking rather than an automatically comparable number across the whole series.

Indegree prestige is the column sum of the directed binary active-dyad adjacency matrix. It is exactly snapshot degree with mode = "in": duplicate, split, and overlapping spells and edge weights do not multiply the result, while an explicitly retained directed loop contributes once. With rescale = TRUE, the column sums are divided by their block total. A zero total is mathematically undefined and is returned as literal NaN.

Row-normalised indegree prestige first converts every nonzero binary adjacency row to sum one; zero rows remain all zero. Its column sums are the received sender-nomination mass, so their total is the number of active senders. rescale = TRUE divides again by that block total. This closed-form transform is the sna::prestige(cmode = "indegree.rownorm") definition on binary matrices. Dynet deliberately ignores edge weights, whereas sna uses their magnitudes on valued matrices.

Row-column-normalised prestige uses deterministic Sinkhorn–Knopp scaling only when the full binary vertex matrix has total support: every active dyad must belong to a perfect matching. It preserves all binary dyads and does not remove isolates or unsupported edges. Infeasible blocks return NA for every vertex with a classed warning. A feasible transform has every incoming column sum equal to one, so raw prestige is uniformly one and rescaled prestige uniformly 1 / n; this definition is a transform diagnostic, not a vertex ranking. Dynet uses fixed-order sweeps, maximum absolute row/column residual 1e-12, and at most 10,000 sweeps. It never returns a partial iterate. This deliberately differs from the randomised loose-tolerance annealer in sna 2.8.

Domain prestige is incoming indegree in the directed reachability graph after excluding its reflexive diagonal. If H[i,j] records whether i = j or a directed path from i to j exists, then p[j] = sum(H[,j]) - 1. Every distinct reaching vertex counts once, regardless of path length or multiplicity. Loops cannot add self credit, isolates score zero, and a zero-total rescaling returns literal NaN. Closure is computed on the binary active snapshot for each reporting block, not on chronologically ordered temporal journeys through the raw spells.

Domain-proximity prestige additionally uses the shortest incoming hop distances. For the nonself domain D[j], let r[j] be its size and s[j] the sum of its finite distances into j. The score is zero when r[j] = 0 and otherwise r[j]^2 / ((n - 1) * s[j]): the incoming domain fraction divided by mean hop distance. Unreachable vertices are omitted before the distance sum. This deliberately fixes an arithmetic artefact in sna 2.8, whose FALSE * Inf operation incorrectly zeros partial nonempty domains.

Eigenvector prestige solves t(B) %*% p = rho * p for the nonnegative Perron ray of binary adjacency B. It requires positive spectral radius and a one-dimensional Perron eigenspace. Raw scores have Euclidean norm one; rescale = TRUE makes their sum one. Zero-radius or nonunique blocks return all NA with a classed warning and diagnostics. Periodic cycles remain valid even when negative or complex roots share the spectral radius. Dynet uses direct eigenvalues plus an SVD nullity/residual check at tolerance 1e-10, orients the ray as nonnegative, and never applies elementwise absolute value.

Row-normalised eigenvector prestige first forms binary adjacency B and divides each nonzero sender row by its number of distinct outgoing dyads; zero rows remain exactly zero. It then solves the certified incoming Perron equation for the transpose of that row-stochastic matrix. Thus each active sender distributes one unit of recursive nomination mass, with no teleportation or dangling-row imputation. Binary session union and retained loop policy occur before row normalisation. The positive-radius, geometric- uniqueness, nonnegative-sign, L2/sum-scale, warning, and diagnostic rules are otherwise exactly those of ordinary eigenvector prestige.

Column-normalised eigenvector prestige divides each nonzero binary receiver column by its number of distinct incoming dyads; zero columns remain zero. It solves the incoming Perron equation only after that transform. If every vertex has positive indegree, the transformed transpose is row-stochastic and every certified score is necessarily uniform. Nonuniform defined scores therefore require a zero-indegree vertex. Binary union and retained-loop policy precede normalisation; certification and scaling remain those above.

Row-column-normalised eigenvector prestige composes the total-support and deterministic Sinkhorn–Knopp contract with the certified Perron contract. Infeasible support and nonconvergent balancing terminate before the spectral solve. A completed doubly stochastic transform always has the all-ones Perron ray, but reducible transforms have several such rays and remain undefined. Every fully certified score is therefore exactly uniform: 1 / sqrt(n) raw or 1 / n rescaled. This selector diagnoses support, balance, and irreducibility; it is not a vertex ranking.

Declared vertex activity induces the eligible vertex population before any kernel is evaluated. Positive windows independently use any-time vertex and edge unions before induction, while window = 0 evaluates the exact state. Results remain rectangular over the fixed vertex universe: inactive vertices receive typed NA, while eligible isolates keep the centrality kernel's ordinary static result.

Value

A dynet_metric: a tidy data frame with one row per vertex, time point and measure. Columns are session (only under sessions = "separate", which is the only mode that keeps session labels apart), time, node, measure and value. Print it, summary() it, plot() it, or take the plain frame with as.data.frame(). Prestige stores its mathematical choices as direct attributes for a prestige-only result and as named records under measure_metadata otherwise. When a prestige variant is structurally undefined or fails to converge, the affected values are NA, a warning says how many reporting blocks were affected, and a record naming the stage and reason for each comes out through as.data.frame(x, what = "diagnostics").

Conditions

Errors: dynet_unknown_measure (a measure not listed above), dynet_needs_directed ("prestige", "hub", "authority", "indegree" or "outdegree" on an undirected network), dynet_no_sessions (sessions = "separate" without a session column), dynet_outside_observation (the requested range misses observed support; it also carries dynet_bad_input), and dynet_bad_input for every other broken contract – dn not a dynet, an unknown mode, an out-of-range damping, exponent, lambda, prestige, rescale, start, end, step or window, and rescale = TRUE without measure = "prestige".

Warnings: dynet_deprecated (measure = "indegree"/"outdegree", or the retired sample argument), dynet_eigen_undefined and dynet_kernel_singular (both also carrying dynet_measure_undefined) when a snapshot's eigenvector, hub, authority, Bonacich power or information kernel has no unique answer, and dynet_prestige_infeasible, dynet_prestige_nonconvergence and dynet_prestige_eigen_undefined when a prestige variant is structurally undefined or fails to converge.

References

Holme, P., & Saramaki, J. (2012). Temporal networks. Physics Reports, 519(3), 97-125.

Wasserman, S., & Faust, K. (1994). Social Network Analysis: Methods and Applications. Cambridge University Press, Chapter 5.

Butts, C. T. (2024). sna: Tools for Social Network Analysis, version 2.8. doi:10.32614/CRAN.package.sna.

Lin, N. (1976). Foundations of Social Research. McGraw-Hill.

Freeman, L. C. (1979). Centrality in social networks: conceptual clarification. Social Networks, 1(3), 215-239. \Sexpr[results=rd]{tools:::Rd_expr_doi("10.1016/0378-8733(78)90021-7")}

Brandes, U. (2001). A faster algorithm for betweenness centrality. Journal of Mathematical Sociology, 25(2), 163-177.

Bonacich, P. (1987). Power and centrality: a family of measures. American Journal of Sociology, 92(5), 1170-1182.

Hage, P., & Harary, F. (1995). Eccentricity and centrality in networks. Social Networks, 17(1), 57-63.

Stephenson, K., & Zelen, M. (1989). Rethinking centrality. Social Networks, 11(1), 1-37.

Goh, K.-I., Kahng, B., & Kim, D. (2001). Universal behavior of load distribution in scale-free networks. Physical Review Letters, 87(27), 278701.

Freeman, L. C., Borgatti, S. P., & White, D. R. (1991). Centrality in valued graphs. Social Networks, 13(2), 141-154.

Page, L., Brin, S., Motwani, R., & Winograd, T. (1999). The PageRank citation ranking: bringing order to the web. Technical Report 1999-66, Stanford InfoLab.

Kleinberg, J. M. (1999). Authoritative sources in a hyperlinked environment. Journal of the ACM, 46(5), 604-632. doi:10.1145/324133.324140.

Seidman, S. B. (1983). Network structure and minimum degree. Social Networks, 5(3), 269-287. doi:10.1016/0378-8733(83)90028-X.

Burt, R. S. (1992). Structural Holes: The Social Structure of Competition. Harvard University Press.

Kundu, S., Murthy, C. A., & Pal, S. K. (2011). A new centrality measure for influence maximization in social networks. In Pattern Recognition and Machine Intelligence, Lecture Notes in Computer Science 6744 (pp. 242-247). Springer. doi:10.1007/978-3-642-21786-9_40.

Bonacich, P. (1972). Factoring and weighting approaches to status scores and clique identification. Journal of Mathematical Sociology, 2, 113-120. doi:10.1080/0022250X.1972.9989806.

Berman, A., & Plemmons, R. J. (1994). Nonnegative Matrices in the Mathematical Sciences. SIAM. doi:10.1137/1.9781611971262.

Sinkhorn, R. (1964). A relationship between arbitrary positive matrices and doubly stochastic matrices. Annals of Mathematical Statistics, 35, 876-879. doi:10.1214/aoms/1177703591.

Sinkhorn, R., & Knopp, P. (1967). Concerning nonnegative matrices and doubly stochastic matrices. Pacific Journal of Mathematics, 21, 343-348. doi:10.2140/pjm.1967.21.343.

Knight, P. A. (2008). The Sinkhorn-Knopp algorithm: convergence and applications. SIAM Journal on Matrix Analysis and Applications, 30, 261-275. doi:10.1137/060659624.

See Also

path_centrality() for closeness and betweenness on time-respecting paths; reachability() for temporal reach.

Examples

dn <- dynet(school_contacts)

centrality_series(dn, measure = "degree")
centrality_series(dn, measure = c("degree", "betweenness"))
centrality_series(dn, measure = "prestige", rescale = TRUE)
centrality_series(dn, measure = "prestige",
                  prestige = "indegree.rownorm")
centrality_series(dn, measure = "prestige", prestige = "domain")
centrality_series(dn, measure = "prestige",
                  prestige = "domain.proximity")
centrality_series(dn, measure = "prestige", prestige = "eigenvector")
centrality_series(dn, measure = "prestige",
                  prestige = "eigenvector.rownorm")
centrality_series(dn, measure = "prestige",
                  prestige = "eigenvector.colnorm")
centrality_series(dn, measure = "prestige",
                  prestige = "eigenvector.rowcolnorm")

# A seven-day window, stepped one day at a time.
centrality_series(dn, measure = "degree", step = 1, window = 7)

degree <- centrality_series(dn, measure = "degree")
summary(degree)


Dynet documentation built on Oct. 7, 2026, 5:08 p.m.