Skip to contents

This vignette demonstrates temporal network analysis of coded contributions to asynchronous course discussions. The thought_chains dataset provides reply relationships in tidy format, with contributions classified into categories such as inquiring, arguing, and approving. Contribution categories constitute the vertices, and directed ties connect the category of a reply to the category of the message it addresses.

The analysis examines how relationships among contribution categories evolve over time, which categories receive replies, and how categories are connected through time-respecting paths. Network measures, centrality indices, relational durations, and temporal paths describe the structure, timing, and reachability of these relationships.

Data

The dataset is synthetic and based on the original data analysed by Saqr, López-Pernas, and Törmänen (2026). It is used to demonstrate temporal network construction, measurement, and visualisation. Results reported in this vignette describe the supplied dataset and should not be interpreted as empirical findings from the original study.

The synthetic dataset contains 23,017 reply links among nine contribution categories, spanning 1,169 discussions in 29 groups across five courses. The relational endpoints are from, the category assigned to the reply, and to, the category assigned to the message it addresses. The variable time records when the reply was posted; discussion, course, and group identify the discussion thread, course, and course group, respectively. The dataset includes 9,452 self-links, where the reply and the message it addresses share the same category.

library(Dynet)
head(thought_chains)
##           from           to                time participant discussion group
## 1 Coordinating Coordinating 2006-09-23 18:06:29        P028          1  A_01
## 2 Coordinating Coordinating 2006-09-23 18:06:29        P028          1  A_01
## 3 Coordinating Coordinating 2006-09-23 18:06:29        P028          1  A_01
## 4 Coordinating Coordinating 2006-09-23 18:06:29        P028          1  A_01
## 5 Coordinating Coordinating 2006-09-23 18:06:29        P028          1  A_01
## 6 Coordinating Coordinating 2006-09-23 18:06:29        P028          1  A_01
##   course
## 1      A
## 2      A
## 3      A
## 4      A
## 5      A
## 6      A

The network

Each reply is represented as a relational spell with onset at the time of posting and termination at the end of its discussion. This specification treats the relationship as active for the remaining duration of the discussion.

The call to dynet() explicitly identifies discussion as the thread variable through thread = "discussion". Setting thread_clock = "relative" expresses time in days since the first recorded reply in each discussion, while loops = TRUE retains self-links and observation_end = 4 specifies a four-day observation period. The constructor automatically recognises course as the session variable.

dn <- dynet(thought_chains, thread = "discussion", thread_clock = "relative",
            loops = TRUE, observation_end = 4)
## Keeping 9452 self-loop event(s); each adds two to its vertex's degree.
dn
## # Temporal network (threaded format, directed) | a cograph netobject
## # 9 vertices | 23017 edge spells | 80 distinct pairs
## # observed from 0 to 4 days, binned every 1
## # 5 sessions: A, B, C, D, E
## 
##       from         to start end duration weight session thread participant
##  Approving  Approving     0   0        0      1       B    282        P181
##  Approving   Drafting     0   0        0      1       B    300        P189
##  Approving  Inquiring     0   0        0      1       D    839        P063
##  Approving Resourcing     0   0        0      1       A     62        P006
##  Approving Resourcing     0   0        0      1       A    187        P232
##  Approving Resourcing     0   0        0      1       A    187        P232
##  group
##   B_02
##   B_03
##   D_02
##   A_05
##   A_04
##   A_04
## # 23011 more spells. summary() describes the network; plot() draws it.
##                 property        value
## 1                 format     threaded
## 2               directed          yes
## 3               vertices            9
## 4            edge spells        23017
## 5         distinct pairs           80
## 6              time unit         days
## 7          observed from            0
## 8            observed to            4
## 9                   span            4
## 10             bin width            1
## 11             time bins            4
## 12 mean snapshot density       0.8958
## 13      temporal density not computed
## 14              sessions            5
## 15     vertex attributes         none

Ties are directed from the category of a reply to the category of the message it addresses. Paths following these ties therefore represent directed reply relationships between contribution categories. They do not, by themselves, establish the forward dissemination of ideas or the transmission of particular content.

Setting thread_clock = "relative" aligns each discussion to its first recorded reply. Temporal overlap consequently represents comparable elapsed times within discussions, rather than simultaneous activity on a shared calendar. The constructor recognises course as the session variable. With the default sessions = "bounded", each temporal path remains within a single course, although it may combine ties from different discussions or groups within that course. These paths describe category-level reachability under the specified alignment and session boundaries.

The resulting temporal network contains nine vertices and 23,017 relational spells distributed across 80 of the 81 possible ordered pairs, including self-pairs. The mean snapshot density is 0.8958. This high density reflects the specification that relational spells remain active until their discussions terminate.

Ties over time

events() computes relational spell onset and termination counts over successive half-day intervals. Setting both step and window to 0.5 produces non-overlapping intervals.

tie_events <- events(dn, measure = c("formation", "dissolution"),
                     step = 0.5, window = 0.5)
tie_events
## # Edge dynamics (graph-level)
## # 8 time points, 0.5 per bin | time in days
## # measures: formation, dissolution
##  time     measure value
##   0.0   formation 13094
##   0.0 dissolution  3842
##   0.5   formation  4435
##   0.5 dissolution  2960
##   1.0   formation  2442
##   1.0 dissolution  5348
##   1.5   formation  1457
##   1.5 dissolution  2819
##   2.0   formation  1021
##   2.0 dissolution  3675
##   2.5   formation   377
##   2.5 dissolution  1996
## # 4 more rows. summary() aggregates them; plot() draws them.

The first three intervals contain 13,094, 4,435, and 2,442 onsets, respectively, compared with 10 in the final interval. Terminations peak in the third interval, with 5,348 spells ending. Relational spell onsets are therefore concentrated early in the observation period.

The timeline displays relational activity for the 40 ordered pairs with the most spells, using an hourly grid. Colour represents the proportion of each interval during which the pair was active, counting overlapping spells once.

plot(dn, type = "timeline", step = 1 / 24)

The activity plot displays onset and termination counts alongside the number of active relational spells over time.

plot(dn, type = "activity")

Drawing the network

Setting type = "network" displays the union of ties active during the observation period. The network is drawn using cograph, to which the layout argument is passed.

plot(dn, type = "network", layout = "oval")

The binary union records whether each ordered pair was connected at any time during the observation period. To account for duration, collapse_network() with weight = "union_duration" weights each pair by the total duration for which at least one relational spell was active, counting overlapping spells once.

collapsed <- collapse_network(dn, weight = "union_duration")
plot(collapsed, layout = "oval")

Setting type = "snapshots" displays the network at nine equally spaced time points using a common layout.

plot(dn, type = "snapshots", panels = 9)

The proximity timeline represents changes in the relationships among contribution categories. Network distances are computed within overlapping temporal slices and reduced to a single coordinate for each category. Lines connect these coordinates across slices, with proximity indicating shorter network distances within a slice. Line thickness represents degree by default. The following call specifies 80 slices and five accompanying phase networks.

plot(dn, type = "proximity", phases = 5, slices = 80)

Setting measure = "betweenness" maps line thickness to betweenness centrality, while networks = FALSE omits the phase networks.

plot(dn, type = "proximity", measure = "betweenness",
     networks = FALSE, slices = 80)

Layered visualisations represent successive temporal slices as separate network layers. Setting type = "layers" and step = 4 / 3 displays three slices as planes.

plot(dn, type = "layers", step = 4 / 3, layout = "circle")

Setting type = "stack" and cumulative = TRUE produces a cumulative projection in which each plane includes all ties active up to that slice.

plot(dn, type = "stack", step = 4 / 3, cumulative = TRUE)

Structure over time

Dynet computes both graph-level and vertex-level metrics for temporal networks. Graph-level metrics characterise the network as a whole, describing properties such as density, reciprocity, and connectivity. Vertex-level metrics characterise the structural position of individual vertices through measures such as degree, closeness, and betweenness. In this vignette, vertices represent contribution categories, so vertex-level metrics describe the positions of these categories within the discussion network. Both levels can be examined over successive temporal intervals to quantify changes in network structure and vertex position.

metrics() computes graph-level measures over specified temporal intervals. Setting both step and window to 1 / 24 produces successive, non-overlapping hourly intervals. Density measures the proportion of possible directed ties present, while reciprocity measures the proportion of directed ties whose reverse is also present. Efficiency and connectedness are two of Krackhardt’s (1994) indices for characterising departures from an out-tree structure. The number of edges and mean degree provide additional descriptions of network connectivity.

structure <- metrics(dn,
                     measure = c("density", "reciprocity", "efficiency",
                                 "connectedness", "edges", "mean_degree"),
                     step = 1 / 24, window = 1 / 24)
structure
## # Graph structure (graph-level)
## # 96 time points, 0.04166667 per bin | time in days
## # measures: density, reciprocity, efficiency, connectedness, edges, mean_degree
##        time       measure      value
##  0.00000000       density  0.7916667
##  0.00000000   reciprocity  0.9122807
##  0.00000000    efficiency  0.2343750
##  0.00000000 connectedness  1.0000000
##  0.00000000         edges 57.0000000
##  0.00000000   mean_degree  6.3333333
##  0.04166667       density  0.8194444
##  0.04166667   reciprocity  0.9152542
##  0.04166667    efficiency  0.2031250
##  0.04166667 connectedness  1.0000000
##  0.04166667         edges 59.0000000
##  0.04166667   mean_degree  6.5555556
## # 564 more rows. summary() aggregates them; plot() draws them.
plot(structure, type = "ridge")

Setting type = "ridge" displays a separate panel for each measure. In the first hour, the network contains 57 directed ties and has a density of 0.79. Connectedness is 1, indicating that all contribution categories belong to a single weakly connected component; this does not imply that every category is directly connected to every other category. Reciprocity is 0.91, indicating that most directed ties have a corresponding reverse tie within the interval. Density increases to 0.82 in the second hour.

The dyad census (Holland and Leinhardt, 1976) classifies unordered pairs of distinct vertices as mutual, asymmetric, or null, depending on whether both, one, or neither of the possible directed ties are present. The corresponding counts are requested through measure.

dyads <- metrics(dn, measure = c("mutual", "asymmetric", "null"),
                 step = 1 / 24, window = 1 / 24)
dyads
## # Graph structure (graph-level)
## # 96 time points, 0.04166667 per bin | time in days
## # measures: mutual, asymmetric, null
##        time    measure value
##  0.00000000     mutual    26
##  0.00000000 asymmetric     5
##  0.00000000       null     5
##  0.04166667     mutual    27
##  0.04166667 asymmetric     5
##  0.04166667       null     4
##  0.08333333     mutual    28
##  0.08333333 asymmetric     4
##  0.08333333       null     4
##  0.12500000     mutual    28
##  0.12500000 asymmetric     5
##  0.12500000       null     3
## # 276 more rows. summary() aggregates them; plot() draws them.
plot(dyads)

In the first hour, the 36 unordered pairs comprise 26 mutual, five asymmetric, and five null dyads. These counts describe connectivity between distinct contribution categories and exclude self-links.

Setting measure = "triads" computes the sixteen-class directed triad census, which classifies sets of three distinct vertices according to their directed ties. The heatmap displays changes in the frequency of each triad class across hourly intervals.

triads <- metrics(dn, measure = "triads", step = 1 / 24, window = 1 / 24)
triads
## # Triad census (graph-level)
## # 96 time points, 0.04166667 per bin | time in days
## # measures: triad_003, triad_012, triad_102, triad_021D, triad_021U, triad_021C, triad_111D, triad_111U, triad_030T, triad_030C, triad_201, triad_120D, triad_120U, triad_120C, triad_210, triad_300
##  time    measure value
##     0  triad_003     0
##     0  triad_012     1
##     0  triad_102     9
##     0 triad_021D     0
##     0 triad_021U     0
##     0 triad_021C     0
##     0 triad_111D     0
##     0 triad_111U     8
##     0 triad_030T     0
##     0 triad_030C     0
##     0  triad_201     7
##     0 triad_120D     3
## # 1524 more rows. summary() aggregates them; plot() draws them.
plot(triads, type = "heatmap")

Additional graph-level statistics describe local configurations of directed ties. These include two-stars, two-paths, triangles, and sums of indegrees and outdegrees raised to the power 1.5. Such statistics are also used as terms in exponential-family random graph models (Morris, Handcock and Hunter, 2008).

local <- metrics(dn,
                 measure = c("indegree_1_5", "outdegree_1_5", "triangles",
                             "in_2stars", "out_2stars", "two_paths"),
                 step = 1 / 24, window = 1 / 24)
local
## # Graph structure (graph-level)
## # 96 time points, 0.04166667 per bin | time in days
## # measures: indegree_1_5, outdegree_1_5, triangles, in_2stars, out_2stars, two_paths
##        time       measure    value
##  0.00000000  indegree_1_5 146.3021
##  0.00000000 outdegree_1_5 148.0187
##  0.00000000     triangles 376.0000
##  0.00000000     in_2stars 161.0000
##  0.00000000    out_2stars 166.0000
##  0.00000000     two_paths 325.0000
##  0.04166667  indegree_1_5 155.0977
##  0.04166667 outdegree_1_5 155.3815
##  0.04166667     triangles 436.0000
##  0.04166667     in_2stars 176.0000
##  0.04166667    out_2stars 177.0000
##  0.04166667     two_paths 356.0000
## # 564 more rows. summary() aggregates them; plot() draws them.
plot(local, type = "ridge")

The first hourly interval contains 376 directed triangles and 325 two-paths; the corresponding counts in the second interval are 436 and 356.

Vertex-level metrics

centrality_series() computes centrality measures for each contribution category over specified temporal intervals. The following call requests degree, closeness, and betweenness (Freeman, 1979), eigenvector centrality, flow betweenness (Freeman, Borgatti and White, 1991), and diffusion degree (Kundu, Murthy and Pal, 2011). These measures characterise the positions of categories within each hourly network. Paths used in these interval-specific calculations are evaluated within the corresponding network; temporal centrality based on time-respecting paths is considered separately below.

centrality <- centrality_series(dn,
                                measure = c("degree", "closeness", "betweenness",
                                            "eigenvector", "flow_betweenness",
                                            "diffusion"),
                                step = 1 / 24, window = 1 / 24)
centrality
## # Centrality (node-level)
## # 9 vertices | 96 time points, 0.04166667 per bin | time in days
## # measures: degree, closeness, betweenness, eigenvector, flow_betweenness, diffusion
##  time         node   measure      value
##     0    Approving    degree 14.0000000
##     0      Arguing    degree 17.0000000
##     0 Coordinating    degree 15.0000000
##     0     Drafting    degree 15.0000000
##     0    Inquiring    degree 15.0000000
##     0    Objecting    degree  7.0000000
##     0   Resourcing    degree 16.0000000
##     0  Socialising    degree 18.0000000
##     0     Tutoring    degree 15.0000000
##     0    Approving closeness  0.8888889
##     0      Arguing closeness  1.0000000
##     0 Coordinating closeness  0.8888889
## # 5172 more rows. summary() aggregates them; plot() draws them.
plot(centrality, type = "heatmap")

Setting type = "heatmap" displays centrality values by category and hourly interval. Setting type = "ridge" displays their trajectories over the observation period.

plot(centrality, type = "ridge")

In the first hour, Socialising has the highest degree (18), and Objecting has the lowest (7). Arguing has a closeness centrality of 1, indicating that it reaches every other category in one step under the specified calculation. Across the 96 hourly intervals, Objecting has the lowest mean degree, closeness, and betweenness.

For directed networks, mode specifies the direction used to calculate degree. Because ties are directed from the reply category to the category being addressed, mode = "in" measures incoming connections from replying categories, whereas mode = "out" measures outgoing connections to addressed categories.

in_degree <- centrality_series(dn, measure = "degree", mode = "in",
                               step = 1 / 24, window = 1 / 24)
out_degree <- centrality_series(dn, measure = "degree", mode = "out",
                                step = 1 / 24, window = 1 / 24)
plot(in_degree, type = "heatmap")

plot(out_degree, type = "heatmap")

Prestige indices characterise a vertex through its incoming relationships (Wasserman and Faust, 1994). Indegree prestige measures direct incoming connections. Domain proximity prestige incorporates indirect connections by dividing the proportion of other vertices that can reach the focal vertex by their mean directed distance to it. Setting measure = "prestige" requests prestige, with the variant specified through prestige.

prestige_indegree <- centrality_series(dn, measure = "prestige",
                                       prestige = "indegree",
                                       step = 1 / 24, window = 1 / 24)
prestige_proximity <- centrality_series(dn, measure = "prestige",
                                        prestige = "domain.proximity",
                                        step = 1 / 24, window = 1 / 24)
plot(prestige_indegree, type = "heatmap")

plot(prestige_proximity, type = "heatmap")

Flows between codes

mixing() summarises directed relationships between categories of a vertex attribute within each temporal interval. Setting attribute = "name" uses the vertex names, which represent contribution categories in this network. The result contains counts of distinct active ordered vertex pairs for each hourly interval and pair of categories. Repeated spells and their weights do not multiply these counts. Setting type = "heatmap" displays all 81 ordered pairs, including self-pairs.

flows <- mixing(dn, attribute = "name", step = 1 / 24, window = 1 / 24)
flows
## # Mixing by name (graph-level)
## # 96 time points, 0.04166667 per bin | time in days
## # measures: Approving -> Approving, Arguing -> Approving, Coordinating -> Approving, Drafting -> Approving, Inquiring -> Approving, Objecting -> Approving, Resourcing -> Approving, Socialising -> Approving, Tutoring -> Approving, Approving -> Arguing, Arguing -> Arguing, Coordinating -> Arguing, Drafting -> Arguing, Inquiring -> Arguing, Objecting -> Arguing, Resourcing -> Arguing, Socialising -> Arguing, Tutoring -> Arguing, Approving -> Coordinating, Arguing -> Coordinating, Coordinating -> Coordinating, Drafting -> Coordinating, Inquiring -> Coordinating, Objecting -> Coordinating, Resourcing -> Coordinating, Socialising -> Coordinating, Tutoring -> Coordinating, Approving -> Drafting, Arguing -> Drafting, Coordinating -> Drafting, Drafting -> Drafting, Inquiring -> Drafting, Objecting -> Drafting, Resourcing -> Drafting, Socialising -> Drafting, Tutoring -> Drafting, Approving -> Inquiring, Arguing -> Inquiring, Coordinating -> Inquiring, Drafting -> Inquiring, Inquiring -> Inquiring, Objecting -> Inquiring, Resourcing -> Inquiring, Socialising -> Inquiring, Tutoring -> Inquiring, Approving -> Objecting, Arguing -> Objecting, Coordinating -> Objecting, Drafting -> Objecting, Inquiring -> Objecting, Objecting -> Objecting, Resourcing -> Objecting, Socialising -> Objecting, Tutoring -> Objecting, Approving -> Resourcing, Arguing -> Resourcing, Coordinating -> Resourcing, Drafting -> Resourcing, Inquiring -> Resourcing, Objecting -> Resourcing, Resourcing -> Resourcing, Socialising -> Resourcing, Tutoring -> Resourcing, Approving -> Socialising, Arguing -> Socialising, Coordinating -> Socialising, Drafting -> Socialising, Inquiring -> Socialising, Objecting -> Socialising, Resourcing -> Socialising, Socialising -> Socialising, Tutoring -> Socialising, Approving -> Tutoring, Arguing -> Tutoring, Coordinating -> Tutoring, Drafting -> Tutoring, Inquiring -> Tutoring, Objecting -> Tutoring, Resourcing -> Tutoring, Socialising -> Tutoring, Tutoring -> Tutoring
## # active binary-dyad counts between vertex groups per time bin
##  time                   measure value   from_group  to_group
##     0    Approving -> Approving     1    Approving Approving
##     0      Arguing -> Approving     1      Arguing Approving
##     0 Coordinating -> Approving     0 Coordinating Approving
##     0     Drafting -> Approving     1     Drafting Approving
##     0    Inquiring -> Approving     1    Inquiring Approving
##     0    Objecting -> Approving     0    Objecting Approving
##     0   Resourcing -> Approving     1   Resourcing Approving
##     0  Socialising -> Approving     1  Socialising Approving
##     0     Tutoring -> Approving     1     Tutoring Approving
##     0      Approving -> Arguing     1    Approving   Arguing
##     0        Arguing -> Arguing     1      Arguing   Arguing
##     0   Coordinating -> Arguing     1 Coordinating   Arguing
## # 7764 more rows. summary() aggregates them; plot() draws them.
plot(flows, type = "heatmap")

The highlight argument selects a category for visual emphasis. The following plot displays incoming and outgoing relationships involving Coordinating in colour and the remaining relationships in grey.

plot(flows, highlight = "Coordinating")

Duration

durations() summarises relational spells and their durations over the observation period. Setting unit = "node_ties" groups spells by their incident vertex, while mode = "all" includes both incoming and outgoing incidences. The requested measures are the incident spell count ("events"), summed incident duration ("total"), and union duration ("union"). A self-link contributes twice to the additive counts and durations, once for each endpoint. Summed duration includes overlapping spells separately, whereas union duration counts each period of activity once.

node_ties <- durations(dn, unit = "node_ties", mode = "all",
                       measure = c("events", "total", "union"))
node_ties
## # Incident tie duration (node-level)
## # 9 vertices | mode all | time in days
## # measures: events, total, union
## # durations in days
##          node measure     value
##     Approving  events  5825.000
##       Arguing  events  6174.000
##  Coordinating  events  2713.000
##      Drafting  events   779.000
##     Inquiring  events  1892.000
##     Objecting  events   109.000
##    Resourcing  events 20999.000
##   Socialising  events  5493.000
##      Tutoring  events  2034.000
##     Approving   total  5355.039
##       Arguing   total  5539.510
##  Coordinating   total  1389.779
## # 15 more rows. summary() aggregates them; plot() draws them.
plot(node_ties)

Resourcing has 20,999 incident spell counts with a summed duration of 22,781 days, compared with 109 and 71 days for Objecting. These summed durations accumulate time across spell incidences and can therefore exceed the observation period. Union duration is bounded by the four-day observation period; six of the nine categories have at least one incident spell active throughout this period.

Setting unit = "pair" computes the same summaries for each ordered pair of contribution categories. Including "mean" additionally returns the mean spell duration.

pair_duration <- durations(dn, unit = "pair",
                           measure = c("events", "total", "union", "mean"))
pair_duration
## # Relationship duration (edge-level)
## # time in days
## # measures: events, mean, total, union
## # durations in days
##       from           to measure value
##  Approving    Approving  events   719
##  Approving      Arguing  events   417
##  Approving Coordinating  events    30
##  Approving     Drafting  events    30
##  Approving    Inquiring  events   110
##  Approving    Objecting  events     4
##  Approving   Resourcing  events  1353
##  Approving  Socialising  events   385
##  Approving     Tutoring  events   100
##    Arguing    Approving  events   415
##    Arguing      Arguing  events   670
##    Arguing Coordinating  events    44
## # 308 more rows. summary() aggregates them; plot() draws them.
plot(pair_duration)

Spell counts vary substantially across ordered pairs. For example, 1,353 spells connect Approving to Resourcing, compared with four connecting Approving to Objecting.

Reachability and temporal centrality

Temporal reachability describes whether vertices can be connected through time-respecting paths, whose successive traversals follow chronological order and respect tie availability (Kempe, Kleinberg and Kumar, 2002). reachability() computes the proportion ("reach") and number ("reach_count") of other vertices reachable along these paths. Setting direction = "both" requests both forward reachability and backward reachability: which categories a focal category can reach and which can reach it.

reach <- reachability(dn, direction = "both",
                      measure = c("reach", "reach_count"))
reach
## # Reachability (node-level)
## # 9 vertices | time in days
## # measures: forward_reach, forward_reach_count, backward_reach, backward_reach_count
## # count and share of other vertices joined by a time-respecting path
##          node             measure value
##     Approving       forward_reach     1
##       Arguing       forward_reach     1
##  Coordinating       forward_reach     1
##      Drafting       forward_reach     1
##     Inquiring       forward_reach     1
##     Objecting       forward_reach     1
##    Resourcing       forward_reach     1
##   Socialising       forward_reach     1
##      Tutoring       forward_reach     1
##     Approving forward_reach_count     8
##       Arguing forward_reach_count     8
##  Coordinating forward_reach_count     8
## # 24 more rows. summary() aggregates them; plot() draws them.
plot(reach)

Every category can reach all eight other categories and can be reached from all eight. Under the specified spell durations and observation period, reachability therefore does not differentiate the categories. Temporal centrality and path analysis provide further information about arrival times and the routes connecting them.

Temporal closeness measures how quickly a vertex can reach other vertices, whereas temporal betweenness measures its contribution to earliest-arrival paths between other vertices (Pan and Saramäki, 2011). These measures are requested through path_centrality().

When traversal has zero duration, paths composed of ties active at time zero can arrive immediately, yielding zero latency and potentially unbounded temporal closeness. Setting traversal_time = 0.1 imposes a traversal duration of one tenth of a day per tie. This is an analytical assumption rather than an observed response time.

temporal <- path_centrality(dn, measure = c("closeness", "betweenness"),
                            traversal_time = 0.1)
temporal
## # Temporal centrality (node-level)
## # 9 vertices | traversal 0.1 days per hop | time in days
## # measures: closeness, betweenness
## # computed on time-respecting paths across the whole window
##          node     measure     value
##     Approving   closeness  7.710069
##       Arguing   closeness  9.301324
##  Coordinating   closeness  8.269624
##      Drafting   closeness  7.010284
##     Inquiring   closeness  8.375948
##     Objecting   closeness  5.512050
##    Resourcing   closeness  8.577492
##   Socialising   closeness  9.731100
##      Tutoring   closeness 10.000000
##     Approving betweenness  0.000000
##       Arguing betweenness  2.885714
##  Coordinating betweenness  0.400000
## # 6 more rows. summary() aggregates them; plot() draws them.
plot(temporal)

Tutoring has the highest temporal closeness (10), corresponding to the reciprocal of the specified traversal duration: it reaches every other category directly without waiting. Objecting has the lowest temporal closeness (5.5). Temporal betweenness is concentrated in Socialising, Arguing, and Resourcing, whereas Approving, Drafting, and Objecting are not intermediate vertices on any earliest-arrival path between other categories.

Paths

paths() identifies earliest-arrival paths from the category specified by from, minimising hop count among paths with the same earliest arrival. The following call uses the same traversal duration as the temporal centrality analysis. In the result, arrival_time records the earliest arrival at each destination, n_hops records the number of traversed ties, and n_paths records the number of optimal paths. When the optimal session is unique, path_session identifies the course containing the ties that achieve that arrival time. The plot displays the resulting paths as a tree.

from_inquiring <- paths(dn, from = "Inquiring", traversal_time = 0.1)
from_inquiring
## # Time-respecting paths from 'Inquiring', from t = 0
## # reaches 8 of 8 other vertices | time in days
## # routes are endpoint-specific session-integral optima, not one predecessor tree
## # traversal 0.1 days per hop
##          node reachable arrival_time attained   latency n_hops n_paths
##     Approving      TRUE    0.1077083     TRUE 0.1077083      1       1
##       Arguing      TRUE    0.1000000     TRUE 0.1000000      1       5
##  Coordinating      TRUE    0.1052199     TRUE 0.1052199      1       1
##      Drafting      TRUE    0.1000000     TRUE 0.1000000      1       2
##     Inquiring      TRUE    0.0000000     TRUE 0.0000000      0       1
##     Objecting      TRUE    0.1891667     TRUE 0.1891667      1       1
##    Resourcing      TRUE    0.1000000     TRUE 0.1000000      1       9
##   Socialising      TRUE    0.1000000     TRUE 0.1000000      1       2
##      Tutoring      TRUE    0.1530208     TRUE 0.1530208      1       1
##  path_session n_best_sessions
##             C               1
##          <NA>               4
##             D               1
##          <NA>               2
##          <NA>               0
##             C               1
##          <NA>               5
##          <NA>               2
##             A               1
plot(from_inquiring)

From Inquiring, every category is reachable in one hop. Arrival at Arguing, Drafting, Resourcing, and Socialising occurs at day 0.1, requiring only the specified traversal duration. Objecting is reached last, at day 0.189. Nine equally early paths reach Resourcing.

Backward path analysis identifies the latest departure times that permit arrival at a focal category by a specified deadline. Setting direction = "backward" makes from the focal destination, while start and end delimit the observation period. In this result, arrival_time contains the supremum of feasible departure times, and latency is the difference between the deadline and that value. An excluded interval endpoint may yield a supremum that cannot itself be attained; this is indicated by attained = FALSE.

into_approving <- paths(dn, from = "Approving", direction = "backward",
                        start = 0, end = 4, traversal_time = 0.1)
into_approving
## # Time-respecting paths into 'Approving', from t = 4
## # reaches 8 of 8 other vertices | time in days
## # routes are endpoint-specific session-integral optima, not one predecessor tree
## # traversal 0.1 days per hop
##          node reachable arrival_time attained   latency n_hops n_paths
##     Approving      TRUE     4.000000     TRUE 0.0000000      0       1
##       Arguing      TRUE     3.900000     TRUE 0.1000000      1       1
##  Coordinating      TRUE     3.409097     TRUE 0.5909028      2       1
##      Drafting      TRUE     3.030394     TRUE 0.9696065      2       3
##     Inquiring      TRUE     3.800000     TRUE 0.2000000      2       2
##     Objecting      TRUE     2.828785     TRUE 1.1712153      2       2
##    Resourcing      TRUE     3.900000     TRUE 0.1000000      1       1
##   Socialising      TRUE     3.800000     TRUE 0.2000000      2       1
##      Tutoring      TRUE     3.800000     TRUE 0.2000000      2       2
##  path_session n_best_sessions
##          <NA>               0
##             A               1
##             B               1
##             A               1
##             A               1
##             B               1
##             A               1
##             A               1
##             A               1
plot(into_approving)

Arguing and Resourcing can depart at day 3.9 and reach Approving by day 4. In contrast, the latest feasible departure from Objecting is day 2.83, corresponding to a latency of 1.17 days. This difference reflects the availability of ties towards the end of the observation period.

pathways() ranks routes across source vertices, with top specifying the number returned. Counts represent optimal paths grouped by vertex sequence, rather than observed frequencies of message sequences. The plot represents each category along a route at its arrival time. This call uses the default zero traversal duration.

top_routes <- pathways(dn, top = 12)
top_routes
## # Time-respecting pathways (69 distinct routes, showing 12)
## # 208 optimal routes counted, pooled over 9 source vertices
##         from                                            route     endpoint
##  Socialising           Socialising -> Resourcing -> Inquiring    Inquiring
##    Inquiring             Inquiring -> Resourcing -> Approving    Approving
##   Resourcing                           Resourcing -> Drafting     Drafting
##  Socialising           Socialising -> Resourcing -> Approving    Approving
##   Resourcing                            Resourcing -> Arguing      Arguing
##   Resourcing                          Resourcing -> Inquiring    Inquiring
##     Tutoring                           Tutoring -> Resourcing   Resourcing
##  Socialising              Socialising -> Arguing -> Inquiring    Inquiring
##      Arguing Arguing -> Resourcing -> Socialising -> Tutoring     Tutoring
##   Resourcing                          Resourcing -> Approving    Approving
##    Approving          Approving -> Resourcing -> Coordinating Coordinating
##      Arguing            Arguing -> Resourcing -> Coordinating Coordinating
##  count      share n_hops arrival_time
##     14 0.06730769      2            0
##     10 0.04807692      2            0
##      9 0.04326923      1            0
##      9 0.04326923      2            0
##      8 0.03846154      1            0
##      8 0.03846154      1            0
##      6 0.02884615      1            0
##      6 0.02884615      2            0
##      6 0.02884615      3            0
##      5 0.02403846      1            0
##      5 0.02403846      2            0
##      5 0.02403846      2            0
plot(top_routes)

Timing

similarity() compares network structure across temporal intervals. Setting both step and window to 0.5 produces non-overlapping half-day intervals. The default Jaccard coefficient measures similarity between their tie sets, and the plot displays the pairwise comparisons as a heatmap.

bin_similarity <- similarity(dn, step = 0.5, window = 0.5)
plot(bin_similarity)

pshifts() classifies consecutive directed interactions using Gibson’s (2003) thirteen participation shifts. These distinguish whether an interaction from A to B is followed by an interaction initiated by B, by A again, or by another vertex X. Here, vertices represent contribution categories, so the classifications describe changes in category-level relational endpoints.

shifts <- pshifts(dn)
shifts
## # Participation shifts (Gibson 2003, 13 types)
## # 3423 classified turn transitions across 4 families
##  shift          family count
##  AB-BA  turn_receiving   391
##  AB-B0  turn_receiving    51
##  AB-BY  turn_receiving   101
##  A0-X0   turn_claiming   592
##  A0-XA   turn_claiming   105
##  A0-XY   turn_claiming   559
##  AB-X0   turn_usurping   426
##  AB-XA   turn_usurping   135
##  AB-XB   turn_usurping   642
##  AB-XY   turn_usurping   170
##  A0-AY turn_continuing    94
##  AB-A0 turn_continuing    52
##  AB-AY turn_continuing   105
plot(shifts)

Of the 3,423 classified transitions, the largest family is turn usurping, in which a third category becomes the source of the next interaction. The most frequent individual shift is AB-XB, with 642 occurrences: a different source category addresses the same target category.

burstiness() characterises the temporal distribution of activity using inter-event intervals. The burstiness coefficient approaches 1 for highly heterogeneous intervals, is 0 for an exponential inter-event distribution, and is −1 for equal intervals (Goh and Barabási, 2008).

bursts <- burstiness(dn)
summary(bursts)
##            node    measure n          mean sd           min           max
## 1     Approving burstiness 1  6.582692e-01 NA  6.582692e-01  6.582692e-01
## 2     Approving     events 1  5.106000e+03 NA  5.106000e+03  5.106000e+03
## 3     Approving     memory 1  8.283281e-02 NA  8.283281e-02  8.283281e-02
## 4       Arguing burstiness 1  6.671291e-01 NA  6.671291e-01  6.671291e-01
## 5       Arguing     events 1  5.504000e+03 NA  5.504000e+03  5.504000e+03
## 6       Arguing     memory 1  1.645164e-01 NA  1.645164e-01  1.645164e-01
## 7  Coordinating burstiness 1  7.184227e-01 NA  7.184227e-01  7.184227e-01
## 8  Coordinating     events 1  1.654000e+03 NA  1.654000e+03  1.654000e+03
## 9  Coordinating     memory 1  1.756259e-02 NA  1.756259e-02  1.756259e-02
## 10     Drafting burstiness 1  6.355224e-01 NA  6.355224e-01  6.355224e-01
## 11     Drafting     events 1  6.960000e+02 NA  6.960000e+02  6.960000e+02
## 12     Drafting     memory 1 -8.726877e-03 NA -8.726877e-03 -8.726877e-03
## 13    Inquiring burstiness 1  6.284927e-01 NA  6.284927e-01  6.284927e-01
## 14    Inquiring     events 1  1.763000e+03 NA  1.763000e+03  1.763000e+03
## 15    Inquiring     memory 1  8.068968e-02 NA  8.068968e-02  8.068968e-02
## 16    Objecting burstiness 1  4.260920e-01 NA  4.260920e-01  4.260920e-01
## 17    Objecting     events 1  1.080000e+02 NA  1.080000e+02  1.080000e+02
## 18    Objecting     memory 1  8.584296e-02 NA  8.584296e-02  8.584296e-02
## 19   Resourcing burstiness 1  7.367974e-01 NA  7.367974e-01  7.367974e-01
## 20   Resourcing     events 1  1.495000e+04 NA  1.495000e+04  1.495000e+04
## 21   Resourcing     memory 1  1.111763e-01 NA  1.111763e-01  1.111763e-01
## 22  Socialising burstiness 1  6.260914e-01 NA  6.260914e-01  6.260914e-01
## 23  Socialising     events 1  4.829000e+03 NA  4.829000e+03  4.829000e+03
## 24  Socialising     memory 1  5.303993e-02 NA  5.303993e-02  5.303993e-02
## 25     Tutoring burstiness 1  5.623661e-01 NA  5.623661e-01  5.623661e-01
## 26     Tutoring     events 1  1.956000e+03 NA  1.956000e+03  1.956000e+03
## 27     Tutoring     memory 1 -1.894700e-02 NA -1.894700e-02 -1.894700e-02
plot(bursts)

All categories have positive burstiness coefficients, ranging from 0.43 for Objecting to 0.74 for Resourcing. The memory coefficients are close to zero, indicating little correlation between successive inter-event intervals.

One course group

The group variable is retained as a spell attribute during network construction. A course group can therefore be selected by filtering the existing temporal network. induce_subgraph() with ties = group == "A_01" retains spells associated with group A_01.

one_group <- induce_subgraph(dn, ties = group == "A_01")
summary(one_group)
##                 property        value
## 1                 format     threaded
## 2               directed          yes
## 3               vertices            9
## 4            edge spells         2425
## 5         distinct pairs           53
## 6              time unit         days
## 7          observed from            0
## 8            observed to            4
## 9                   span            4
## 10             bin width            1
## 11             time bins            4
## 12 mean snapshot density        0.559
## 13      temporal density not computed
## 14              sessions            1
## 15     vertex attributes         none

The resulting subnetwork contains 2,425 spells across 53 of the 81 possible ordered pairs, with a mean snapshot density of 0.559 and one session.

Setting type = "events" displays reply events with contribution categories on the vertical axis. Specifying time = "clock" positions events at their recorded times, while blend = TRUE interpolates link colours between their source and target categories.

plot(one_group, type = "events")

plot(one_group, type = "events", time = "clock", blend = TRUE)

Graph-level metrics can be computed for the subnetwork using the same hourly intervals as the full network.

group_structure <- metrics(one_group,
                           measure = c("density", "edges", "reciprocity",
                                       "connectedness"),
                           step = 1 / 24, window = 1 / 24)
plot(group_structure, type = "ridge")

Interpretation

In this synthetic example, Resourcing is incident to the largest number of reply links and is frequently reachable through multiple equally early paths. Together with Socialising and Arguing, it accounts for much of the temporal betweenness. Objecting has comparatively few incident replies, the lowest temporal closeness, and no temporal betweenness.

Under the specified construction, relational spells remain active until discussion termination, and every category is temporally reachable from every other category. Differences in arrival times and intermediate categories provide additional distinctions that reachability alone does not capture. These results illustrate the analytical methods applied to the synthetic dataset and do not constitute empirical findings about the original study.

Limitations

Maintaining each relational spell until discussion termination is a modelling assumption that increases tie availability and contributes to high network density and complete reachability. Shorter spell durations could produce sparser networks, longer waiting times, or unreachable destinations. The four-day observation period also truncates longer discussions for measurement, without modifying their raw spell endpoints.

Vertices represent contribution categories rather than individual participants; consequently, the analysis characterises relationships among categories and does not identify interpersonal interaction patterns. Replies assigned multiple categories may contribute multiple ties, affecting spell counts and accumulated durations. Temporal path results additionally depend on the specified traversal duration.

The dataset is synthetic and based on the original study data. Its results serve to demonstrate package functionality; correspondence with empirical patterns in the original data requires separate assessment.

References

Freeman, L. C. (1979). Centrality in social networks: conceptual clarification. Social Networks, 1(3), 215–239.

Freeman, L. C., Borgatti, S. P., & White, D. R. (1991). Centrality in valued graphs: a measure of betweenness based on network flow. Social Networks, 13(2), 141–154.

Gibson, D. R. (2003). Participation shifts: order and differentiation in group conversation. Social Forces, 81(4), 1335–1380.

Goh, K.-I., & Barabási, A.-L. (2008). Burstiness and memory in complex systems. EPL (Europhysics Letters), 81(4), 48002.

Holland, P. W., & Leinhardt, S. (1976). Local structure in social networks. Sociological Methodology, 7, 1–45.

Kempe, D., Kleinberg, J., & Kumar, A. (2002). Connectivity and inference problems for temporal networks. Journal of Computer and System Sciences, 64(4), 820–842.

Krackhardt, D. (1994). Graph theoretical dimensions of informal organizations. In K. M. Carley & M. J. Prietula (Eds.), Computational organization theory (pp. 89–111). Lawrence Erlbaum.

Kundu, S., Murthy, C. A., & Pal, S. K. (2011). A new centrality measure for influence maximization in social networks. In Pattern Recognition and Machine Intelligence (Lecture Notes in Computer Science 6744, pp. 242–247). Springer.

Morris, M., Handcock, M. S., & Hunter, D. R. (2008). Specification of exponential-family random graph models: terms and computational aspects. Journal of Statistical Software, 24(4).

Pan, R. K., & Saramäki, J. (2011). Path lengths, correlations, and centrality in temporal networks. Physical Review E, 84(1), 016105.

Saqr, M., López-Pernas, S., & Törmänen, T. (2026). A temporal network approach to reveal the longitudinal dynamics of CSCL group regulation and productive collaboration. International Journal of Computer-Supported Collaborative Learning, 21, 237–270. https://doi.org/10.1007/s11412-025-09464-5

Saqr, M., & Nouri, J. (2020). High resolution temporal network analysis to understand and improve collaborative learning. In Proceedings of the Tenth International Conference on Learning Analytics & Knowledge (pp. 314–319). ACM.

Wasserman, S., & Faust, K. (1994). Social network analysis: methods and applications. Cambridge University Press.