Changelog
Source:NEWS.md
Dynet 0.5.1
- CRAN resubmission. The
pathways()example pools three named sources instead of all fourteen, and theanimate()examples draw fewer frames, so every example runs in well under a second. -
thought_chainsis 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_peoplecites its source chapter directly; a stale reference to a removed article is gone from themooc_postsexample. - The package help page carries the current Description, and a local-only reproduction script no longer ships.
Dynet 0.5.0
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()andpathways()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 thethought_chainsdata (23,017 contacts),path_centrality()drops from 314 s to 5 s and one-sourcepaths()from 35 s to under 1 s. Backward searches are vectorised but not pruned.
Dynet 0.4.13
Breaking changes
dyn_reachability()is renamedreachability(). The old name still works, forwards every argument unchanged and warns with classdynet_deprecated; it will be removed in a future release. Note thatsnaalso exports areachability(): with both attached, the one attached last wins, so callDynet::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 oldscope = "temporal"for"closeness"and"betweenness": one value per vertex from time-respecting paths across the period, with notimecolumn. Temporal"reach"and"reach_count"arereachability(). Neither new function has ascopeargument, 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 classdynet_deprecated. The new names avoidcograph::centrality()andtna::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 = 0still 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, asnetworkDynamic::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 newedge_start_styleandedge_start_lengtharguments, named as incograph::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.
Bug fixes
A
nodes =orties =condition that cannot be evaluated, such as one naming a column the table does not have, now raisesdynet_bad_selection(alsodynet_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, astsna::tEdgeDensity()does. Onschool_contactswith weekly windows the final partial week’s temporal density goes from 0.0013 to 0.0181; explicitobservation_endvalues are honoured as before.metrics()’"concurrent_nodes"and"concurrent_share"now require simultaneity. With a positivewindowthey 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 = 0results are unchanged. The result records the rule in theconcurrency_window_ruleattribute.mixing()is unaffected: it counts group pairs connected anywhere in the window and never implied simultaneity.
Dynet 0.4.12
Breaking changes
- In a threaded log, a self-reply that
loops = FALSEdrops 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 = TRUEis unchanged.
New features
-
dynet()gainsmin_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. Requiresthread; a value below 1 or a non-threaded log raisesdynet_bad_input/dynet_needs_thread. -
plot()on amixing()result accepts a group name inhighlight, which colours every flow into or out of that group. Ahighlightthat matches no series raisesdynet_unknown_highlightinstead of drawing everything grey. -
mooc_peoplecarriesexpert_level, the chapter’s label for the experience code, so the mixing attribute needs no recode. - The website article that rebuilt the MOOC network from the published CSV files was removed;
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()gainsbreaksandlabels: 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.
Dynet 0.4.7
Breaking changes
-
add_vertex_spells()andupdate_vertex_spells()now refuse input they cannot honour instead of accepting it silently. Supplyingsessionto a network with no session scheme raisesdynet_incompatible_vertex_spellsrather than dropping the label;add_vertex_spells()used to discard it and return normally whileupdate_vertex_spells()already errored on the same input. Supplying a column outside the vertex-spell schema toupdate_vertex_spells()raisesdynet_unknown_columnrather than returning the object unchanged, so a misspelled field is no longer a silent no-op.
Performance
-
summary()gainstemporal_density,FALSEby 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, andsummary()on that network takes 0.3 seconds. Passtemporal_density = TRUEfor the number.
Bug fixes
as_dynet()placed per-edge attributes on the wrong spell when importing an undirectednetworkDynamic.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 thestartandendselectors with exact equality on doubles, so a spell that accumulated as0.1 + 0.1 + 0.1could not be removed by naming0.3. Times are now compared with the same magnitude-relative tolerance the rest of the package uses.dyn_centrality(measure = "closeness", scope = "temporal")returnedInfwithout a word when every reachable vertex was joined within one instant.Infis still returned, since it is the honest limit, but adynet_zero_latencywarning now accompanies it.
Documentation
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 fromas.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.
Dynet 0.4.6
-
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 offile. It takes the same four grid arguments as every measuring verb, so an animation shows exactly whatsnapshots()tabulates and whatplot(dn, type = "snapshots")draws as a filmstrip, and a test pins that the three agree bin for bin.Each bin is drawn
tweentimes, 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 withmeasure =node size follows a snapshot measure fromdyn_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 asabsentsays: 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 asisolatessays. 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-runscograph::layout_spring()per bin, seeded from the bin before it and held withinmax_displacement, then smooths every vertex’s path with a centred triangular kernel, which halved the direction reversals between consecutive moves onschool_contactsat 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()unlessfilesays otherwise, so nothing reaches the working directory by accident.gifskiandavare Suggests; without the one the extension needs the verb raisesdynet_needs_gifskiordynet_needs_av, and an extension it cannot write raisesdynet_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 withtime,nodes,ties,forming,dissolvingand the bin’s first renderedframe, as classdynet_animationwithprint(),summary()andas.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 asmooc_posts(2529 posts across 338 discussion threads of a MOOC forum, April to June 2013) andmooc_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 reportNAhops, 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 – andattainedis what records that the instant itself is not realised. Route reconstruction undersessions = "bounded"andsessions = "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 ofpaths()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 explicitatis still used exactly.dynet(nodes = )names the vertices from a node table whose key is notnamebut which has anamecolumn, asnetwork(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,vertexandnode.idare 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 withonsetandterminuscolumns is accepted as it is.rename_nodes()takes the name of a vertex attribute whose values become the node names.
Dynet 0.4.4
- Every example, the README, both vignettes and the reproductions are written one call per line: a verb’s result is named, and the next verb takes that object. No call is nested inside another. Nothing computed changed.
Dynet 0.4.3
-
remove_ties(),remove_arcs()andupdate_ties()taketiesas a condition on the spell table (ties = duration > 2), asinduce_subgraph()already did; positions and masks still work. - On an undirected network a tie keeps its row position through an edit:
dynet()now canonicalises endpoints before sorting, the order every rebuild uses, soupdate_ties(ties = 1:2)edits the rows the caller saw. - Bonacich power and information centrality on a singular system return
NAunder a warning of classdynet_kernel_singularinstead of silently; the spectral warning and this one share the parent classdynet_measure_undefined. - The
sampleandindegree/outdegreedeprecation warnings carry classdynet_deprecated; the duplicate-nodeswarning carriesdynet_duplicate_nodes. - Documentation states what was previously only implemented: assortativity correlates total degree at both ends of every arc; closeness centralisation uses this package’s reachable-set closeness with maxima
n - 1(directed) andn - 2(undirected);pshifts()orders simultaneous turns by speaker, group turn, then target in vertex order; temporal closeness isInfwhen every reachable vertex is reached at zero latency; co-presence connects every member pair for the whole group span.
Dynet 0.4.2
-
eigenvector,hubandauthorityare certified like eigenvector prestige: a snapshot whose spectral radius is zero or whose Perron root is repeated returnsNAfor that block under a warning of classdynet_eigen_undefined, instead of one arbitrary basis vector. -
dynet()picks up a column namedweight,weightsorstrengthas the tie weight and says so; before, an unnamed weight column was silently replaced by ones. -
window = 0on the default grid now samples through the last observed instant, astsnadoes; a positive window is unchanged.
Dynet 0.4.1
- Plot defaults: node-level bar charts and the frequency trajectory tree colour by vertex, in the network’s own order, so a vertex keeps one colour across every view;
plot(dn, type = "timeline")and"events"takestep, a bin width (1/24on a network in days is hourly), and the timeline is clipped to the declared observation window;collapse_network()andpath_network()results haveplot()methods with Dynet’s rendering defaults, soplot(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, soinduce_subgraph(ties = group == "A_01")works on a freshly built network as it already did afteras_dynet()andadd_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. - Reproduction
inst/reproduction/thought-chains/thought_chains.Rmd: the Trees of Thought analysis on the bundledthought_chains, with a gallery of every temporal view and result plot. - New bundled dataset
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. - Window edges are snapped onto the data’s own boundaries. A grid built as
start + k * stepwith a step such as1/24fell 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 andPOSIXct-hour encodings of one network now give identical series (tests/testthat/test-bin-edge-snap.R).
Dynet 0.4.0
- New verb
pathways(): whole time-respecting routes ranked by how often they are used, withprint,summary,plotandas.data.frame(what = "steps"). -
plot = TRUEon the thirteen measurement verbs draws as a side effect and still returns the tidy table, in the manner ofhist(). -
induce_subgraph(ties = )takes a condition on the spell table, asnodes =already did. -
as.data.frame(x, what = "diagnostics")exposes the prestige diagnostics;dynet_pshiftsanddynet_collapsed_listgained the full method set andas.data.frame(x, session = )reaches one session by argument. - New bundled dataset
synthdata, a resampled synthetic twin of the Trees of Thought interaction data, with the reproduction underinst/reproduction/. - The dependency floor is R >= 4.1; the package defines its own
%||%. Continuous checking on macOS, Windows and Linux (devel, release, oldrel-1, and a pinned 4.1). - Events landing just past a bin boundary are no longer discarded; the final default window is closed on its right edge.
- Two wrong-output defects found by adversarial review were fixed, and the internal helper pages were removed from the manual.
Dynet 0.3 series
- The
similarity()verb, centralitymode("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. - Public verbs dropped the
dyn_prefix exceptdyn_centrality()anddyn_reachability().