The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
pathways() example pools three
named sources instead of all fourteen, and the animate()
examples draw fewer frames, so every example runs in well under a
second.thought_chains is documented as based on the Trees
of Thought study (Saqr, López-Pernas and Törmänen, 2026) with about
20 percent of the records removed, dates and rates changed and
anonymised, and the data augmented by simulation.mooc_people cites its source chapter directly; a stale
reference to a removed article is gone from the mooc_posts
example.First CRAN release. CRAN preparation: the maintainer is recorded as copyright holder, the README gives the CRAN installation line, and the animation article’s GIFs carry alternative text.
durations() is up to 37 times faster on large logs
(about 20,000 spells: 26 s to under 1 s). A spell between two vertices
without declared activity is now built as its own fragment in one
vectorised step, and each pair’s fragments are indexed once rather than
matched against every pair. Results are identical to 0.4.13.
The time-respecting path search behind paths(),
reachability(), path_centrality() and
pathways() is much faster. A search state’s candidate
entries are computed for all its ties at once, the ties leaving each
vertex are indexed once, and state keys are built in one call. Forward
searches also drop dominated states: a vertex reached no earlier than an
existing state with fewer hops. Such a state can never be, or lead to, a
shortest-foremost path, so arrival times, hop counts, path counts and
betweenness are identical to 0.4.13. On the thought_chains
data (23,017 contacts), path_centrality() drops from 314 s
to 5 s and one-source paths() from 35 s to under 1 s.
Backward searches are vectorised but not pruned.
dyn_reachability() is renamed
reachability(). The old name still works, forwards every
argument unchanged and warns with class dynet_deprecated;
it will be removed in a future release. Note that sna also
exports a reachability(): with both attached, the one
attached last wins, so call Dynet::reachability() when in
doubt.
dyn_centrality() is split in two, because its two
scopes returned different things. centrality_series() is
the old default (scope = "snapshot"): centrality in every
window, a series per vertex. path_centrality() is the old
scope = "temporal" for "closeness" and
"betweenness": one value per vertex from time-respecting
paths across the period, with no time column. Temporal
"reach" and "reach_count" are
reachability(). Neither new function has a
scope argument, so each returns one shape.
path_centrality(plot = TRUE) now draws, which the old
temporal scope silently ignored. dyn_centrality() still
works, returns exactly what it returned before and warns with class
dynet_deprecated. The new names avoid
cograph::centrality() and
tna::centralities().
centrality_series(measure = "strength") now weights
each spell by the share of its duration inside the window
(weight * overlap / duration) instead of counting its full
weight in every window it touches. Tiled windows therefore add back up
to the network’s total weight rather than counting a long spell once per
window. Point contacts keep their full weight in the window that holds
them, window = 0 still uses full weights at the instant,
and the part of a spell outside the observation period is not reassigned
to observed windows. Degree and every binary measure are unchanged.
snapshots(), animate() and the network plots
still count each spell’s full weight in every bin it touches, as
networkDynamic::network.collapse() does; their
documentation now says so.
plot(type = "events") marks where each link starts
the way cograph’s TNA styling does: the first 20% of each link, from its
source, is dotted. The new edge_start_style and
edge_start_length arguments, named as in
cograph::splot(), change or turn off the mark. Row labels
are now drawn in their actor’s colour, so they work as the colour key,
and nodes are larger.
A nodes = or ties = condition that
cannot be evaluated, such as one naming a column the table does not
have, now raises dynet_bad_selection (also
dynet_bad_input) instead of base R’s raw error, whose class
changed in R-devel and failed the devel CI check.
metrics()’ "temporal_density",
"observed_pair_density", "onset_intensity" and
"observed_pair_onset_intensity" no longer count time after
the data end as exposure. Without explicit observation bounds, a last
window reaching past the final spell divided by its full width, so tiled
windows did not pool to the whole-period value. Integration now stops at
the observation period, which defaults to the data’s span, as
tsna::tEdgeDensity() does. On school_contacts
with weekly windows the final partial week’s temporal density goes from
0.0013 to 0.0181; explicit observation_end values are
honoured as before.
metrics()’ "concurrent_nodes" and
"concurrent_share" now require simultaneity. With a
positive window they were read from the window’s union
snapshot, so a vertex tied to one partner early in the window and to
another later was counted as concurrent although the two ties never
overlapped. A vertex now counts when relations to two distinct
neighbours are active at the same instant somewhere in the window;
spells that only meet at a boundary do not overlap, and a point contact
is concurrent with whatever is active at its timestamp.
window = 0 results are unchanged. The result records the
rule in the concurrency_window_rule attribute.
mixing() is unaffected: it counts group pairs connected
anywhere in the window and never implied simultaneity.
loops = FALSE
drops is now removed before the thread’s lifetime is computed, so a
dropped post no longer keeps its thread alive. Threaded networks built
from logs with self-replies can have shorter spells than before;
loops = TRUE is unchanged.dynet() gains min_thread_posts: for a
threaded log, threads with fewer surviving posts are dropped whole and
reported with a message, so the “threads that never became an exchange”
rule of the chapter-17 analysis is one argument rather than a
hand-written filter. Requires thread; a value below 1 or a
non-threaded log raises dynet_bad_input /
dynet_needs_thread.plot() on a mixing() result accepts a
group name in highlight, which colours every flow into or
out of that group. A highlight that matches no series
raises dynet_unknown_highlight instead of drawing
everything grey.mooc_people carries expert_level, the
chapter’s label for the experience code, so the mixing attribute needs
no recode.vignette("ch17-temporal-networks")
builds the same network from the bundled data in one call. The site
deploy now clears files that are no longer built.set_tie_sessions() gains breaks and
labels: sessions can be cut on the time axis
(breaks = c(7, 14) gives three weeks) instead of being
derived by hand as a column and matched positionally against the spell
table.add_vertex_spells() and
update_vertex_spells() now refuse input they cannot honour
instead of accepting it silently. Supplying session to a
network with no session scheme raises
dynet_incompatible_vertex_spells rather than dropping the
label; add_vertex_spells() used to discard it and return
normally while update_vertex_spells() already errored on
the same input. Supplying a column outside the vertex-spell schema to
update_vertex_spells() raises
dynet_unknown_column rather than returning the object
unchanged, so a misspelled field is no longer a silent no-op.summary() gains temporal_density,
FALSE by default. That one row integrates exact occupancy
over every eligible ordered pair, so its cost is quadratic in the vertex
count: on a 442-vertex forum network it alone took about 32 seconds,
while the other fourteen rows were immediate. It now reads
"not computed" unless asked for, and summary()
on that network takes 0.3 seconds. Pass
temporal_density = TRUE for the number.as_dynet() placed per-edge attributes on the wrong
spell when importing an undirected
networkDynamic. dynet() canonicalises an
undirected pair before sorting its spells, and the importer derived its
ordering from the raw tail and head, so the two permutations disagreed
whenever the endpoints were stored in the other order. Attributes now
follow the canonical endpoints.
remove_ties() matched the start and
end selectors with exact equality on doubles, so a spell
that accumulated as 0.1 + 0.1 + 0.1 could not be removed by
naming 0.3. Times are now compared with the same
magnitude-relative tolerance the rest of the package uses.
dyn_centrality(measure = "closeness", scope = "temporal")
returned Inf without a word when every reachable vertex was
joined within one instant. Inf is still returned, since it
is the honest limit, but a dynet_zero_latency warning now
accompanies it.
Five statements that contradicted the code are corrected: kept
self-loops are counted by degree and contribute two,
snapshots(at = ) can return zero rows when the nearest bin
holds no active tie, animate(seed = NULL) leaves the
caller’s random state advanced rather than restored,
similarity(sessions = "separate") adds no session column,
and only one of the four Krackhardt indices is an index of
hierarchy.
set_tie_sessions() now documents that a full-length
vector is matched positionally against the sorted spell
table, not against the data frame the network was built from.
Derive labels from as.data.frame(dn).
dynet() now documents that canonical spell column
names – duration, weight,
session, thread, onset_censored,
terminus_censored – are dropped from tie attributes even
when never named as arguments.
Internal specification identifiers that had leaked into the manual pages with no definition anywhere are replaced by the measure names they referred to.
New verb animate(): the measurement grid as a film,
written to a GIF (gifski) or an mp4 or webm video
(av), chosen by the extension of file. It
takes the same four grid arguments as every measuring verb, so an
animation shows exactly what snapshots() tabulates and what
plot(dn, type = "snapshots") draws as a filmstrip, and a
test pins that the three agree bin for bin.
Each bin is drawn tween times, six by default. Between
bins the vertices glide along the smoothstep curve, a tie about to
appear fades in dotted and green, one about to vanish fades out dashed
and vermilion, and with measure = node size follows a
snapshot measure from dyn_centrality() on the same grid.
Tie width follows weight on one scale fixed across the whole animation,
so the same weight has the same width in every frame. A vertex not
present in a bin is drawn as absent says: faded in place,
parked out of sight at the edge of the layout and gliding in when it
arrives and out when it leaves, or hidden; a present vertex with no tie
is drawn as isolates says. A timeline strip under the
network shows the grid, a marker at the current time, and the key; both
the tie states and the strip can be turned off.
Five layouts and a coordinate table. "spring", the
default, lays out the union of every bin once; "circle",
"oval" and "groups" are rings;
"relaxed" re-runs cograph::layout_spring() per
bin, seeded from the bin before it and held within
max_displacement, then smooths every vertex’s path with a
centred triangular kernel, which halved the direction reversals between
consecutive moves on school_contacts at a two per cent cost
in structure. Under every layout but "relaxed" a vertex
never moves, and every layout covers the whole vertex set, so a vertex
never changes place because its neighbours came and went.
The file goes to tempfile() unless file
says otherwise, so nothing reaches the working directory by accident.
gifski and av are Suggests; without the one
the extension needs the verb raises dynet_needs_gifski or
dynet_needs_av, and an extension it cannot write raises
dynet_unknown_format. A bin holding nothing to draw is
skipped with a message that counts the skipped bins. The tidy bin table
comes back invisibly, one row per bin with time,
nodes, ties, forming,
dissolving and the bin’s first rendered frame,
as class dynet_animation with print(),
summary() and as.data.frame();
as.data.frame(x, what = "frames") maps every rendered frame
to its time.
New website article Animating a temporal network, a
tutorial on animate() over the classroom and the MOOC forum
data.
set_vertex_spells(dn, "ties") declares each vertex
present from the start of its first tie spell to the end of its last, so
a network built from a tie log alone can say when each vertex arrived
and left.
New vignette ch17-temporal-networks: chapter 17 of
Learning Analytics Methods and Tutorials (Saqr, 2024),
“Temporal network analysis: Introduction, methods and analysis with R”,
re-run with Dynet’s verbs in the chapter’s own order – build, active
subnetwork, visualisation, graph-level and node-level measures,
reachability, mixing. Its data is bundled as mooc_posts
(2529 posts across 338 discussion threads of a MOOC forum, April to June
2013) and mooc_people (445 participants with their
experience level), so the vignette reaches no network at render time.
The two places where the chapter’s own code changes its numbers – the
thread spell rule, and ties admitted before single-post discussions are
dropped – are named and measured rather than reproduced.
Backward routes on interval spells keep the route family of an
unattained supremum. An interval spell is half-open, so the latest
departure into a target is a supremum no journey reaches exactly.
paths(direction = "backward") used to report
NA hops, zero paths and no steps for such a vertex, which
left every backward trajectory tree on interval data drawing only its
source. The family that approaches the supremum is now reported in full
– hops, exact path count and reconstructed steps – and
attained is what records that the instant itself is not
realised. Route reconstruction under sessions = "bounded"
and sessions = "separate" is unchanged.
Reachability is anchored at each vertex’s own presence. With
vertex spells declared, dyn_reachability(),
dyn_centrality(scope = "temporal") and the default origin
of paths() start a vertex’s forward search at its first
appearance inside the window and its backward search at its last,
instead of at the window bound. A vertex that entered the network late
no longer scores zero; a backward search may anchor at the instant a
vertex leaves. An explicit at is still used
exactly.
dynet(nodes = ) names the vertices from a node table
whose key is not name but which has a name
column, as network(vertex.attrnames = ) does: edge
endpoints and vertex spells given by the key are translated, and the key
stays on the node table as an attribute. vertex.id,
node, vertex and node.id are
recognised as vertex keys.
dynet(vertex_spells = ) resolves its node, start and
end columns through the alias table and ignores other columns, so a node
table with onset and terminus columns is
accepted as it is.
rename_nodes() takes the name of a vertex attribute
whose values become the node names.
remove_ties(), remove_arcs() and
update_ties() take ties as a condition on the
spell table (ties = duration > 2), as
induce_subgraph() already did; positions and masks still
work.dynet() now canonicalises endpoints before sorting,
the order every rebuild uses, so update_ties(ties = 1:2)
edits the rows the caller saw.NA under a warning of class
dynet_kernel_singular instead of silently; the spectral
warning and this one share the parent class
dynet_measure_undefined.sample and
indegree/outdegree deprecation warnings carry
class dynet_deprecated; the duplicate-nodes
warning carries dynet_duplicate_nodes.n - 1 (directed) and n - 2
(undirected); pshifts() orders simultaneous turns by
speaker, group turn, then target in vertex order; temporal closeness is
Inf when every reachable vertex is reached at zero latency;
co-presence connects every member pair for the whole group span.eigenvector, hub and
authority are certified like eigenvector prestige: a
snapshot whose spectral radius is zero or whose Perron root is repeated
returns NA for that block under a warning of class
dynet_eigen_undefined, instead of one arbitrary basis
vector.dynet() picks up a column named weight,
weights or strength as the tie weight and says
so; before, an unnamed weight column was silently replaced by ones.window = 0 on the default grid now samples through the
last observed instant, as tsna does; a positive window is
unchanged.plot(dn, type = "timeline") and
"events" take step, a bin width
(1/24 on a network in days is hourly), and the timeline is
clipped to the declared observation window;
collapse_network() and path_network() results
have plot() methods with Dynet’s rendering defaults, so
plot(collapsed, layout = "oval") is the whole call.dynet() keeps every column of the log it did not
consume as a tie attribute, for interval, contact and threaded logs, so
induce_subgraph(ties = group == "A_01") works on a freshly
built network as it already did after as_dynet() and
add_ties().dynet(thread_clock = "relative") puts each thread of a
threaded log on its own clock, measured from the thread’s first post:
the Trees of Thought construction in one call.inst/reproduction/thought-chains/thought_chains.Rmd: the
Trees of Thought analysis on the bundled thought_chains,
with a gallery of every temporal view and result plot.thought_chains: the Trees of
Thought reply table (code of a message to the code of the message it
answers) with identities stripped, the bottom 20% of authors trimmed,
the two sparse weekdays removed, the calendar shifted by whole weeks,
and Evaluation merged with Acceptance into Approving. 23,017 links, 240
participants, nine codes.start + k * step with a step such as
1/24 fell one ulp short of the exact spell boundaries a
date-converted network carries, so a spell ending exactly at a window’s
start was counted inside it. Integer-hour and POSIXct-hour
encodings of one network now give identical series
(tests/testthat/test-bin-edge-snap.R).pathways(): whole time-respecting routes
ranked by how often they are used, with print,
summary, plot and
as.data.frame(what = "steps").plot = TRUE on the thirteen measurement verbs draws as
a side effect and still returns the tidy table, in the manner of
hist().induce_subgraph(ties = ) takes a condition on the spell
table, as nodes = already did.as.data.frame(x, what = "diagnostics") exposes the
prestige diagnostics; dynet_pshifts and
dynet_collapsed_list gained the full method set and
as.data.frame(x, session = ) reaches one session by
argument.synthdata, a resampled synthetic
twin of the Trees of Thought interaction data, with the reproduction
under inst/reproduction/.%||%. Continuous checking on macOS, Windows and Linux
(devel, release, oldrel-1, and a pinned 4.1).similarity() verb, centrality mode
("all", "out", "in"), node
selection by condition, temporal reach, closeness and betweenness,
bounded and session-aware path searches, per-hop traversal time, and the
first two vignettes.dyn_ prefix except
dyn_centrality() and dyn_reachability().These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.