as_tna() warns when it leaves a cluster out. A
cluster becomes a tna model only when every node in it has a transition
to another node of the same cluster, so a one-node cluster, or a cluster
with a node whose transitions all leave it, was dropped from the result
without notice. The warning, of class
cograph_cluster_dropped, names each dropped cluster and the
nodes responsible. The clusters remain in the macro model.
cograph no longer registers a print() method for
mcml objects. Nestimate owns the mcml class
and its print method; with both packages registering one, the printed
form, and whether unknown arguments were rejected, depended on which
package was loaded last. mcml objects print through
Nestimate’s method (Nestimate is in Suggests).
as.data.frame() method for the results of
motifs() and subgraphs(). It returns the main
table by default (triad types for a census, node triples for
subgraphs()), and the counts per triad type with
what = "types".network_summary() no longer returns
hub_score and authority_score. Both columns
were always NA, because the code read fields that igraph
does not return, and igraph scales HITS scores to a maximum of 1, so the
maximum carried no information. network_summary() now
returns 16 statistics by default, 27 with detailed = TRUE
and 35 with extended = TRUE as well.
inst/CITATION lists the package authors as in
DESCRIPTION.
network_clique_size(), and with it
network_summary(extended = TRUE), crashed R with a C stack
overflow on directed networks such as student_interactions.
The crash is in igraph::clique_num() (igraph 2.3.3) on
directed input. Cliques ignore direction, so the count now runs on the
simple undirected network; the result is unchanged for undirected
input.
New dataset regulation_net: a synthetic weighted
transition network among ten learning regulation states, used in the
examples. Its help page states that it is synthetic and gives the recipe
that generated it.
The introduction vignette is rewritten section by section. Each
section opens with what cograph offers for the task, the tables are
corrected (the higher-order functions are marked as Nestimate’s,
mlna() is listed as the plot it is,
summarize_network() is listed with the cluster tools, and
plot_difference() replaces plot_compare()),
the disparity example uses the object splot() expects, and
the examples use regulation_net.
The README is redesigned: a statement of the package’s scope, a
quick start with four examples on regulation_net (plotting,
centrality, a hierarchical multi-cluster plot and higher-order
pathways), a summary of each area linked to its article, and a list of
the tutorials and articles. It replaces the long function tables and the
example figures from the June release.
Every citation in the help pages was checked against Crossref,
arXiv and DataCite. Two did not exist and are gone: a 2011 Procedia
Engineering paper cited by
centrality_node_contraction() and a 2008 conference paper
cited by group_centrality(), which now cites Puzis, Elovici
and Dolev (2007). centrality_pairwisedis() now cites the
paper that introduced the index (Potapov, Goemann and Wingender, 2008).
The brokerage role pages give the full Gould and Fernandez (1989)
reference, and 53 references are reduced to standard form, with DOIs
where they exist.
The centrality catalogue vignette no longer prints a reference paragraph under each of its 201 measures. Those paragraphs carried citations, page numbers and verification claims that could not be vouched for, so they were removed rather than kept unchecked. The 46 “Meaning” paragraphs that had grown into implementation notes (up to 728 words, arguing with source papers and with the Centrality Zoo’s transcriptions) are rewritten as short interpretations of what a high value means; the longest is now 62 words.
The “Centrality Zoo lookup” article is now “cograph and the
Centrality Zoo”. It compares the number of centralities in cograph with
nine other centrality packages and lists which of the Zoo’s 349 measures
centrality() implements, with the closest implemented
measure for each of the others. Every number on the page is computed
from the files in docs/zoo/.
plot_mcml():
the figure’s height can now be controlledplot_mcml() is drawn with a locked aspect ratio from
fixed layout numbers, so its shape never changed: a taller image only
added white space (an 8 x 12 in image used 59% of its height, 8 x 16 in
44%). And the argument documented for this, layer_spacing,
was accepted and then ignored – 2 and 30 drew
the same figure.
layer_spacing now works, and takes three kinds of
value:
NULL (default) – the automatic layout, exactly as
before. Existing figures are unchanged."fill" – the gap between the two layers is stretched so
the figure uses the full height of the image it is drawn on. Change the
image height and the plot follows. Shapes stay round; on a wide image,
where height is what binds, nothing changes.spacing. Overrides inter_layer_gap. A value
that overlaps the layers raises a cograph_layers_overlap
warning; anything else invalid raises
cograph_bad_layer_spacing.plot_htna() legend could still run off the
pageWhen a side legend is too large for its band it is scaled down to fit (new in 2.6.10). That scaling made one proportional adjustment, but a legend’s size is not proportional to its text size – symbols and padding scale differently, and font hinting makes text widths step – so under Cairo fonts (Linux, Windows) the result could still extend 0.5% past the edge of the page, while fitting exactly on macOS. The legend is now re-measured after each adjustment until the measured box fits. Legends that already fit are unaffected.
plot_htna()
gains legend_size, and its legend is no longer
oversizedThe “Groups” legend was drawn at a hard-coded cex = 1.4
– larger than the node labels it explains – with no argument to change
it. plot_htna() now takes legend_size (a
cex, default 0.8, the same default and meaning
as splot(legend_size = )), and the legend’s symbols are
sized from it. The default legend is therefore visibly smaller
than in 2.6.9; pass legend_size = 1.4 for the old
text size. Anything other than a single positive number raises a
cograph_bad_legend_size error.
plot_htna() legend overlapped the network and was
cut offWith a side legend (legend_position = "bottom", the
default, or "top", "left",
"right"), the “Groups” legend was drawn partly on top of
the network and partly off the page, at every figure size. Two
causes:
plot_htna() reserved the legend’s margin with
par(mar = ), which splot() replaces with its
own margins before drawing. The margin now travels as
splot(margins = ), so the band the legend needs really
exists.inset, which is a fraction of the plot region and moves the
box by only a sliver of its own height. The legend is now measured first
and anchored by coordinates in the centre of its band.The band is sized from the legend itself (rows, title, longest group
name, legend_size, and the device’s text scale) rather than
a fixed 6.5 lines. If a legend still cannot fit – many long group names
on a small figure – it is scaled down instead of overlapping the plot or
leaving the page.
A margin passed explicitly (mar = or
margins =) is left untouched. With
legend_position = "top" and a title, the title
is drawn on the outer lines of the top margin and the legend below it;
previously splot() centred the title in that margin, on top
of the legend. Corner positions ("topright" etc.) are
unchanged and still draw inside the plot box.
Every exported and internal function was read against its own roxygen block. Everything below is a documentation correction unless a heading says otherwise.
igraph is in Suggests, but examples
throughout the manual failed without it. 78 roxygen example blocks are
now
@examplesIf requireNamespace("igraph", quietly = TRUE).
Most built their example network with
igraph::make_ring(), igraph::make_star() or
igraph::make_graph("Zachary") and were found by reading the
examples. The last 21 were not: network_summary(),
network_girth(), network_radius(),
degree_distribution(), dyad_census(),
ego_networks(), shortest_paths() and the rest
of that family take a plain matrix and reach igraph internally through
to_igraph(), so nothing in the example text revealed the
dependency. They were found by removing igraph and running the
manual.
Verified by running every example in a library where
igraph (and therefore tna) could not be
loaded: 403 topics, no failures.
...
documented an argument that could not be passed32 verbs – the community detection family, the network_*
summaries, assortativity(), core_periphery(),
dyad_census(), ego_networks(),
shortest_paths(), k_shortest_paths(),
rich_club(), rich_club_local(),
robustness() and vulnerability() – documented
... as “additional arguments passed to
to_igraph()”. to_igraph() is
function(x, directed = NULL): it has no ...,
so anything passed raised an “unused argument” error. Each site now says
what is true. Where directed is already an explicit formal
the dots are documented as unused; where it is not, they are documented
as carrying directed and nothing else.
plot_mcml()’s title line was missing its leading
#, so R parsed it as a stray top-level string and roxygen
took the next ten lines of prose as the title.
man/plot_mcml.Rd now has the title it was meant to
have.
A roxygen block in blob-helpers.R had been separated
from its function by a later comment banner, which silently moved
.expand_repeated_nodes()’s @param and
@return onto the one-line accessor that followed it.
Among the factual fixes: core_periphery()’s example
printed a component that does not exist;
network_global_efficiency() documented the wrong value for
its own example; community_consensus() documented
... as reaching the detection method when it is discarded;
degree_distribution() marked three always-present
components as conditional; binarize(signed = TRUE) was
described as producing -1 rather than +1 or
-1; get_edges() did not say its
from/to are integer indices rather than
labels; several verbs did not name the classed condition they raise; and
the register_shape() and register_layout()
examples overwrote a built-in shape and layout for the rest of the
session. show_zero_edges in from_tna() and
from_qgraph() is documented as having no effect, which is
what it has.
The plot_tna() example that demonstrated custom colours
with rainbow(5) now uses
palette_colorblind(5): the reference manual was teaching
the one palette the package’s own style rules exclude.
British spellings in roxygen prose were normalized to the
en-US that DESCRIPTION declares (513 words
across 79 files; comments only).
scales has been dropped from Suggests. It
was used for exactly one call – scales::squish as the
out-of-bounds handler on the centrality heatmap’s fill scale – and that
behaviour is now a nine-line base R helper. Clamping is unchanged: a
finite value outside the limits is pulled to the nearer end, and
NA and the infinities are left alone.
show_zero_edges
now does somethingfrom_tna() and from_qgraph() have accepted
and documented a show_zero_edges argument for several
releases without ever reading it. Zero is how a weight matrix stores “no
edge”, so an edge whose weight rounds to zero at
weight_digits disappears from the plot, and there was no
way to prevent that. With show_zero_edges = TRUE such an
edge is now drawn at the smallest magnitude weight_digits
can express, carrying its sign; every other weight is untouched. The
default, FALSE, behaves exactly as before.
ld_radius, s_shell_a and
comm_r were unvalidated, so a value outside their domain
surfaced as an opaque failure from deep inside a kernel –
values must be length 1, but FUN(X[[1]]) result is length 0
for ld_radius = 0, and an all-NA result with a
coercion warning for a comm_r that is neither
"max_intra" nor a number. All three now raise a classed
cograph_bad_parameter error naming the argument and its
domain, matching the other tuning parameters in the centrality
surface.
Both were introduced before this release and are found by checking cograph against the eight packages that depend on it. All eight now check identically against this version and against the current CRAN cograph.
centrality() failed with arguments imply differing
number of rows on a network whose node table carries no
label column. The node-label fallback tested only for
NULL, and a source with no labels arrives as a zero-length
character vector instead, so the fallback to node indices never fired
and the node column came out empty beside full-length
measure columns. The fallback now checks that there is exactly one label
per node, which also rejects a label vector of the wrong length.
to_df() and to_data_frame() return
from, to and weight again. When
these stopped routing through igraph, the round-trip that used to
discard extra edge columns went with it, and the verb silently began
returning every column the edge table carried. They are the narrow
conversion verbs; to get the edge table whole – including columns
mutate_edges() computed – use as.data.frame()
on the network, which is the accessor and is unchanged.
centrality(measures = "hubbell") refused to report a
divergent result only when the largest real part of the scaled
spectrum reached 1. The Neumann series behind Hubbell diverges once the
spectral radius does, so a signed network carrying a complex
pair of modulus above 1 – or a real eigenvalue below -1 – was reported
as a confident number instead of NA. The guard now tests
the modulus. For a non-negative weight matrix the Perron root is real,
positive and equal to the spectral radius, so no unsigned network
changes.
centrality(measures = "delta_closeness", closeness_delta = 0)
scored every node of a disconnected graph 1. R evaluates
Inf^0 as 1 and is.finite(1) is
TRUE, so an unreachable pair passed the filter and counted
as fully close. The distance matrix is now screened as well. Only
closeness_delta = 0 is affected; every positive delta
already behaved.
centrality_expected_influence_1() and
centrality_expected_influence_2() read their result column
by $ partial matching, because the column is really
expected_influence_1_out. They now index the column by
name. Results are unchanged.
mcml()’s deprecation marker no longer depends on the
lifecycle package, which was not declared anywhere, and no
longer points at an SVG that the package does not ship – the HTML help
image for that topic was broken.
A simplex is a set of vertices, so a blob on its own cannot say which
state came first: A -> B -> B and
B -> A -> B drew the identical shape. With
dismantled = TRUE each panel shows one ordered pathway, so
plot_simplicial() can now draw the traversal three ways,
selected with direction_cues: a light-to-dark core ramp
along the path ("shade"), a ring whose highlight peaks on
the side facing the next state ("ring"), and an arrowhead
just outside each node aimed at its successor
("arrows").
direction defaults to NULL, which turns the
cues on exactly when dismantled = TRUE. A single combined
blob cannot express direction, so direction = TRUE with
dismantled = FALSE raises a classed
cograph_direction_needs_panels error rather than drawing a
misleading figure. legend draws the in-figure legend strip
beneath a dismantled grid and defaults to TRUE whenever the
cues are on.
An association-rule itemset and a clique of a simplicial complex are
sets: every member is co-equal and there is no target at all. These are
now carried through the pipeline as genuinely unordered —
ordered = FALSE — instead of having a source/target split
imposed on them, and their panels are titled with a member list rather
than a path. Ordered HON/HYPA/MOGen pathways are unaffected.
A panel-centring clip that could cut a blob off at the panel edge is
fixed, and an NSE-related R CMD check NOTE is resolved.
A permutation replicate that does not come back intact from a
parallel worker now raises an error. Previously only an explicit worker
error was caught; a worker killed by the operating system returns
NULL for its whole chunk with only a warning, and those
gaps were silently filled by recycling the surviving replicates – part
of a permutation null replaced by duplicates, with no warning and no
error.
The serial replicate path no longer leaves the session’s random
number generator switched to L’Ecuyer-CMRG. motifs() itself
never used that path, but the package’s own test suite did, which
changed the random fixtures of every test file that ran afterwards.
cores validation now raises a classed
cograph_bad_cores condition.
igraph is a suggested dependency. Functions that need it – including
to_igraph(), motif_census(),
detect_communities(), robustness(),
vulnerability(), rich_club() and
network_summary() – now raise a
cograph_missing_suggest error naming the function and how
to install igraph, instead of R’s bare “there is no package called
‘igraph’”. Functions that need no igraph, such as
motifs(significance = FALSE), subgraphs(),
extract_triads() and centrality(), keep
working without it.
motifs() on individual-level data (a tna
model, or an edge list with an actor column) now runs its permutation
null several times faster — on tna::group_regulation with
the default n_perm = 1000, 255s to 39s. The null builds
class counts directly instead of materialising and then re-counting a
row per node triple, and the triple indices are computed once per state
space rather than once per unit per replicate.
Results are unchanged: for a given seed the permutation
draws, and every count, expectation, z score and p value, are identical
to previous versions.
extract_motifs(significance = TRUE)The instance-level permutation null now counts (triple, class) pairs
directly instead of building a labelled row per triple per unit and
re-aggregating it by a pasted key. On tna::group_regulation
at n_perm = 1000, 428s to 32s. Results are unchanged for a
given seed.
motifs(cores = )The individual-level permutation null can now run across worker
processes. cores = 1 remains the default and is
byte-for-byte the previous behaviour.
cores > 1 gives each replicate its own L’Ecuyer-CMRG
stream, so a result depends on seed alone – not on the
worker count, and not on how replicates were chunked across workers.
Repeated parallel runs of one seed agree exactly, at any
cores. Those are a different set of draws from the serial
path, so p-values from cores > 1 will not match a
cores = 1 run of the same seed; both are valid permutation
nulls. Forking is used where available, with a PSOCK cluster on
Windows.
Measured on 10,000 simulated sequences (10 states) at
n_perm = 400: 103.0s at cores = 1, 20.5s at
cores = 10.
On Windows the null runs through a PSOCK cluster, which is exercised in the test suite on every platform.
A replicate that does not come back intact from a worker – an error,
a NULL from a killed process, or a wrong-length result – raises a
cograph_parallel_failure error naming the problem, rather
than being folded into the null matrix. If the available core count
cannot be detected, cores > 1 is used but reported with
a cograph_cores_undetected warning.
plot_mcml(expand = )The top layer can now be drawn at a finer resolution than the
partition: named clusters appear as their member states while every
other cluster stays a single node, and the bottom layer still shows the
partition, so an expanded state sits inside its cluster’s shell and is
linked to its own summary node. expand = "all" (or
TRUE) expands every cluster.
The expanded macro is re-counted from the input with a refined
partition, using cograph’s own cluster_summary(); a k x k
aggregate cannot be disaggregated after the fact. Only a pre-built
cluster_summary or mcml, which carries no
source to re-count from, falls back to
Nestimate::macro_network(), and says so with a
cograph_expand_unavailable error when that is
unavailable.
This also fixes a silent defect in the previous layout code: the top layer was indexed positionally against the cluster count, so a macro with more nodes than the partition was truncated to its first k rows and drawn under the cluster names — a confident, wrong figure with no error. It is now matched by name, and a macro wider than the partition that cograph did not build itself is refused.
Fixes for defects an adversarial review found in the 2.6.0 wrangling verbs. All of them were introduced in 2.6.0 except the last, which was older.
Zero is how cograph stores “no edge”, so it must never be compared
against a real weight. to_undirected(),
symmetrize(), spanning_tree() and
bind_networks() did exactly that, which silently deleted
edges in networks that carry negative weights — correlation and
partial-correlation networks above all:
-2 was deleted by
method = "max", because pmax(-2, 0) is
0;2 was deleted by
method = "min";method = "mean" halved every unreciprocated edge
against a phantom reverse arc;spanning_tree() returned an empty network on any
all-negative graph: Prim chose the right edges, then the mirroring step
compared each against zero and erased it;bind_networks(weight = "max") lost an edge only one
network had if its weight was negative.Presence is now carried separately from weight throughout: two values
are combined only where both arcs exist, and an unreciprocated edge
keeps its own weight. Combining to exactly zero raises a
cograph_edges_dropped warning rather than shrinking the
edge set in silence.
symmetrize() gains method = "mutual" for
the reciprocated-only rule (sna’s “strong”). method = "min"
no longer means that: it is a weight combination that keeps
unreciprocated edges, which is a different operation.
contract_nodes() counted an undirected within-group
edge twice, because a symmetric matrix holds every such edge twice. It
now aggregates the edge table, so a single edge of weight 3 becomes a
self-loop of 3, not 6.reorder_nodes() checked only the length of
order, so a non-permutation such as
c("A", "A", "B") produced duplicate labels or an internal
subscript error. It now requires an exact permutation.add_edges() and set_edges() accepted the
same undirected edge twice (A->B and
B->A), leaving the edge table and the weight matrix
disagreeing about how many edges exist. Both now reject it.mutate_nodes() and mutate_edges() could
overwrite the columns the structure is keyed on (label,
id, from, to), leaving the node
table, edge table and matrix describing different networks. Those
columns are now reserved. A weight mutated to zero drops the edge with a
classed warning rather than leaving a row the matrix does not have.cograph_isolates_created, as the documented invariant says:
the nodes are kept, so they are newly isolated.split_components() and contract_nodes() no
longer fail on a zero-node network, and the matrix-level verbs raise
cograph_bad_selection on non-finite weights instead of an
internal error several frames later.proportion and density reject 0, and
top, k and min_size reject
fractional values, as documented. complement_network()
rejects weight = 0, which would have produced an empty
complement.bind_networks(directed = FALSE) on directed input
returned an “undirected” network whose matrix was asymmetric and whose
edge table was empty; it now symmetrises the inputs first. It also
carries x’s metadata, estimation data and node attributes
instead of dropping them.as.data.frame() on one keeps the extra edge columns.reverse_edges() and normalize_weights()
("max", "sum", "minmax") keep
extra edge columns; they map edges one-to-one, so there was no reason to
lose them. network_to_igraph() carries node columns across
as vertex attributes, so attributes added with
mutate_nodes() survive
keep_format = TRUE.parse_matrix() read an undirected matrix from the
strict upper triangle, so a self-loop on the diagonal was dropped from
the edge table while $weights kept it — the two disagreed,
and a later matrix-level verb could resurrect the loop. Undirected
self-loops are now edges. This predates 2.6.0.The verbs that reshape a network are now a family with one contract:
any supported input, options as named arguments, a
cograph_network back (or the input format with
keep_format = TRUE), and classed conditions. See
?network_wrangling.
as.data.frame() on a cograph_network
returns the tidy edge table, with endpoints as labels rather than
internal indices, and as.data.frame(what = "nodes") returns
the node table. No caller needs to reach into the object with
$ any more.
threshold_edges(), binarize(),
symmetrize(), normalize_weights(),
invert_weights().to_undirected(), to_directed(),
reverse_edges(), remove_isolates(),
contract_nodes(), split_components(),
select_k_core(), spanning_tree(),
complement_network(), reorder_nodes(),
rename_nodes().add_nodes(), remove_nodes(),
add_edges(), remove_edges(),
mutate_nodes(), mutate_edges(),
bind_networks().add_edges() shares its name with
igraph::add_edges(); call cograph::add_edges()
when igraph is attached.
Node expressions gain is_isolated,
is_source, is_sink, is_leaf,
is_cut, local_transitivity and
local_triangles, and any measure centrality()
computes can now be named directly —
select_nodes(x, harmonic > 0) works, as does
select_top(x, n = 5, by = "leverage"). Edge expressions
gain is_loop, is_multiple,
is_reciprocal, weight_rank,
from_community and to_community, and
select_edges(by = ) accepts the endpoint metrics.
filter_nodes(), select_nodes(),
select_edges(), to_df(),
to_network() or to_igraph().
network_to_igraph() built the graph from the edge list, so
every node after the last edge endpoint disappeared and the label
assignment then failed.as_cograph()
re-detected the result as directed and every downstream consumer saw
half the strength.set_edges() and set_nodes() rebuild the
stored weight matrix, so to_matrix() can no longer return
the pre-edit network, and set_edges() keeps extra edge
columns.session,
time, …) survive as_cograph() and are usable
in filter expressions, as documented.keep_format = TRUE returns an
empty object of the input type instead of erroring with “No such edge
attribute”.keep_format = TRUE on a tna model returns a rebuilt tna
model.to_matrix() and to_data_frame() no longer
route through igraph.filter_edges(), select_edges() and friends now
keep every node, matching igraph::delete_edges() and
tidygraph, and warn (cograph_isolates_created) when the
filter left a node without edges. Use remove_isolates(), or
keep_isolates = FALSE, for the old behaviour..keep_isolates and .keep_edges are renamed
to keep_isolates and keep_edges. The dotted
names still work and warn.cograph_bad_selection rather than warnings that return
something plausible: unknown node names, out-of-range or fractional
indices, a between that is not two node sets, an unknown
measure in by.filter_nodes() computes only the measures its
expression names. It used to compute all twelve, including HITS, on
every call.Matrix is no longer imported (nothing used it after the
port); the committed test-network fixtures are now tracked; two tests no
longer need withr.The whole centrality surface (centrality() and its 191
centrality_* wrappers, edge_centrality(),
centralization(), group_centrality(),
dispersion(), estrada_index(),
trophic_incoherence(), and the centrality vocabulary of the
wrangling verbs) now computes on cograph’s own kernels. Every input is
turned once into a dense, labelled weight matrix
(R/kernels-graph.R); no igraph object is built. igraph
stays in Suggests for input conversion of igraph objects and for
community detection and layouts.
Equivalence was verified measure by measure against the igraph-backed
implementation on a golden corpus of 62 unsigned networks (30 real, 32
synthetic edge cases) under every mode and weighting, at relative
tolerance sqrt(.Machine$double.eps), with the following
documented exceptions.
flow_betweenness still needs igraph and raises
cograph_needs_igraph when it is not installed.A'A) is not primitive are not unique; igraph returned a
random member of the eigenspace and sometimes failed to converge. The
native kernels are deterministic and verified by eigen-residual tests
instead.any_multiple() had been called on the object, which
the old centrality() always did.alpha now computes on weighted graphs with self-loops;
igraph 2.3.3 errored there.1e-18 gives identical betweenness.
igraph’s absolute epsilon does not.simplify = FALSE or "none"
now sum them (the old adapters summed them when assembling an adjacency
anyway). Only degree on a multigraph changes, counting a
parallel pair once.cograph_directed_unsupported.New internal kernels: edge betweenness, Barrat transitivity, average neighbour degree, articulation points, bridges, ego masks, and a loop-preserving coreness matching igraph’s convention.
Test infrastructure: a versioned corpus of test networks under
tests/testthat/networks/ (32 real, 35 degenerate, plus
local tiers), a golden-file comparison harness, a scale-invariance
property test, and a CI job that runs the centrality tests with igraph
uninstalled.
centrality_trust_pagerank(), Sheng, Zhu, Wang,
Wang and Hou’s trust-PageRank. PageRank’s even split of a node’s score
among its neighbours is replaced by a trust-value mixing a
similarity ratio with a degree ratio:
T(i,j) = (1-k) s(i,j)/sum_{l in N_j} s(j,l) + k d_i/sum_{l in N_j} d_l,
fed to
TPR_i = (1-alpha)/n + alpha sum_{j in N_i} T(i,j) TPR_j,
with s the fixed point of SimRank restricted to the lines
of the graph. New parameters tpr_alpha (0.85),
tpr_k (0.85), tpr_decay (1),
tpr_tol (1e-14) and tpr_max_iter (1000).doi:10.3390/a13110280,
which is what cograph implements. A reader following the Zoo’s reference
lands on a different measure.0.1 that initialises the lines. The base
case s(a,a) = 1 is then the only inhomogeneous term, and it
reaches a line exactly through the triangles that line carries, so the
recursion contracts even at the source’s C = 1. Pinning
non-adjacent pairs at 0.1 instead reproduces neither
published fixture.S_v column is the sum of the paper’s own
rounded cells rather than the rounded sum, a convention node 5 alone
separates. Table 5 on page 10 prints two top-ten rankings: the karate
club’s reproduces in order at all ten positions, and the kite’s up to
three exact ties forced by its own automorphism group.NA there rather than inventing
one. The recursion is then homogeneous, its least non-negative
fixed point is zero, and the similarity ratio is 0/0. Every
path, tree, star, even cycle and complete bipartite graph is in that
class, and so is the Petersen graph. Unlike
centrality_dil() and centrality_lhc(), the
quotient is not determined by its numerator, so the 1/d_j
fallback would silently turn the measure into a degree-ratio PageRank
over the whole class; this follows centrality_iec()
instead. An isolate is not in the class and keeps the bare
(1 - alpha)/n.tpr_max_iter raises cograph_no_converge.C
does not matter is false for the converged recursion. It holds
for a homogeneous recursion; the base case makes this one affine, so
C enters the resolvent. Moving it from 1 to 0.5 moves the
karate club’s similarity ratios by up to 0.141.type = "all" excludes it unless asked for.centrality_dil(), Liu, Xiong, Shi, Shi and Wang’s
degree and importance of lines. A node’s degree is corrected by the
share it can claim of the importance of the lines touching it:
I_e(m,n) = (k_m - p - 1)(k_n - p - 1) / (p/2 + 1) is the
importance of a line, p being the number of triangles
carrying it; W(i,j) = I_e(i,j) (k_i - 1)/(k_i + k_j - 2) is
the endpoint’s share of it; and
DIL(i) = k_i + sum over the open neighbourhood of W(i,j).
No new parameter.lambda is p/2 + 1, and a
text-layer reading gets it wrong. The stacked fraction extracts
from the published PDF as lambda = 2p + 1, in the original
as much as in the Almasi and Hu (2019) reproduction of it. The equations
were read from 300 dpi page images, and the paper’s own worked example
settles it in printed prose: at p = 1 it writes
lambda = 1/2 + 1 = 1.5 and I_e45 = 8/3.I_e45 = 9 and
8/3; figure 2 on page 211 prints four edge importances and
then L_v2 = 26/9 and L_v5 = 52/15; and table 3
on page 217 prints a value for all 21 nodes of the ARPA network. The
figure-1 and figure-2 values match as exact fractions with no tolerance
at all, and all 21 table-3 values match under the paper’s own
four-decimal rounding, in the descending order it prints them. The edge
list read off figure 6 is corroborated by the paper’s own degree column
at all 21 nodes.k_i + k_j - 2
vanishes only when both degrees are one, and there the line’s importance
is exactly zero as well, so every admissible share of it gives the same
contribution. The share is written as zero with the test taken before
the division, and both nodes score 1. The source is silent on the case;
the choice follows centrality_lhc(), not
centrality_iec(), and the help page says why.p can never exceed either endpoint’s degree minus one, and
the network’s total excess over degree is exactly the total importance
of its lines — the two endpoint shares of a line sum to one. Raw scores
are component-local: the measure never reaches past a node’s second
neighbours.type = "all" includes it.centrality_iec(), Friedkin’s immediate effects
centrality. A node is scored by how quickly the rest of the network’s
influence reaches it:
c_IEC(j) = (n - 1) / sum_{i != j} m_ij, where
M = (I - Z + E Z_dg) diag(1/c) is the mean first passage
time matrix of the influence chain W = A / rowSums(A),
c is W’s left eigenvector at eigenvalue one
and Z = (I - W + 1 c')^-1 is the fundamental matrix. The
sum runs down column j, so a high score marks a
node the network reaches fast. No new parameter.a_ii = 1 before row-normalising, a construction it
attributes to French (1956) and states twice on page
1494. Its footnote 10 gives the reason — a strong network with
w_ii > 0 must be regular, meaning aperiodic — and its
footnote 9 the periodic counterexample a zero diagonal admits.centrality_markov(), and
the difference is not a rescaling. The package’s own candidate
ledger recorded for several rounds that the two differed only in an
n versus n - 1 numerator. That is wrong:
markov also omits the self-loop. The numerator is
a constant factor and cannot reorder anything; the self-loop can and
does. On the five-node star markov gives
1.25, 0.161, ... where iec gives
0.5, 0.08, ..., and the two rank the nodes differently on 2
of the 21 connected five-node graphs. markov is unchanged;
the two ship side by side, and each help page now points at the
other.c is
undetermined and diag(1/c) undefined. Worse, the closed
form does not announce the failure: for i and
j in different classes z_ij = 0, and equation
(11) returns the entirely finite m_ij = z_jj / c_j where
the true mean first passage time is infinite. Rather than publish a
finite wrong number, iec tests the chain by boolean closure
before any solve and returns NA at every node with a
cograph_undefined_measure warning. In practice the
requirement is a connected undirected graph or a strongly connected
digraph. A one-node graph is NA too, equation (20) dividing
by n - 1 = 0; an empty graph returns no scores.W is a matrix of directed
influence and row i is what actor i attends
to; there is no in/out/all variant, so mode,
cutoff and invert_weights are ignored. Weights
are dropped deliberately — a_ii = 1 is calibrated against
a_ij = 1, so a rescaling of the weights would silently
re-weight each actor’s self-reliance against the network — and loops in
the input are absorbed by the mandated diagonal while parallel edges
collapse. The measure is marked costly and is therefore held back from
type = "all".0.1875 the paper prints as
.187. The 105 printed values of the companion TEC column
reproduce as well, three of them needing the same truncation rule.centrality_lhc(), the Lhc index of Wang, Yang,
Liu and Ma. A node’s influence is
C(v) = sum_{u in Phi(v)} k_u (1 + TP(u)) / d^2(uv), a sum
over the ball Phi(v) of radius lhc_radius in
which each member contributes its degree, inflated by its share of the
network’s triangles, discounted by the square of its distance; the index
is Lhc(v) = sum_{w in tau(v)} C(w), that influence summed
over the open neighbourhood. The triangle share is
TP(u) = NTS(u) / TNTS with NTS(u) the number
of triangles containing u. New parameter
lhc_radius.TNTS, not the number of
triangles, and the paper settles it rather than the Zoo.
Immediately after defining TNTS the source writes that “the
total number of triangle structure exists in the network are
1/3 * TNTS”, so TNTS = 3 * Delta and the share
sums to exactly one over the nodes. Entry 2.221 of the Centrality Zoo
transcribes the structure of both equations correctly but names the
denominator “Delta, the total number of triangular
structures in the network”, which read literally is three times too
small: on the Krackhardt kite that reading scores node 1 at 125.45 where
the paper gives 100.15. cograph follows the paper.lhc_radius is the source’s own parameter, exposed with
the source’s default. The paper writes it d, states on page
4 that it “is set to be 2”, and sweeps it in section 3 over eleven real
networks, reporting “the optimal value of d is about 2-3”. At
lhc_radius = 1 the ball collapses to the neighbours; at or
above the graph’s radius the score stops moving. The domain is a whole
number of at least one and anything else raises
cograph_bad_parameter.TNTS = 0, making TP(u) a
0/0 at every node, and the source never mentions the case.
Since TNTS is a sum of nonnegative counts it vanishes
exactly when every numerator does, so there is no share to distribute:
TP is written as zero and the index reduces to the pure
degree-over-squared-distance sum. The test is made before any division,
so no 0/0 is evaluated.TNTS is a global sum, so attaching a disconnected component
that carries a triangle rescales every score, while attaching one with
no triangle – an isolate included – changes nothing. An isolate scores
zero because its neighbourhood is empty; a singleton and every node of
an edgeless graph score zero for the same reason.mode,
cutoff and invert_weights are ignored, and
normalized = TRUE max-scales, the source stating no
normalization.centrality_rsp_betweenness(), the simple
randomized shortest paths betweenness of Kivimaki, Lebichot, Saramaki
and Saerens. A Boltzmann distribution over the absorbing walks from
s to t is tilted by an inverse temperature
away from the unbiased random walk and towards low-cost walks, and a
node scores the expected number of visits it receives summed over every
ordered source-target pair,
bet_i = sum_{s,t} (z_si / z_st - z_ti / z_tt) z_it with
Z = (I - W)^-1 and
W = (D^-1 A) o exp(-beta C). New parameters
rsp_beta and rsp_cost.Z, and Algorithm 1
takes a strongly connected graph as its input, but the text below
equation (9) settles the general case: the derivation “holds only if
there exists a path from s to t. Otherwise,
naturally, eta_ij(s, t) = 0.” cograph evaluates the closed
form masked by reachability, which reproduces equation (15) to machine
precision whenever the graph is strongly connected and applies the
source’s own zero rule when it is not.D^-1 is undefined there, so cograph writes
that row of P^ref as zero, which is the paper’s own killed
random walk read at a node where the walker dies at once; Z
then has z_ii = 1 and the arithmetic gives exactly
1 - 1 = 0. An isolate, a singleton graph and every node of
an edgeless graph score zero for that reason.
NetworkToolbox::rspbc() raises an error on such input and
plot-side current_flow_betweenness returns
NA on disconnected input; this measure can answer where
those cannot, because (I - W) stays nonsingular whatever
the connectivity.NetworkToolbox::rspbc() masks only the reciprocal half of
the term and leaves the n Diag(Z') half counting every
source, so the two agree exactly on strongly connected input and part
company on a disconnected graph. That function additionally rounds to
zero decimals and shifts so its minimum is one, post-processing that is
nowhere in the paper and is not copied here.rsp_beta defaults to 0.01, which is
not the source’s number: the paper fixes no default and
treats it as a modelling choice. 0.01 is the value
NetworkToolbox::rspbc() recommends, adopted so the two are
comparable out of the box; it sits near the random-walk end, so raise it
towards 1 and beyond to move the reading towards shortest paths. The
domain is rsp_beta > 0 and values outside it raise
cograph_bad_parameter.rsp_cost chooses how a weight becomes a cost, which the
source leaves free: "inverse" (default) sets
C = 1 / w, reading a weight as an affinity, as the CRAN
reference hard-codes; "weight" sets C = w,
reading it as a distance. The two coincide on a binary graph. Negative
and non-finite weights raise cograph_bad_input, Algorithm 1
requiring a non-negative cost matrix.mode,
so a reversed input generally scores differently. Marked
costly: one dense n x n inverse, which the
source itself calls the computational bottleneck at O(n^3)
time and O(n^2) memory.beta approaches zero from
above on an undirected network; it holds, and does so at first order in
beta.centrality_hcc() and
centrality_ehcc(), the hybrid characteristic centrality of
Liu and Zheng and its extension. HCC adds two normalised halves: the
extended degree
delta * k + (1 - delta) * sum of the neighbours' degrees
over its largest value, and the round in which an E-shell peel
removes the node over the number of rounds. EHCC is the
closed-neighbourhood sum of HCC, the focal node counted once. New
parameter hcc_delta, default the source’s 0.5.S_p = arg max while the same
sentence calls S_p “the set of minimum nodes” and the
paper’s table 2 heads its column “Minimum extended degree” with the
increasing values 2, 2.5, 3, 4.5, 5, 6. The minimum reading reproduces
every printed row; the literal maximum peel deletes a different set
first and finishes in four rounds instead of six.k^ex
and k^ex_max are the original-graph values. The paper’s
node d settles it: its original 9.5 gives the printed 1.86,
its residual 6 gives 1.55.k^ex_max and pos_max are single global
constants, so a disconnected addition rescales the two halves
independently rather than by one common factor.EHCC(g) = 10.01 for its figure 1, where the exact value
661/66 is 10.015151… and rounds to 10.02. That printed cell
is the truncation, but six other printed cells require rounding, so no
single convention reproduces all twenty table 3 entries. The other 85
printed values reproduce.hcc_delta in [0, 1], so it always leaves in
the first round; an edgeless graph makes equation (4)’s first term
0/0, written as zero, so every node of an edgeless graph —
a singleton included — scores exactly 1. hcc_delta outside
the source’s stated [0, 1] raises
cograph_bad_parameter rather than being extended.centrality_ked(), the KED method of Chen, Xiao,
Zeng and Zhang: the degree, weighted by one plus the normalised entropy
of the neighbours’ degrees, times exp(K / N) for the
neighbour-degree sum K and the whole graph’s order
N. Two nodes with the same degree and the same number of
second neighbours are separated by how evenly their neighbours carry the
onward paths. It takes no parameters: equation (6) is a bare
product.k outcomes, so the base cancels top
and bottom. A full base-ten reading gives identical scores, which is
asserted to 60 digits on every verification fixture.N is the vertex count of the whole
network, and exp(K / N) shrinks a large neighbour-degree
sum more than a small one, so adding a disconnected component is not a
rescaling: on a seven-node example in the tests, one extra isolate swaps
two nodes’ places.1 <= D <= e is not general. It needs
K <= N, which holds on the sparse toy networks of its
figure 1 and on only 1,598 of the 5,532 verification fixtures; every
node of the five-clique has K = 16 against
N = 5. cograph implements the formula, not the range claim,
and raises an error rather than returning Inf if the
exponent ever leaves the range of exp().1 + from E, divides K
by the largest cluster degree instead of by N, and adds
tunable exponents that appear nowhere in the paper. On the source’s own
figure 1 that reading gives 13.5914 and 6.5672 where the paper prints
25.9187 and 19.2212. cograph implements the paper and offers no Zoo
variant.0/0 for its
normalised entropy and takes zero, and an isolate scores zero.centrality_lnc(), the local neighbor contribution
of Dai, Wang, Sheng, Sun, Khawaja, Ullah, Dejene and Duan: the chance
that a node picking a neighbour at random picks a given one and misses
the rest, scaled by its degree, multiplied by the sum of its neighbours’
degrees weighted by their degree centralities. It takes no parameters,
which the source advertises as one of its contributions.1 / (n - 1) comes from the source’s degree centrality,
where n counts every node in the network rather than in the
component, so adding a disconnected component multiplies every score by
(n - 1) / (n' - 1). The ranking is untouched; the raw
values are not.j = 1 to
k, and k is described three incompatible ways:
the prose calls it the number of nearest and next nearest neighbours,
Algorithm 1 sets it to the degree, and equation (5) read literally
carries one factor of the degree too many and returns 6.75 where the
paper prints 1.6875. Inverting each of the eleven printed influences
gives k = d^2 in (4) and k = d in (5), the
reading implemented here; the alternative literal split of the two
factors gives the same product, so the measure itself is
unambiguous.1 / d, is undefined, and no result is silently
NaN.centrality_ira(), the iterative resource
allocation of Ren, Zeng, Chen, Liao and Liu: every node starts with one
unit of resource and hands it repeatedly to its neighbours in proportion
to the receiver’s centrality until the amounts stop moving.
ira_mass, ira_alpha, ira_tol and
ira_max_iter expose the source’s theta,
alpha, epsilon and iteration bound. Every
non-empty column of the allocation matrix sums to one, so a component’s
scores sum to its vertex count.centrality_iira(), the improved variant of Zhong,
Liu and Shang, which scales each share by 1 - (1 - beta)^k.
That factor is strictly below one, so the resource decays geometrically;
the source fixes the step count instead of a tolerance.
iira_beta and iira_steps default to the
source’s 0.2 and 50, the raw I(50) is returned so the
printed example is reproducible, and normalized = TRUE
max-scales it. Raw IIRA scores from different connected components are
on different exponential scales and must not be compared.ira reports non-convergence instead of hiding
it. The allocation matrix is a reversible walk, so on a
bipartite component it has an eigenvalue of exactly -1 whose coefficient
in the all-ones start is the difference between the two class sizes.
When those differ, the resource settles into a period-two cycle and no
tolerance is ever met: the three-star alternates for ever between
3, 1/3, 1/3, 1/3 and 1, 1, 1, 1.
centrality_ira() then stops at ira_max_iter,
raises a classed cograph_no_converge warning naming the
largest remaining change, and returns that parity-dependent iterate.
Neither source mentions this case.15/8 up to
1.88, while the iteration approaches that limit from below
and returns 1.8749998, which rounds to 1.87.
No author software exists for either measure.centrality_neighbor_distance(), the neighborhood
centrality of Liu, Tang, Zhou and Do: a benchmark centrality plus its
decayed sums over the non-backtracking walks that leave the node.
nd_order, nd_decay and nd_mass
expose the source’s n, a and
theta, and the defaults (degree, two steps, 0.2) are the
setting the Centrality Zoo calls neighbor distance centrality.centrality_relative_entropy(), which turns
several indexes into discrete distributions and returns the unit-sum
distribution with the smallest total relative entropy to all of them:
the normalised geometric mean of the index distributions.re_indexes chooses the constituents from six the source
both defines and gives an evaluating direction (degree, closeness,
betweenness, constraint, and the two post-deletion destructiveness
indexes n_components and largest_component);
re_negative overrides which of them are read as “smaller is
more important”. The default reproduces the published Kite study’s
four-index column, and all three of its printed integrated columns are
reproduced.cograph_undefined_index error rather than returning
zeros.centrality() no longer lets one undefined measure end a
whole tier. A measure named in measures or
include still raises, but when type = "basic",
"extended" or "all" supplied it, an undefined
result becomes a cograph_undefined_measure warning and an
NA column, matching what the community-partition measures
already do without membership.centrality_dkgm(), whose mass is the degree
k-shell index: the original degree plus a shell number refined by the
stage at which the node left that shell during the k-shell peeling.dkgm_radius = 2 matches the paper’s printed
nine-node example, which the implementation reproduces along with its
removal stages, improved shell indices and DK values.
"auto" applies the paper’s own half-mean-distance rule with
cograph rounding.k versus at most k,
the one-shell placement of isolates, and the single global stage
denominator, which makes raw scores depend on disconnected
components.centrality_mixed_gravity() and
centrality_extended_mixed_gravity(), using focal core
numbers and partner degrees, with an optional outer sum over immediate
neighbors.centrality_mcgm() with the published adaptive
coefficient and default radius two, plus explicit radius and coefficient
overrides.centrality_spectralrank() with optional scalar or
node-specific diagonal priors. Scores use outgoing neighbors in an
augmented graph.centrality_controlrank(), the smallest eigenvalue
of each grounded symmetric row-Laplacian, retaining original degrees
after node deletion.centrality_map_equation() for fixed one-level or
leaf-module partitions, with recorded node and unrecorded link
teleportation.paper includes
module-exit flow as defined in the equations; infomap
reproduces the author’s visit-only implementation and published table.
Both conventions have independent numerical verification. Stable
arithmetic retains extremely small scores.centrality_ninl() with the full finite iteration
family (default three) and optional radius overrides. The initial score
sums degrees within the ceiling of average path length, then propagates
through neighbors.centrality_localized_bridging() and
centrality_extended_local_bridging(): one-hop and two-hop
ego betweenness multiplied by the original-graph bridging coefficient.
Uses simple, undirected, unweighted topology; the two-hop variant is
marked costly.local_bridging retains its existing inverse-degree product,
which is a different score.centrality_beta_measure() with positive and
negative directed variants. Successors share one unit equally among
their predecessors; the negative variant reverses the graph. Loops and
duplicate arcs are removed, weights ignored, and isolates score
zero.centrality_expected_force() and
centrality_modified_expected_force() from Lawyer’s
two-event definition, with explicit sequence multiplicity, boundary-edge
counting, direction and exhausted-force conventions.expected
computes neighbor-degree sums and remains available under that
definition. ExF now maps to expected_force; the original
ExFm candidate maps to the modified function.centrality_proximal_betweenness() with source,
target, sum and union variants from Brandes. Uses directed unweighted
shortest paths, excludes endpoints, and explicitly preserves
ordered-pair raw scaling.centrality_bridging_capital() with a finite walk
horizon and optional source-destination information values. It follows
Jackson’s single-entry deletion definition using transmission
probabilities between zero and one.centrality_coleman_theil(), measuring
concentration of Burt’s dyadic constraints using mutual tie weights.
Follows the author’s explicit isolate-zero and single-contact-one
conventions, with organizational multipliers fixed at one.centrality_x_degree(), counting four-edge
nonbacktracking walks centered at each node using original neighbor
excess degrees. It uses the simple undirected unweighted skeleton and
supports maximum normalization.centrality_linerank(), using directed line-graph
walks or the ordinary undirected line graph clarified by Kosa et
al. (2015).linerank_aggregation choices. Supports loops and remaining
parallel edge states with documented conventions, uniform dangling
redistribution and damping in [0,1).centrality_random_walk_decay() with
rwd_decay and optional rwd_node_weights.
Scores sum discounted first-arrival probabilities, including the
starting node. Retains directed flow, weighted transitions and loops;
sinks terminate the walk without restarting it elsewhere.centrality_graph_regularization() with finite
nonnegative grc_gamma, default one. Computes reciprocal
diagonal entries of the inverse regularized weighted Laplacian. Isolates
and zero regularization score one; disconnected components are
independent before normalization.centrality_adaptive_leaderrank(), weighting every
destination by its original open-neighborhood H-index and adding a
ground node with H-index one. Retains source total mass N and omits the
ground score without redistribution. Zero H-indices receive zero
stationary scores; an all-zero H-index vector yields NaN.alr_h_mode selects the H-index convention: all
(default), out or in. The paper leaves its directed H-index choice
unspecified; these choices are explicit cograph conventions, while
resource flow retains input arcs. The focal node is excluded from the
H-index calculation.centrality_weighted_leaderrank() with finite
wlr_alpha, default one. Original directed arcs retain unit
weight and ground-to-node weights depend on original in-degree. Input
edge weights are ignored. Undirected edges are represented as opposite
arcs.centrality_global_structure() (GSM),
centrality_hybrid_global_structure() (H-GSM) and
centrality_improved_global_structure() (IGSM). GSM uses
coreness; H-GSM combines degree and coreness; IGSM uses degree. The
latter two use their published adaptive distance exponents. All three
use the simple undirected skeleton, with explicit zero contributions for
unreachable partners and global size/means that include isolates.centrality_exogenous() with degree, betweenness
and adjusted reverse-closeness bases. Measures the contribution to other
nodes’ centrality when the focal node is deleted. Supports directed base
directions and retains negative betweenness contributions.centrality_improved_closeness() with
icc_alpha in [0,1], default 0.2, following Luan et al.’s
shortest-path multiplicity formula. Uses the simple undirected skeleton;
alpha zero recovers ordinary normalized closeness on connected graphs.
Disconnected graphs and singletons score zero under explicit cograph
conventions.centrality_cda() with cda_alpha,
default 0.5. Returns Wang et al.’s propagation-capability score using
degree, strength, Barrat clustering and weighted neighbor contributions.
The full weighted calculation retains original weight units and a global
maximum weight.centrality_extended_coreness(), the two-step
aggregation of original core numbers, and
centrality_extended_gravity(), the sum of immediate
neighbors’ raw k-shell gravity scores. Both use the simple undirected
skeleton and assign isolates zero.gravity_radius, default three
as in Ma et al. The radius applies around each neighbor before the outer
sum. Independent checks use NetworkX cores and distances and an
exhaustive core-number oracle on small graphs, covering multiple radii
and input projections.centrality_resistance_curvature(), implementing
Devriendt and Lambiotte’s conductance-weighted node curvature component
by component. Raw scores can be negative; isolates score one. Directed
projection, zero conductances, normalization and numerical limits are
documented.centrality_dynamics_sensitive() with
ds_beta, ds_mu and ds_steps,
including the full recovery-rate family from Liu et al. Recovery rate
one recovers the finite-diffusion formula listed by Zoo; recovery zero
supports the paper’s SI case. These are linearized scores, with their
interpretation and numerical limits documented explicitly.centrality_malatya(), the static degree-ratio
sum. On nonisolated nodes it is exactly the reciprocal of the bridging
coefficient. Both additions use the simple undirected skeleton and
assign isolates zero.centrality_diffusion_centrality() with
diffusion_q and diffusion_steps. It evaluates
Banerjee et al.’s finite weighted walk sum, separate from existing
diffusion degree and the TNA power series. Directed edges follow their
outgoing orientation; weights, loops and repeated walks are supported.
Probability interpretation and default parameter choices are documented
explicitly.centrality_dynamical_importance(): relative
spectral-radius loss on node deletion, recomputed directly. It supports
nonnegative directed weighted graphs, removes loops and returns NaN when
the original radius is zero. This costly measure is held back from the
default all tier.centrality_volume() with
volume_radius and centrality_mcc(), also
available through centrality(measures = ). Both use the
simple undirected skeleton. Volume sums original degrees over closed hop
neighbourhoods; MCC sums factorial contributions from maximal
cliques.type = "all" tier because clique enumeration has
exponential worst-case cost.centrality_truss(),
centrality_mdd(),
centrality_bridging_coefficient(),
centrality_godfather() and
centrality_support(), also available through
centrality(measures = ). They use the simple undirected
skeleton. mdd_lambda controls the exhausted-degree weight;
truss numbers use the k-2 triangle convention.semilocal measure
on simple undirected graphs. Corrected parameterized Zoo lookup calls
and replaced unsupported equivalence claims based solely on rank
correlation.docs/zoo/parameter_candidate_status.csv.Five measures chosen from the Centrality Zoo correlation study
(Shvydun 2025; 349 measures, 648 ICON networks, average Kendall tau) as
the ones with the lowest rank redundancy against what
centrality() already computed (maximum tau with any
existing measure in parentheses). All are implemented in base R on
matrices (R/kernels-batch7.R) with thin igraph glue and one
exported verb each (R/centrality-batch7.R).
centrality_distance_entropy() (tau 0.30) — Stella &
De Domenico (2018). Normalised Shannon entropy of a node’s hop-distance
profile; closeness is the mean of that profile, this is its spread. The
normaliser is log(M - m + 1) so a uniform profile scores
exactly 1 (the printed formula’s log(M - m) is undefined
for two distances).centrality_local_dimension() (tau 0.50) — Pu et
al. (2014). OLS slope of ln B(r) on ln r, ball
including the centre. Reproduces the worked example of Wen & Deng
(2019) exactly (0.9231). Lower = more influential.centrality_local_information_dimension() (tau 0.38) —
Wen & Deng (2020). Entropy-weighted local dimension over boxes up to
half the eccentricity. Higher = more influential. Single-box nodes use
the paper’s discretised derivative.centrality_modularity_vitality() (tau 0.40) —
Magelinski, Bartulovic & Carley (2021).
Q(G, C) - Q(G - i, C \ i) under a fixed partition; positive
= community hub, negative = bridge. Closed-form vectorised update (one
matrix product for all nodes); matches brute-force
igraph::modularity() after deletion on random directed,
undirected and weighted graphs. Requires membership;
wrong-length input raises cograph_bad_membership.centrality_neighborhood_connectivity() (tau 0.64) —
Maslov & Sneppen (2002). Mean neighbour degree, isolates 0; equals
igraph::knn(weights = NA), with mode
support.The three distance-scaling measures are hop-count measures and ignore
edge weights (as gravity and
collective_influence already do); they share one unweighted
all-pairs matrix per centrality() call.
The twelve measures the batch 7 lookup listed as “on the way” are now
implemented (thirteen centrality() measures), each verified
against an exact brute-force definition or the source paper’s own
numbers, plus an independent Python reference written from the paper
(kept in local_testing_and_equivalence/batch8/, not
shipped). Base-R kernels in R/kernels-batch8.R, verbs in
R/centrality-batch8.R.
centrality_shapley_game1(), _game2(),
_game3() — Michalak et al.
(2013) closed-form Shapley values of the one-hop,
shapley_k-neighbour and shapley_cutoff-hop
coverage games. Equal to exact Shapley values from full coalition
enumeration on random graphs of up to 8 nodes (with isolates, loops,
several components), directed extension included.centrality_access_information(),
centrality_hide_information() — Rosvall et al. (2005) /
Sneppen et al. (2005) search information, averaged from and to each
node; shortest-path DAG accumulation, no path enumeration. Equal to
explicit all-shortest-paths enumeration; reproduces the papers’ star and
complete-bipartite values. Disconnected graphs average over each node’s
reachable set.centrality_rumor() — Shah & Zaman (2011) rumor
centrality on each node’s BFS tree, log scale. exp() of it
equals brute-force spreading-order counts on trees; reproduces the
paper’s Fig. 5 (8, 12, 2, 3, 3).centrality_community_hub_bridge() — Ghalmane, El
Hassouni & Cherifi
(2019) raw hub-bridge score (needs membership;
cograph_bad_membership on bad input).centrality_entropy_variation(of = "degree" | "betweenness")
— Ai (2017) signed entropy drop on node deletion; degree variant in
closed form. Equal to the author’s own R code path to 1e-15 and to the
paper’s Table 2 quantiles on its 4234-node network.centrality_s_shell() — Liu, Tang, Do & Hui (2017)
strength-based shell index with asymmetric topological weights
(s_shell_a, default 0.5). The Zoo’s “s-shell index” is this
measure, not the Eidsaa-Almaas s-core. Shells verified against the
maximal-subgraph definition; a = 0 gives k-core dense
ranks.centrality_degree_discount(),
centrality_single_discount() — Chen, Wang & Yang (2009)
greedy seed-selection orders (discount_p), scored like
voterank (first selected = 1).centrality_ncvoterank() — Kumar & Panda (2020)
neighbourhood-coreness VoteRank (ncvote_theta). The
original article could not be obtained; the definition follows the Zoo
encyclopedia and three restatements, and the coreness normalisation (by
its maximum) is a documented choice. Its VoteRank limit reproduces
networkx.voterank.The Centrality Zoo lookup article and coverage document were regenerated: 82 Zoo measures are now available in cograph and nothing is “on the way”.
centrality()
tiers, and a catalogue of the measurestype = "all" no longer runs the measures whose
cost grows steeply with network size. Four are held back:
infection, two_way_rw,
node_contraction_improved and
entropy_variation_betweenness. On an 81-node graph
infection alone took 611 seconds while every other measure
together took about five, so a single type = "all" call
could take minutes by accident. type = "basic" (the
default) and type = "extended" are unchanged.include argument puts them back:
include = "costly" for all four, or name the ones you want.
Naming a measure in measures = always computes it whatever
its cost, so nothing became unreachable.list_centralities() (new export) is a
tidy table of every measure with the facts you need before reading a
column: orientation (which end of the scale marks a
prominent node), mode_aware, needs_membership,
uses_weights and costly. Twelve measures are
oriented so that a low value marks the more central node,
including eccentricity, constraint,
heatmap and the local-dimension family; sorting their
column the usual way puts the periphery on top.
list_centralities(orientation = "lower") lists them. The
measure lists now live in one place that both centrality()
and list_centralities() read, and a test asserts the two
agree.katz now warns with a
cograph_katz_diverged condition when
katz_alpha is too large for the graph. Katz converges only
for alpha < 1 / rho(A); above it the linear solve still
returns numbers, but they are not Katz scores and can be negative. The
default 0.1 is invalid on any graph whose spectral radius exceeds 10,
which includes many weighted networks. The check costs nothing on the
happy path and the warning names the valid bound.alpha and power now raise a classed
cograph_singular_system error instead of surfacing a bare
LU factorization message from igraph when I - alpha A is
singular.?centrality_dmnc gains a “Divergence from centiserve”
section. centiserve::dmnc() counts the largest component’s
edges in the wrong index space: its membership vector indexes the
neighbourhood subgraph but is used to subset the original graph. The two
disagree on 14 of the 34 karate nodes even at a matched epsilon, and
reproducing that indexing exactly reproduces centiserve’s output.
cograph counts the edges of the component it actually found. The
catalogue’s equivalence claim was corrected.Twenty more measures (R/kernels-batch9.R,
R/centrality-batch9.R), each researched from its source
paper by a dedicated agent and verified against published tables,
brute-force definitions and an independent Python reference (kept in
local_testing_and_equivalence/batch9/, not shipped).
membership):
centrality_community_based() (Zhao et al. 2015; reproduces
the paper’s Table 1 and Tulu et al.’s Table 1),
centrality_comm_centrality() (Gupta, Singh & Cherifi
2016; comm_r), centrality_community_mediator()
(Tulu, Hou & Younas 2018; base-2 entropy reproduces its Table
1).centrality_local_dimension_fixed()
(Silva & Costa 2013; ld_radius),
centrality_fuzzy_local_dimension() (Wen & Jiang 2019;
reproduces its kite Table 1 and karate top ten in order),
centrality_local_volume_dimension() (Li & Deng 2021;
definition from the authors’ later preprint, flagged).centrality_wvoterank() (Sun et
al. 2019; reproduces all sixty numbers of its Figure 1),
centrality_enrenew() (Guo et al. 2020; reproduces its
Figure 1; enrenew_depth),
centrality_voterank_plus() (Liu et al. 2021; matches the
authors’ code; voterank_lambda).centrality_node_contraction() and
centrality_node_contraction_improved() (Tan, Wu & Deng
2006; Wang et al. 2011; reproduce Table 1 and the path closed forms;
contraction_rho). The Zoo entry’s “removal” wording is
wrong; the sources contract.centrality_two_way_rw() (Curado et al. 2022; reproduces
the paper’s toy example including every fraction; O(n^4)).centrality_heatmap() (Duron 2020;
reproduces Table 1; lower = more central),
centrality_flow_coefficient() (Honey et al. 2007, BCT form;
equals one minus clustering on undirected graphs),
centrality_local_entropy() (Nie et al. 2016),
centrality_weighted_h_index() (Gao et al. 2019),
centrality_redundancy() (Burt 1992; Borgatti’s worked
example).centrality_weighted_kshell() (Garas, Schweitzer &
Havlin 2012; wks_alpha, wks_beta; Figure 1
example and Table 2 core size),
centrality_renewed_coreness() (Liu, Tang, Zhou & Do
2015; Figure 1 and all twelve percentages of its Table S1; the Zoo’s
transcription is off by one), centrality_geodesic_kpath()
(Borgatti & Everett 2006; paths counted with multiplicity;
centiserve::geokpath counts nodes instead).Not shipped, with reasons recorded in the coverage document: DegreePunishment, improved WVoteRank and local degree dimension (source articles unobtainable, definitions rest on the Zoo alone) and multi-local dimension (a rescaling of local dimension for every q outside (0, 1)).
New pkgdown article Centrality Zoo lookup answers
“is the Zoo measure I want in cograph?”: every Zoo measure listed once
under Available, Almost identical (tau >= 0.99, with the cograph
measure to use), Near-duplicate (0.90 <= tau < 0.99), On the way,
or Not available. docs/CENTRALITY-ZOO-COVERAGE.md records
the full intersection of the Zoo matrix with cograph: which Zoo measures
are rank-identical to an existing cograph measure (and therefore not
worth adding), which are near-duplicates, and the remaining ranked
candidates.
docs/CENTRALITY-CROSS-COVERAGE.md counted, in both
directions, what each R and Python centrality package reaches of the
Zoo. The five node measures that other packages offered and
centrality() did not are now implemented
(R/kernels-batch10.R, R/centrality-batch10.R),
each verified against the package whose gap it closes:
centrality_local_efficiency() — Latora & Marchiori
(2001). Global efficiency of the subgraph induced on a node’s
neighbours, the node removed. Matches
brainGraph::efficiency(type = "local") and the networkx
induced-subgraph form on 25 random graphs. Note that
igraph::local_efficiency() measures those distances
through the rest of the network and so reports larger values;
network_local_efficiency() keeps its igraph parity and its
help page now says so.centrality_s_core() — Eidsaa & Almaas (2013). The
weighted k-core: the largest strength threshold whose core still
contains the node. Matches igraph::coreness() on unweighted
graphs and a brute-force reading of the definition on weighted ones.
brainGraph::s_core() returns the peeling round instead,
which is documented as a divergence.centrality_fragmentation() — Borgatti (2006).
Distance-weighted fragmentation after deleting the node; matches
keyplayer::fragment(). This is the Zoo’s “Distance-weighted
fragmentation”, taking cograph’s Zoo coverage to 103 of 349.centrality_kpath() — Sade (1989). Simple paths of
length at most kpath_len that the node lies on; matches the
per-vertex counts of sna::kpath.census() for k = 2 and 3,
directed and undirected.centrality_epc() — Lin et al. (2008), the cytoHubba
edge percolated component. Recovers the exact bond-percolation mean on
small graphs and matches the normalisation of
centiserve::epc(). Monte Carlo: pass epc_seed
for a reproducible value; the caller’s random stream is restored.fragmentation and epc join the costly list,
so type = "all" holds them back
(include = "costly" or naming them restores them).
Three of the reported gaps turned out not to be gaps at all, and the
document now says so with the evidence:
centiserve::closeness.latora() is cograph’s
harmonic exactly, centiserve::communibet() is
communicability_betweenness exactly, and
brainGraph::efficiency(type = "nodal") is
harmonic over n - 1. Two remain unimplemented
and are listed with the reason: the link-community centrality of Kalinka
& Tomancak (its reference package linkcomm is archived,
so no equivalence check is possible) and keyplayer::kpset()
(a set search, not a node measure).
160 of the Zoo’s measures sit at a rank correlation of 0.90 or better
with something centrality() already computes
(docs/zoo/parameter_candidates.csv), which suggests many
are the same family at a different setting rather than different ideas.
The first five investigated bear that out, and four new measures plus
two arguments cover seven more Zoo labels (coverage 103 -> 110 of
349):
centrality_length_scaled_betweenness() — Borgatti &
Everett (2006), Brandes (2008) Algorithm 5. Betweenness with each
separated pair weighted by 1 / d(s,t).centrality_delta_betweenness() — Agneessens, Borgatti
& Everett (2017). Betweenness with the pair weight
(d - 1)^-delta (betweenness_delta, default 1);
delta = 0 is ordinary betweenness.centrality_ego_betweenness() — Everett & Borgatti
(2005). Betweenness inside the node’s own ego network. Close to
effective_size, and a test pins that it is not a function
of it.centrality_delta_closeness() — Agneessens et al. (2017)
eq. 2. sum_j d_ij^-delta / (n-1)
(closeness_delta, default 1). One exponent spans the
family: delta = 1 is harmonic over
n-1, delta = 2 is harary over
n-1, a large delta approaches degree,
delta = 0 counts the reachable set.centrality(x, measures = "betweenness", cutoff = k) already
computes it, verified against a brute-force reading of the definition on
directed and undirected graphs. It is now mapped as covered.All four are exact under a brute-force enumeration of weighted
geodesic pairs
(local_testing_and_equivalence/batch11/run_equivalence.R, 6
blocks, 6 PASS).
gravity computed a formula that appears in no
paper. It summed deg(j) * kshell(j) / d(i,j)^2
over every reachable j: the product of two masses on the
partner, none on the focal node, and no truncation. Its help page cited
Li et al. (2019), whose formula is k_i k_j / d^2. Dropping
the focal mass changes the ranking, not just the scale. The measure now
computes m_i m_j / d^exponent and gains two arguments:
gravity_mass ("kshell" default,
"degree", or "legacy") and
gravity_radius (a number, default 3, "auto"
for half the mean distance, or NULL). The default is now
Ma, Ma, Zhang & Wang (2016);
gravity_mass = "degree", gravity_radius = NULL is Li et
al.’s gravity model and gravity_radius = "auto" their local
gravity model, so one measure covers three Zoo labels.
gravity returns different values than in 2.4.7 and
earlier; gravity_mass = "legacy" with
gravity_radius = NULL reproduces the old numbers exactly,
and a test pins that.splot() on a netobject with
method = "entropy" (Nestimate’s
entropy_network()) now receives TNA styling — oval layout,
TNA palette, initial-probability donuts — instead of falling through to
psych styling, so the entropy re-weighting of a transition network
renders comparably with its source.
splot() now accepts label_abbrev,
matching mcml: use an integer for a fixed maximum label
length, "auto" for node-count-aware abbreviation, or
NULL to retain full labels.
Producer-supplied splot metadata
(x$meta$splot): packages that create cograph-plottable
objects can now attach a small rendering contract —
renderer (resolved through a cograph-maintained whitelist
of existing renderers; arbitrary function names are never evaluated),
weight (which stored edge quantity to render: an edge
column keeps the producer’s edge set, a matrix redefines the drawn
network from its nonzero cells, aligned by dimnames), and
defaults (renderer arguments). Precedence is always
user arguments > meta$splot$defaults > cograph defaults;
on the regular network path this includes deprecated argument aliases (a
user-supplied positive_color still beats a metadata
edge_positive_color default). See ?splot,
section “Producer-Supplied splot Metadata”.
Motif subsystem overhaul following an adversarial review (13 findings, each verified against igraph before fixing):
motif_census() mislabeled 13 of the 16 directed
triad classes: it attached MAN-order names to
igraph::motifs() output, which is in igraph’s
isomorphism-class order (a pure 021U triad was reported as
102). The directed 3-node census now uses
igraph::triad_census(), whose ordering is MAN
order. Counts were internally consistent — z-scores compared like with
like — but carried the wrong names. motifs(),
subgraphs() and triad_census() were never
affected.empty/edge/wedge/triangle),
and self-loops are stripped before counting (they are not part of any
3-node class)."configuration" null model now uses exact
degree-preserving edge rewiring. The old stub-matching +
simplify() silently changed degrees, and the undirected
"vl" sampler errored on graphs with isolates and restricted
the ensemble to connected graphs (two disconnected triangles got
sd = 0, z = 0, p = 1 for an observation the null could
never produce).z = NA when the observation differs from it — never a
silent z = 0. Zero-variance handling was previously
inconsistent across the three engines (forced 0 / sd := 1 / sd := 0.1).
n_random / n_perm below 2 is now an
error.motifs(pattern = "all") now actually includes the
003 class; a full census sums to choose(n, 3)
and matches igraph::triad_census() class by class.subgraphs()) now tests the
null probability that a triple instantiates the row’s own MAN
type; the old null counted “any of the six edges exists” (for
ten subjects each with a 3-cycle, expected was 8.33 instead of the
correct 3.33).params$significance = FALSE instead of
silently returning results without the promised
z/p columns.empty/wedge/triangle names never
matched a MAN row, yielding all-NA statistics).extract_motifs(level = "aggregate") now actually pools
the per-individual transition matrices (previously only metadata
changed), and min_transitions applies per-triad at
aggregate level as documented.motif_census(x, directed = ...) conflicting with an
igraph input’s own directedness is now an error instead of relabeling
without converting.edge_method = "percent" thresholds above 1 are
percentages and the comparison is >= as documented (the
old > total * 1.5 default could never classify
anything); fractional edge weights are rounded, not truncated, when
building permutation stubs.plot.cograph_motifs(type = "network") forwards
... to the per-motif igraph plots as documented, and
pattern-panel significance decoration is suppressed for legacy
per-triple results where a per-type lookup would be ambiguous.igraph::triad_census(), brute-force triple enumeration, and
per-actor reference censuses.extract_motifs() individual-level path now
retains one row per (node triple, MAN type) and tests that
exact type under the same weighted stub-matching null as
individual-level subgraphs(). It no longer collapses
mixed-type triples to a dominant label or counts any permuted type as a
match.sample(x) length-one
ambiguity. Unit eligibility is frozen from the original loopless
weighted activity, and every positive edge retains support during
integerization.sig, printing, and motif plot colors now consistently
follow the empirical permutation decision (p < .05)
instead of mixing it with |z| > 1.96 or
|z| > 2 cutoffs. Parallel edges are simplified before
motif_census() so observed and null graphs use the same
simple-graph projection.node1 /
node2 / node3 columns in addition to the
existing triad display label, so node names containing
" - " remain unambiguous in statistics and plots.M1–M218 naming sequence covering all igraph
isomorphism slots (199 are connected), and directed=
conflicts are rejected consistently for both igraph and cograph-network
inputs.z = NA with the smallest possible
empirical p, emitted when the observation lies outside a zero-variance
null) are now treated as the strongest findings everywhere: they rank
first in sorted results, survive top = n cuts instead of
being silently truncated, and the significance plots report them with a
message instead of silently dropping them (a z bar cannot be drawn for
them).plot(x, type = "triads") no longer errors on fractional
weighted counts (e.g. probability matrices at aggregate level); whole
numbers keep the plain n=3 caption.min_transitions no longer aborts the whole significance run
(malformed cells in eligible units still error loudly).plot_transitions() with a multi-column data frame
(the consecutive multi-step branch) no longer silently drops styling
arguments: value_min, label_color,
label_fontface, label_nudge,
title_color, title_fontface,
value_halo, value_fontface,
value_nudge, and total_fontface are now
forwarded, so both multi-step input forms (list of matrices, data frame)
respond to the same arguments identically.
Motif pattern plots
(plot(motifs(x), type = "network"), triad glyph panels)
drew the wrong structure for five of the sixteen MAN triad classes: the
021D and 021U glyphs were swapped (transposed
matrices), the 120D and 120U glyphs both drew
a 120C-isomorphic triad, and the 210 glyph
drew a 120-class triad — so the 120U and
210 structures were never drawn at all. Only the drawn
glyphs were wrong: motif counts, significance tests, and
triad_census() were always computed from a separate,
correct canonical pattern set (verified against
igraph::triad_census()). All sixteen visual patterns are
now verified against igraph by a regression test that also pins the
visual and canonical sets to each other. Thanks to Mengli Zhang for
reporting (reconstructing the structures from the package source and
spotting that three “distinct” glyphs were isomorphic).
plot_bootstrap_forest(),
extract_motifs(), motif_census(),
triad_census() and extract_triads() are now
listed in the package index and the reference site. All five are
exported and user-facing, but carried @keywords internal,
which hid them from help(package = "cograph") — you could
only find them if you already knew the name. mcml() remains
hidden; it is a deprecated alias of csum(). The
n and ... arguments of
print.cograph_motif_analysis() and
print.cograph_motifs() are now documented (previously
exempt from checking by the internal keyword).
plot_difference()’s new difference
argument moved to the end of the signature, after combined.
It had been inserted before combined, which
shifted the positional argument order relative to the released 2.3.6
signature. Because plot_compare() is
function(x, ...) and forwards to
plot_difference(), a caller passing 14 positional arguments
had their 14th silently rebound from combined to
difference — making the function treat x as an
already-subtracted matrix, discard y, and draw the wrong
network with no error. difference was introduced after the
last CRAN release, so no released behaviour changes. Named calls were
never affected.
plot_bootstrap_forest() and
plot_edge_diff_forest() no longer emit a
geom_errorbarh() deprecation warning under ggplot2 4.0.0.
The four horizontal error-bar layers now use
geom_errorbar(orientation = "y"); the rendered output is
unchanged. DESCRIPTION now declares the
ggplot2 (>= 3.4.0) requirement the package already had
(it uses the linewidth aesthetic throughout).
plot_edge_diff_forest(layout = "chord") no longer
emits a spurious “row names were found from a short variable and have
been discarded” warning for every node arc it draws.
aggregate_layers(), supra_adjacency(),
layer_similarity_matrix() and plot_motifs()
now ship runnable examples. Their \examples sections were
previously commented out (or entirely \dontrun), so they
demonstrated nothing and were never checked. The remaining
\dontrun blocks in motifs() and
extract_motifs() are now \donttest, so they
are executed under R CMD check --run-donttest.
detect_communities() with the "louvain"
(the default) or "leiden" method no longer errors on a
directed graph. These igraph algorithms are
undirected-only, so detect_communities(tna_object) — a tna
model is always directed — aborted with “Multi-level community detection
works for undirected graphs only”. It now collapses the directed edges
to undirected (mean, as the "fast_greedy" method already
did) with a message, so the package’s primary object type works with the
default algorithm. This also fixes
plot_htna(x, community = "louvain") and other internal
callers that ran community detection on a directed model.
splot() on a Nestimate netdifference
(from subtract_networks() /
as_netdifference()) now routes to
plot_difference(). Previously it fell through to the
netobject path, which styles by $method —
“difference” is not a TNA-family method, so the asymmetric difference
matrix was drawn with undirected psych styling: no arrowheads and one
triangle of each asymmetric edge pair silently dropped.
splot(d, minimum = 3) is now the straightforward call for a
signed difference network.
The netdifference routing excludes
net_permutation-family objects: net_bayes
carries both classes and must keep reaching
splot.net_permutation, whose per-edge CI/star arrays are
aligned by Nestimate::plot.net_bayes to that renderer’s
edge ordering.
plot_difference() on a netdifference
now draws the display matrix ($weights — e.g. only the
credible differences when coerced with
as_netdifference(b, significant_only = TRUE)), falling back
to $difference_matrix. For subtract_networks()
results the two are identical, so nothing changes there.
plot_permutation() /
splot.net_permutation(): the title and
layout defaults now use exact [[ indexing.
args$title on a dots-list holding title_size
(but no title) partially matched title_size,
so the default title was silently skipped and no title was drawn — this
is why Nestimate::plot.net_bayes() output had no title.
Same latent hazard fixed for layout /
layout_scale.
Edge label templates gain a {p_diff} placeholder
(probability of the difference, for Bayesian comparisons), fed by the
new edge_label_p_diff argument — a per-edge vector or a
full node-by-node matrix (the matrix is indexed at each drawn edge, so
it survives minimum/threshold filtering, and
is aligned by dimnames so it may be supplied in any node order). Filled
automatically from $p_difference by
splot.net_permutation and by plot_difference()
on Bayesian netdifference coercions. Template example:
edge_label_template = "{est} (P={p_diff})".
splot.netobject() styling classifier:
"edge_betweenness" networks are now styled by their
directedness. A directed edge-betweenness network previously fell into
psych styling — drawn undirected, silently losing one direction of each
asymmetric pair; it now gets the TNA presets with arrows. An undirected
one (from a correlation-family source — Nestimate preserves the source’s
directedness) keeps the psych look.
Nestimate producers now use the meta$splot contract:
netdifference objects carry
renderer = "difference" and net_bayes carries
renderer = "permutation", so metadata routing (which runs
before class dispatch) selects the renderer; the
netdifference class branch remains as a fallback for
objects built without metadata.
plot_difference() no longer hides small difference
edges: it defaults minimum = 0 (the style presets otherwise
injected minimum = 0.01, silently dropping edges with
|x - y| < 0.01). An explicit minimum still
wins.
plot_difference(x, y, difference = TRUE) now warns
that y is ignored and uses x as the difference
network, instead of silently computing x - y.
plot_compare() is no longer
deprecated — it is a plain alias of
plot_difference(). tna::plot_compare()
delegates to it by name (cograph::plot_compare(x, y, ...)),
so deprecating it wrongly made every tna::plot_compare()
call emit a warning; the warning is removed. Both names call the same
implementation; plot_difference() is the preferred spelling
for new cograph code.
plot_difference() also auto-detects a Nestimate
netdifference object (or any object exposing
$difference_matrix), alongside
tna_comparison.
plot_difference() can now consume a
pre-computed difference network: a
tna_comparison object (from tna::compare()) is
detected automatically and its $difference_matrix is
plotted, and difference = TRUE treats x as an
already-subtracted matrix/network (no y needed). The
two-network plot_difference(x, y) path is unchanged.splot(x, layout = "target") /
layout = "saqr"):
layout_target() ports qgraph’s flow() —
places one node of interest (target =) on the left and
every other node in columns by unweighted BFS distance (hops). Unlike
qgraph it handles disconnected graphs (isolated nodes go to a trailing
column) instead of erroring.layout_saqr() ports the Dynalytics Desktop “saqr”
transition layout (Saqr et al., LAK25): Start on top, End on bottom,
middle nodes ranked by outgoing weight from Start and split into 2–3
sine-enveloped rows with a zig-zag first row (start =,
end =, jitter =).plot_difference() now styles the difference
network automatically instead of drawing bare default-blue
nodes: an undirected difference gets the psychometric look (Okabe-Ito
node palette, no arrows, thin edges), a directed difference gets the TNA
look (TNA palette, arrows). Node size uses the calibrated preset
(previously nodes could render near-invisible), and edges stay coloured
by the sign of the difference. Explicit node_* arguments
still override the preset.
plot_difference() is added as the preferred name for
the difference-network plotter. plot_compare() remains a
first-class alias of it (tna::plot_compare() delegates to
cograph::plot_compare() by name, so the name must keep
working).
plot_difference() (the renamed difference-network
plotter) now treats an S3 cograph_network (which is itself
a list — e.g. a psychnet fit, a Nestimate
netobject, or any as_cograph() result) as a
single network. Previously such an object fell into the “plain list of
networks” branch and was misread as a list of sub-networks, failing with
“x must be a matrix, cograph_network, tna, or igraph object”. Comparing
two psychnet/netobject networks with
plot_difference(net1, net2) now works.
dyad_census() classifies every dyad of a directed
network into mutual (M), asymmetric (A), or null (N), returning a tidy
one-row-per-type data.frame with counts and proportions and a dyad-based
reciprocity (2M / (2M + A)) attribute. It is the dyad-level
companion to triad_census(). Undirected input counts every
edge as a mutual dyad.
ego_networks() reports tidy per-ego personal-network
metrics — size, ego/alter tie counts and densities, and Burt’s
structural-hole measures (effective_size,
constraint, order = 1 only) — with one row per
ego. The structural-hole columns reuse the same implementations as
centrality(), so they match
centrality(x, measures = c("effective_size", "constraint"))
exactly.
Bootstrap plots of undirected co-occurrence networks
(splot.net_bootstrap) now default to the
"oval" layout instead of the force-directed
"spring" layout, matching splot.tna_bootstrap.
Pass layout = "spring" to restore the previous
behavior.
Bootstrap plots now auto-suppress the ".00" decimal
tail on integer-valued weight matrices (co-occurrence counts, raw
frequencies): 266.00** renders as 266**.
Detection mirrors splot.netobject — when every nonzero
weight is a whole number and the user has not set
weight_digits, both weight_digits and
edge_label_digits default to 0. Applies to
both splot.net_bootstrap and
splot.tna_bootstrap. Non-integer (correlation/GLASSO)
networks are unaffected, and an explicit weight_digits
always wins.
plot_mcml() gains a theme argument:
"classic" (default — the established pie-node /
straight-edge look, now with thinner node and shell borders and slightly
larger detail nodes), "rich" (donut nodes on both layers
plus curved summary edges and splot self-loops), and
"light" ("rich" with no shell outline and a
softer fill). Granular overrides node_donut,
node_donut_inner_ratio,
summary_donut_inner_ratio,
summary_donut_show_value, curved_edges, and
summary_curve win over the preset.
plot_mcml() now colors edges by weight sign on every
layer (within-cluster, between-cluster, summary, and self-loops) via
edge_color_by: "auto" (default) keeps cluster
coloring for non-negative transition networks but switches to sign
coloring when any negative weight is present (correlation / association
networks), "cluster" and "sign" force either
mode. Positive edges use edge_positive_color
("#2E7D32", green) and negative edges
edge_negative_color ("#C62828", red), matching
splot(). Edge visibility thresholding and width scaling now
use the absolute weight, so negative edges are drawn rather than
silently dropped, and a positive/negative key is added to the legend
when sign coloring is active.
plot_mcml() summary-node labels are now placed “on
the clock”: each label sits just outside its node in the cardinal
direction the node points from the arrangement center (top at 12, bottom
at 6, left at 9, right at 3), anchored at the node boundary so it always
clears the node regardless of summary_size. An explicit
summary_label_position still overrides this.
plot_mcml() and splot() accept
mcml_pc objects (Nestimate::build_mcml_pc(),
experimental psychometric MCML) and render them undirected via their
meta$directed flag.cluster_summary() and
build_mcml() are removed to end, permanently, the collision
with Nestimate::cluster_summary() and
Nestimate::build_mcml() — different functions that silently
masked each other depending on package attach order (the same disease as
the cluster_network() alias removed in 2.3.6, where load
order silently flipped results between raw counts and row-normalized
probabilities). Migration is name-for-name with identical behavior:
cluster_summary(...) → csum(...) (the
existing short alias is now the canonical exported name; same arguments,
same cluster_summary return object).build_mcml(...) → summarize_clusters(...)
(same arguments, same mcml return object). In sessions
where both packages are attached, the bare names
cluster_summary() / build_mcml() now always
refer to Nestimate’s data-layer verbs, regardless of attach order. The
as_tna() generic is intentionally exported by both
packages: the definitions are identical
(function(x) UseMethod("as_tna")), so masking is harmless
and S3 methods from both packages dispatch correctly.plot_mcml() gains a directed argument
(default NULL = auto-detect). Undirected rendering
suppresses arrowheads on all three edge layers (within-cluster,
between-cluster, summary), draws each symmetric edge pair once instead
of twice (previously a symmetric matrix produced overplotted reciprocal
arrows), and moves edge labels to the edge midpoint. Auto-detection
reads $meta$directed from
cluster_summary/mcml input (e.g.,
co-occurrence aggregations such as
Nestimate::build_mcml(type = "cooccurrence") now render
undirected with no extra flag), the $directed field of
network objects, or matrix symmetry for plain matrices — the same
contract as splot(), which forwards directed
when dispatching mcml/cluster_summary
objects.plot_mcml() undirected matrix input is aggregated with
cluster_summary(type = "cooccurrence") (symmetrized counts)
instead of the row-normalized type = "tna", whose output is
asymmetric even for symmetric input and cannot be represented by
undirected drawing. When directed = FALSE is forced on
weights that are not symmetric, plot_mcml() now warns that
only the upper triangle is drawn.cluster_summary() and the sequence path of
build_mcml() now record the effective directedness
in $meta$directed: FALSE when
type = "cooccurrence" (which symmetrizes the weights),
instead of echoing the directed argument unchanged.cluster_network() alias for
summarize_network(). It collided with
Nestimate::cluster_network() — a completely different
function (PAM clustering on sequence data, one network per cluster) —
and the two silently masked each other depending on package attach
order, producing confusing
unused arguments (k = ..., cluster_by = ...) errors. Use
summarize_network() (or its remaining short form
cnet()) for matrix-to-cluster aggregation in cograph.LICENSE..smooth_blob() (used by plot_simplicial()
and overlay_communities()) now guards
grDevices::chull() against non-finite anchor coordinates.
Previously a node lacking layout coordinates (NA/Inf) aborted the blob
with “finite coordinates are needed”; such anchors are now dropped
before the convex-hull step.motifs() / subgraphs() roxygen
documentation with the post-audit behavior shipped in 2.3.2 (census
type_summary counts, min_count handling, and
corrected plot legend descriptions).type_summary
now holds real MAN-type counts in census mode, min_count is
honored in census mode, and the swapped source/target color description
in plot.cograph_motif_result() is corrected.motifs() and plot_simplicial() on
Nestimate-backed workflows (HON / HYPA sequence inputs).panel_layout(): tightened dimension validation and made
the restoration claim honest — it now restores only the
par() settings it actually changed.combined argument (default TRUE) on
every multi-panel plot function: splot() group-cascade,
plot_netobject_group(), plot_netobject_ml(),
plot_net_bootstrap_group(),
plot_group_permutation(), plot_compare(),
splot.net_mlvar(), plot_network_evolution(),
plot.cograph_motifs(),
plot.cograph_motif_result(),
plot.cograph_motif_analysis(), and
plot.tna_disparity(). With combined = FALSE
these functions draw panels into the active device without calling
graphics::par(mfrow=...), so callers can drive their own
layout (e.g. graphics::layout() or the new
panel_layout() helper). Default TRUE preserves
prior behavior — every existing call site renders identically.panel_layout() helper sets up a custom multi-panel
device layout for use with combined = FALSE. Accepts either
a uniform-grid c(nrow, ncol) or a
graphics::layout() matrix for non-uniform layouts (e.g. one
wide panel + two narrow ones). Returns a par() snapshot for
restoration via on.exit().test-coverage-splot-{41,42}.R: bumped
n_nodes from 4 to 10 in seven per-edge attribute tests so
the seed=42 sampler does not produce duplicate (1,2) pairs that trip
cograph’s undirected-duplicate-edge detector.test-coverage-class-network-41.R: aligned the
set_layout_coords() mismatched-row-count test with the
strict input validation already enforced by
R/class-network.R.test-overlay-communities.R: prefixed two
communities() calls with cograph:: to avoid
tna masking when both packages are loaded in the suite (per
CLAUDE.md “namespace masking” gotcha).cr_color #D4820A ->
#D4829A in plot-forest.R;
show_value default FALSE ->
TRUE in splot-nodes.R), corrected dataset
dimensions in data-hai.R (302 ->
429 x 287), corrected a reference to the nonexistent
igraph::is_bipartite() (now
bipartite_mapping()), expanded centrality()
@param measure lists for mode,
cutoff, invert_weights, and
membership to match the implementation, dropped baked-in
measure counts that rot on each addition, and removed nonexistent themes
from sn_theme documentation. No runtime behavior changes
from the documentation pass itself.plot_simplicial() now warns when anomaly
is set on an input that has no anomaly concept (HON, association rules,
link prediction, character pathways, method = "hon" /
"rules"). Previously the argument was silently dropped, so
calls like plot_simplicial(hon, anomaly = "over") and
plot_simplicial(hon, anomaly = "under") produced
byte-identical plots. anomaly is honored only for
net_hypa inputs and method = "hypa"
auto-builds.centrality() gains an umbrella argument
tna_network (logical or NULL). When TRUE (or
auto-detected from a
tna/group_tna/ctna/
ftna/atna input), all measures shared with
tna::centralities() match byte-for-byte:
loops = FALSE, invert_weights = TRUE,
diffusion_method = "power_series",
transitivity_type = "onnela". Side-by-side audit confirms
zero divergence on OutStrength, InStrength,
ClosenessIn/Out/All, Betweenness,
Diffusion, Clustering
(max|diff| = 0). Any per-argument override the user passes
explicitly always wins over the umbrella.centrality() (and centrality_diffusion())
gain a
diffusion_method = c("kandhway_kuri", "power_series")
argument. The default NULL auto-detects:
"power_series" for tna inputs (matches
tna::centralities(., measures = "Diffusion") byte-for-byte
when loops = FALSE), "kandhway_kuri" (the
existing 1-hop binary-degree formula, Kandhway & Kuri 2014) for
everything else. Previously cograph’s diffusion silently disagreed with
tna’s because cograph used an unweighted neighborhood-degree sum while
tna uses rowSums(P + P^2 + ... + P^n) on the
diagonal-zeroed weighted matrix — the same name covered two different
statistics. Set explicitly to override the auto-detect.tests/testthat/test-validate-nestimate-bootstrap-permutation.R
asserting that centrality() on a Nestimate
netobject agrees with centrality() on its
$weights matrix when the diagonal is non-zero. Locks in the
upstream Nestimate fix to .extract_edges_from_matrix()
(Nestimate >= 2026-05-02) which now preserves self-loops in
$edges. Without that fix, loop-bearing netobjects
(e.g. Nestimate::build_mcml() |> Nestimate::as_tna())
silently under-counted node degree by 2.edge_label_size is now coupled to the node
label cex at a fixed 0.55 fraction
(edge_cex = 0.55 * mean(node_label_cex)) so the
node-to-edge-label ratio stays a stable ~1.82x across canvases. This
replaces the previous EDGE_LABEL_SCALE_CAP-based
compensation, which let the ratio drift from 2.5x at reference to 3.6x
at poster canvases because edge labels were clamped to a tighter 1.6
ceiling while node labels scaled freely to 2.3. The visible effect: edge
weight annotations are now readable at poster sizes instead of shrinking
relative to node labels. User-explicit edge_label_size
still wins and receives the same (capped) visual-scale compensation as
before; only the default path changed.render_edges_splot() into splot.R so the final
cex is produced in a single place.splot() now applies device-dependent compensation to
text, line, and point sizes so visual ratios (label-to-node,
legend-to-plot, edge thickness) stay consistent when the output device
changes. This fixes the long-standing “labels too big at high DPI” and
“legend desynchronised from the plot” issues when saving PNGs at
res = 300 or res = 600 with pixel-default
width/height, and when resizing the RStudio
plot pane. Implementation: a single compute_visual_scale()
reads the active device’s canvas size (dev.size("in")) and
returns multipliers keyed off a 5.9-inch reference (matching the default
RStudio 7×5” pane so backward-compatible behaviour at the default canvas
is preserved). Multipliers are clamped to [0.55, 1.9] to
keep thumbnails and posters legible. See the new
R/visual-scale.R.scaling = "fixed" mode on splot() —
and corresponding global option
options(cograph.visual_scaling = FALSE) — disables device
compensation for reproducibility-sensitive workflows that calibrated
against the previous behaviour.splot() return value now carries two attributes for
downstream tooling: cograph.visual_scale (the multiplier
list) and cograph.node_diam_in (the representative node
diameter in inches at the rendered device).render_legend_splot() plus the new
shared .render_legend_base()
(R/render-legend-shared.R) replace the ad-hoc legend
cex/pt.cex handling with a single compensated path.
plot_htna, plot_mtna, plot_mlna,
plot_mcml still use their historical scale multiplier
arguments; Phase 2 will migrate them to the shared helper.splot.netobject now routes on the Nestimate
$method slot rather than just direction. Undirected
sequence-based networks from build_cna() and
wtna(method = "cooccurrence") get oval TNA-family styling
(layout, palette, donuts) with arrows and dotted edge starts
automatically dropped because the matrix is symmetric. Glasso / cor /
pcor / ising networks still get psych_styling = TRUE
(spring layout, Okabe-Ito palette).from_tna() auto-detects integer-valued weight matrices
(ftna, ctna, raw counts) and sets
weight_digits = edge_label_digits = 0 so edge labels render
as 2304 rather than 2304.00. Fractional
weights still format to two decimals. Explicit user-supplied
weight_digits still wins.psych_styling = TRUE is now exported as a first-class
styling preset (undirected counterpart of tna_styling) —
Okabe-Ito palette, spring layout, no arrows — applied by default to
splot.netobject on correlation-family input and to the
$contemporaneous / $between constituents of
net_mlvar.splot() dispatch coverage across the tna and
Nestimate class hierarchies, ensuring tna,
ftna, ctna, group_tna,
tna_bootstrap, group_tna_bootstrap,
tna_permutation, group_tna_permutation,
netobject, netobject_group,
netobject_ml, net_mlvar,
wtna_mixed, net_bootstrap,
net_permutation, boot_glasso,
mcml, net_hon, net_hypa, and
simplicial_complex all reach the correct renderer.detect_duplicate_edges(),
aggregate_duplicate_edges(),
simplify.cograph_network(), and the internal
check_duplicate_edges() helper now respect directed vs
undirected semantics. Previously the canonical (min/max) endpoint key
collapsed A -> B and B -> A into one
edge even on directed graphs, matching igraph::simplify()
ground truth..compute_modularity() replaces a nested for loop with
cluster-wise vectorization
(sum(A[idx, idx]) - sum(k_out[idx]) * sum(k_in[idx]) / m),
per the project “no for loops” rule. Results verified bit-exact against
igraph::modularity().is_directed() now recognises
CographNetwork R6 objects — previously only the
cograph_network list format dispatched correctly.compute_layout_for_cograph() uses
layout$get_type() instead of the removed $name
field on CographLayout.network_small_world() returns 0 (valid: no
triangles means definitively not small-world) instead of
NA_real_ when the observed clustering coefficient is zero
but path length is finite.simplify.cograph_network() threads the directed flag
through to edge aggregation so directed multigraphs collapse
correctly.simplify() performance refactor for large networks plus
a cleaner title-composition path.motifs(), extract_motifs(), and
plot.cograph_motif_analysis examples reworked to use
n_perm = 10L (or significance = FALSE) and
promoted from \dontrun to CRAN-runnable (optional tna
branches stay in \donttest). Retires 320 seconds of latent
CRAN timing risk — every example now runs in under 4 seconds.test-audit-fixes.R — ground-truth regressions for the
directed edge semantics, modularity vectorization, and small-world
behaviour changes.test-integer-weight-labels.R — locks
from_tna() integer-weight auto-detect behaviour and
precedence of explicit weight_digits.test-equiv-{assortativity, cluster-quality, communities, disparity, edge-centrality, network-summary, robustness, standalone-measures}.R
— numerical equivalence against igraph, sna, centiserve, brainGraph,
influenceR, tidygraph, and NetworkX. Gated by
skip_coverage_tests() + skip_on_cran(), so they do not run
on the CRAN pipeline.These measures don’t fit the per-node centrality() data
frame, so they live as standalone functions:
estrada_index() — graph-level spectral invariant: ,
equal to the trace of the matrix exponential of the adjacency.
Equivalently, the sum of subgraph_centrality() across all
nodes. Matches networkx.estrada_index at machine epsilon
(max relative diff ~5e-15 across random test graphs).trophic_incoherence() — graph-level food-web stability
measure (Johnson et al. 2014). Defined as the population standard
deviation of per-edge trophic differences where is the trophic level of
node . Zero for perfectly coherent DAGs (e.g., a pure chain). Matches
networkx.trophic_incoherence_parameter at machine epsilon.
Directed-only; reuses the existing trophic_level
calculator.group_centrality(x, nodes, measure = c("betweenness", "closeness", "degree"))
— Everett-Borgatti (1999) group centrality for a set of nodes.
Returns a scalar. Supports mode = "in"/"out" for
directed-degree variants. Group closeness and group
degree match networkx.group_*_centrality
bit-exact. Group betweenness implements the textbook
Everett-Borgatti / Puzis 2008 definition (fraction of shortest paths
passing through at least one node in the group), which diverges from
networkx.group_betweenness_centrality on some graphs due to
a known quirk in NetworkX’s Puzis-Yahalom-Elovici iterative algorithm.
Verified via an independent Python brute-force: cograph matches the
textbook definition; NX produces larger values on graphs with many
overlapping shortest paths. Documented in the roxygen “Divergence from
NetworkX” section.dispersion(x, u = NULL, v = NULL, normalized = TRUE, alpha = 1, b = 0, c = 0)
— Backstrom-Kleinberg (2014 Facebook) pair-level measure of tie
strength. Counts the number of “well-dispersed” mutual friends of
u and v (pairs of common neighbors that are
not directly connected and share no common neighbor inside
u’s ego network other than u and
v). Matches networkx.dispersion bit-exact
across all 156 edges on the karate club graph. Returns a scalar, named
vector, or data frame depending on which of u,
v are specified.Added the five Gould-Fernandez (1989) brokerage role counts, a
foundational measure in social network analysis (~1500 citations). Each
role is a separate per-node measure requiring a membership
argument (following the same pattern as participation,
within_module_z, gateway), and counts open
directed 2-paths a -> v -> c through broker
v:
centrality_brokerage_coordinator() — all three in
broker’s group (w_I)centrality_brokerage_itinerant() — endpoints same
group, broker different (w_O, “consultant”)centrality_brokerage_representative() — broker + source
same, target different (b_IO)centrality_brokerage_gatekeeper() — broker + target
same, source different (b_OI)centrality_brokerage_liaison() — all three in different
groups (b_O)Bit-exact match against sna::brokerage$raw.nli for all
five roles across 20 random directed graphs. Implemented natively (no
runtime dependency on sna). Key implementation detail: the
Gould-Fernandez counting rule requires open 2-paths
only — triads where a direct edge a -> c
already exists are excluded. This matches sna’s C implementation exactly
and was derived empirically (sna’s .C("brokerage_R", ...)
has no R-level source).
Directed-only; warns and returns NA on undirected
input.
centrality_prestige_domain() — directed-graph prestige
measure: for each node , the number of other nodes that can reach via a
directed path. Classical Wasserman-Faust (1994) measure from
sna::prestige(cmode = "domain"). Bit-exact match against
sna, implemented natively via
igraph::distances(mode = "out") +
colSums(is.finite(D)) - 1 (no runtime dependency on sna).
Directed-only; returns NA with a warning on undirected input.centrality_prestige_domain_proximity() —
distance-weighted variant: R_v^2 / (D_v * (n - 1)) where
R_v is the number of reachers and D_v is the
sum of their geodesic distances to v. Bit-exact match
against sna::prestige(cmode = "domain.proximity") on
strongly connected directed graphs. On graphs with any unreachable pair,
sna has a known bug (FALSE * Inf = NaN collapses the
denominator, producing all-zero output); cograph’s
is.finite()-masked formula produces mathematically correct
values on any directed graph. Directed-only.centrality_katz() — Katz (1953) status index. Bit-exact
match against centiserve::katzcent (cograph mirrors
centiserve’s exact LAPACK call sequence). Also matches
igraph::alpha_centrality(exo = 1) and
networkx.katz_centrality_numpy at machine epsilon. New
katz_alpha parameter (default 0.1).centrality_hubbell() — Hubbell (1965) input-output
centrality. Bit-exact match against centiserve::hubbell
(cograph mirrors centiserve’s full-inverse LAPACK call path). Note:
centiserve’s default (weights = NULL) silently ignores
E(g)$weight; to reproduce cograph’s behavior with
centiserve on weighted graphs, pass
weights = igraph::E(g)$weight explicitly. New
hubbell_weight parameter (default 0.5).centrality_information() — Stephenson-Zelen (1989)
information centrality. Bit-exact match against
sna::infocent on connected undirected graphs (cograph
mirrors sna’s exact construction and solve() call
sequence).centrality_pairwisedis() — Pairwise disconnectivity
(Potapov et al. 2008). Directed-only; fraction of reachable ordered
pairs that become unreachable when each node is removed. Bit-exact match
against centiserve::pairwisedis. Warns and returns
NA on undirected input, matching the convention used by
salsa, leaderrank, and
trophic_level.centrality_reaching_local() /
reaching_global() — Local and global reaching centrality
(Mones, Vicsek & Vicsek 2012). Bit-exact match against
networkx.local_reaching_centrality across the directed
unweighted, undirected unweighted, and weighted branches. Undirected
unweighted LRC coincides with
igraph::harmonic_centrality(normalized = TRUE)
(documented). reaching_global() is a graph-level hierarchy
statistic in [0, 1].plot_simplicial() now accepts tna,
netobject, net_hon, and net_hypa
objects directly — higher-order pathways are auto-built and visualized
with proper state labels, no manual extraction needed. New parameters:
method ("hon" / "hypa"),
max_pathways, ncol. Dismantled mode uses
gridExtra grid layout with scaled nodesprint.cograph_network() now shows a structured summary:
node/edge counts, density, reciprocity, weight range, and top-degree
nodes — replacing the minimal R6 default outputmcml S3 class with as_mcml() generic
for type-safe handling of Markov Chain Multi-Level models — enables
print(), plot(), and method dispatch on MCML
objects%||% operator for R 4.1 compatibility (no
longer requires R 4.4+)$between →
$macro, $within → $clustersas_tna() on MCML objects now returns a flat
group_tna list instead of a nested structureplot_mcml() now suppresses zero-weight edges instead of
drawing invisible lines, and strips leading zeros from edge labels
(.32 instead of 0.32)cluster_summary() are now preserved in
the macro diagonal, reflecting intra-cluster retention ratesoverlay_communities() for drawing community blob
overlays on any network plot — accepts method names, membership vectors,
or pre-computed community objectsplot_simplicial() for higher-order pathway
visualization, rendering simplicial complexes as smooth blobs with
flexible separators and a dismantled view optionvalue_nudge parameter to
plot_transitions() for controlling the distance between
flow labels and nodesbundle_legend_size,
bundle_legend_color, bundle_legend_fontface,
bundle_legend_positionlabel_size,
label_color, label_fontface,
label_hjust) to plot_transitions(),
plot_trajectories(), and plot_alluvial()cluster_summary() for aggregating network weights
at the cluster level, producing between-cluster and within-cluster
matrices from raw transition databuild_mcml() for constructing Markov Chain
Multi-Level models from edge lists or sequence data with automatic
cluster detectioncluster_quality() for modularity-based cluster
quality metrics and cluster_significance() for
permutation-based significance testingas_tna() to convert cluster summaries to TNA
objects for bootstrapping, permutation testing, and plotting with
splot()simplify() for pruning weak edges from networks,
with configurable weight threshold and aggregation methoddisparity_filter() for backbone extraction
(Serrano et al. 2009), with methods for matrices, tna, igraph, and
cograph_network objectsrobustness() for network robustness analysis with
targeted (betweenness, degree) and random attack strategies, plus
ggplot_robustness() for faceted ggplot2 outputtemporal_edge_list() for converting sequence data
to timestamped edge listssupra_adjacency(), supra_layer(),
supra_interlayer() for multilayer supra-adjacency matrix
constructionlayer_similarity(),
layer_similarity_matrix(), and
layer_degree_correlation() for comparing layers in
multilayer networksaggregate_weights() and
aggregate_layers() for weight aggregation across
layersverify_with_igraph() for cross-validating cograph
centrality and network metrics against igraphmotifs() / subgraphs() as a unified
API for triad census (node-exchangeable counts) and instance extraction
(named node triples), with auto-detection of actor/session columns,
rolling/tumbling window support, and exact configuration model
significance testingplot_mcml() for Markov Chain Multi-Level
visualization showing between-cluster summary edges alongside
within-cluster detail, with pie charts, self-loops, and 22 customization
parametersplot_chord() for native chord diagrams with
automatic weight-based arc sizingplot_time_line() for cluster membership timeline
visualizationplot_htna() orientations: "facing"
(tip-to-tip columns) and "circular" (two semicircles), plus
intra_curvature for drawing intra-group edges as dotted
bezier arcsthreshold parameter to all plot functions for
filtering edges/cells below a minimum absolute weightvalue_fontface, value_fontfamily,
and value_halo parameters to plot_heatmap()
for text styling controlscale_nodes_by:
indegree, outdegree, instrength,
outstrength, incloseness,
outcloseness, inharmonic,
outharmonic, ineccentricity,
outeccentricityscale_nodes_scale parameter to
splot() for dampening (< 1) or exaggerating (> 1)
centrality-based node sizing differencessplot(): when
plotting tna objects, qgraph-style parameters (vsize,
asize, edge.color, lty,
shape) are automatically mapped to cograph equivalentsnode_label_format (e.g.,
"{state} (n={count})") for showing counts on transition
plot nodesbundle_size for aggregating
individual trajectories into weighted summary lines in large
datasetsshow_values /
value_position for displaying transition counts on flow
lineslabel_position consistency across ALL columns
(first, middle, last) in trajectory plotsgamer_data,
group_engagement, srl_dataset_node_groups() /
get_node_groups() for managing cluster assignments on
cograph_network objects$meta with
getter/setter functionsgroup_tna support to splot() for
direct plotting of grouped TNA modelscentrality_* wrapper its own focused help
pagesplot() viewport
calculationsplot()’s signature and
silently dropped when dispatchingplot_heatmap() so
high values get dark colorsbuild_mcml() density method crash when weight
vector had no names.collect_dispatch_args() helper to replace 6 copy-paste
dispatch blocks, using match.call() + mget()
for reliable argument capturecentrality() with 23 measures and individual
wrappers: degree, strength, betweenness, closeness, eigenvector,
pagerank, harmonic, authority, hub, alpha, power, kreach, diffusion,
percolation, eccentricity, transitivity, constraint, coreness, load,
subgraph, leverage, laplacian, current-flow betweenness, current-flow
closeness, voterankedge_betweenness() for edge-level centralitydetect_communities() with 11 algorithms: louvain,
walktrap, fast_greedy, label_propagation, leading_eigenvector, infomap,
spinglass, leiden, optimal, edge_betweenness, multilevel — plus
com_* shorthand aliasescluster_significance()
for permutation-based validationnetwork_summary() and
summarize_network() for computing comprehensive
network-level statistics (density, reciprocity, transitivity, diameter,
components, degree distribution)plot_transitions() for alluvial/Sankey flow
diagrams, with plot_alluvial() and
plot_trajectories() wrappersplot_bootstrap() and
plot_permutation() for significance-styled visualization of
bootstrap and permutation test results — significant edges rendered
solid on top, non-significant edges dashed behindplot_mixed_network() for overlaying symmetric
(undirected, straight) and asymmetric (directed, curved) edges on the
same networkplot_heatmap() for adjacency matrix heatmaps with
optional hierarchical clustering and plot_ml_heatmap() for
multilayer 3D perspective heatmapsplot_compare() for difference network
visualization showing edge-weight changes between two networkssplot() S3 methods for tna_bootstrap
and tna_permutation objectsmotif_census(), triad_census(), and
extract_motifs() for triad motif analysis with pattern
filtering, significance testing, and network diagram visualizationfilter_edges(), subset_edges(),
select_nodes(), select_edges() for flexible
network subsettingset_groups() for storing cluster assignments on
cograph_network objects with automatic dispatch to
plot_htna() / plot_mtna()cograph_network objects
as input, in addition to matrices, igraph objects, and tna objectslayout_spring and layout_gephi_fr
algorithms: vectorized attraction forces, edge aggregation for dense
networkspar(pin) error on exit when plot device state was
corruptedx across all
plotting functions:
plot_tna(): input → xplot_htna(): input → x (was
model)plot_mtna(): input → x (was
model)splot() already used xtplot() default margins causing tiny plots
compared to splot()vignettes/qgraph-to-splot.md)The following parameters have been renamed for consistency. The old names still work but emit deprecation warnings:
| Old Name | New Name | Reason |
|---|---|---|
esize |
edge_size |
Add edge_ prefix, expand abbreviation |
cut |
edge_cutoff |
Add edge_ prefix, clarify meaning |
usePCH |
use_pch |
Fix camelCase to snake_case |
positive_color |
edge_positive_color |
Add edge_ prefix (matches theme storage) |
negative_color |
edge_negative_color |
Add edge_ prefix (matches theme storage) |
donut_border_lty |
donut_line_type |
Expand lty abbreviation |
edge_label_fontface now accepts string values (“plain”,
“bold”, “italic”, “bold.italic”) in addition to numeric valuesmlna() for multilevel network visualization with
3D perspectivemtna() for multi-cluster network visualization
with shape-based cluster containersplot_htna() for hierarchical multi-group network
layouts with polygon and circular arrangementstplot() as a qgraph drop-in replacement with
automatic parameter translationarrow_angle parameter for customizable arrowhead
geometryedge_start_dot_density parameter for TNA-style
dotted edge starts indicating directionfrom_tna() —
no manual matrix extraction needednetwork and
qgraph objects as inputpie_values vector to
donut_fill when all values are in [0,1]splot() when other parameters were specifieddonut_shape validation rejecting custom SVG
shapesfrom_qgraph() when a layout
override was providednormalize_coords()from_qgraph() by using a
matrix intermediary for per-edge vector reorderingnrow(el) crash: qgraph’s Edgelist is a list, not
a data.framedonut_empty parameter for rendering unfilled
donut nodesfrom_qgraph() for converting qgraph objects to
cograph format, reading resolved graphAttributes for
accurate parameter extractionlayout_info guard causing errors on certain
device configurationssoplot() for grid/ggplot2-based network plotting
— full feature parity with splot() using a different
rendering backendlayout_oval() for oval/elliptical node
arrangementslayout_scale parameter to expand or contract the
network layout, with "auto" mode for node-count-based
scalingedge_start_style parameter for visually
indicating edge direction via styled start segments (dashed,
dotted)soplot() curve direction and edge defaults
diverging from splot() behaviorrescale_layout distorting oval aspect ratios by
switching to uniform scalingpar(pin) restoration error on plot device
exitsplot() — a base R graphics engine for network
visualization using polygon(), lines(), and
xspline(), providing better performance than grid-based
rendering for large networkssn_save() with
configurable DPIdonut_color API to accept 1 color (fill), 2 colors (fill +
background), or n colors (per-node)