Introduction to socialdrift

Overview

socialdrift provides a complete workflow for turning raw social interaction event logs into longitudinal network diagnostics. It is designed for computational social scientists, digital community managers, and platform analysts who want to track how a network changes — not just what it looks like at a single moment.

The package is built around four signature indices:

Index Symbol What it measures
Network Drift Index NDI Structural change between periods
Community Fragmentation Index CFI Degree of silo formation
Visibility Concentration Index VCI Concentration of incoming attention
Role Mobility Index RMI Frequency of role transitions

1. Prepare event data

The base input is a tidy data frame with one row per interaction event.

library(socialdrift)
data(sim_social_events)
head(sim_social_events)
#>     actor_id target_id           timestamp event_type weight actor_group
#> 485      u31       u35 2025-01-01 00:51:46      reply      1           A
#> 353      u30       u17 2025-01-01 01:54:13      reply      1           C
#> 180       u6       u44 2025-01-01 09:05:01       like      1           C
#> 288      u24        u5 2025-01-01 10:29:21      reply      1           C
#> 360      u41        u4 2025-01-01 11:52:32    mention      1           B
#> 287      u54        u7 2025-01-01 12:48:18      reply      1           C
#>     target_group
#> 485            B
#> 353            B
#> 180            B
#> 288            B
#> 360            A
#> 287            A

Convert to a standardized social_events object:

ev <- as_social_events(
  sim_social_events,
  actor_group  = "actor_group",
  target_group = "target_group"
)

2. Build a monthly graph series

gs <- build_graph_series(ev, window = "month")
gs   # print method shows summary
#> <social_graph_series>
#>   Periods   : 6 
#>   Window    : 2025-01-01 to 2025-06-01 
#>   Directed  : TRUE 
#>   Avg nodes : 56 
#>   Avg edges : 97

3. Structural metrics

summarize_network_series(gs)
#> # A tibble: 6 × 8
#>   period  n_nodes n_edges density reciprocity clustering degree_gini degree_mean
#>   <chr>     <dbl>   <dbl>   <dbl>       <dbl>      <dbl>       <dbl>       <dbl>
#> 1 2025-0…      56     105  0.0341      0.0381     0.0718       0.277        3.75
#> 2 2025-0…      56      95  0.0308      0          0.0456       0.305        3.39
#> 3 2025-0…      54      85  0.0297      0.0235     0.0586       0.300        3.15
#> 4 2025-0…      56      98  0.0318      0          0.0648       0.267        3.5 
#> 5 2025-0…      55      98  0.0330      0.0408     0.0181       0.291        3.56
#> 6 2025-0…      56     103  0.0334      0.0388     0.127        0.243        3.68

Plot density over time:

dens_tbl <- network_density_ts(gs)
plot_network_metrics(dens_tbl, metric = "density",
                     title = "Monthly network density")

Plot all four metrics together:

all_metrics <- summarize_network_series(gs)
plot_network_metrics(
  all_metrics,
  metric = c("density", "reciprocity", "clustering", "degree_gini"),
  title  = "Network structure over time"
)


4. Community dynamics

comm_tbl <- detect_communities_ts(gs)
comm_tbl
#> # A tibble: 6 × 5
#>   period     n_communities modularity largest_community_size singleton_count
#>   <chr>              <int>      <dbl>                  <int>           <int>
#> 1 2025-01-01             8      0.435                     12               0
#> 2 2025-02-01             8      0.480                     13               0
#> 3 2025-03-01             7      0.499                     11               0
#> 4 2025-04-01             6      0.484                     12               0
#> 5 2025-05-01             6      0.463                     14               0
#> 6 2025-06-01             6      0.531                     13               0

Community drift (period-over-period changes):

drift_comm <- community_drift(comm_tbl)
drift_comm
#> # A tibble: 6 × 8
#>   period     n_communities modularity largest_community_size singleton_count
#>   <chr>              <int>      <dbl>                  <int>           <int>
#> 1 2025-01-01             8      0.435                     12               0
#> 2 2025-02-01             8      0.480                     13               0
#> 3 2025-03-01             7      0.499                     11               0
#> 4 2025-04-01             6      0.484                     12               0
#> 5 2025-05-01             6      0.463                     14               0
#> 6 2025-06-01             6      0.531                     13               0
#> # ℹ 3 more variables: delta_n_communities <int>, delta_modularity <dbl>,
#> #   delta_largest_community <int>

Community Fragmentation Index:

community_fragmentation_index(comm_tbl)
#> # A tibble: 6 × 2
#>   period       cfi
#>   <chr>      <dbl>
#> 1 2025-01-01 0.3  
#> 2 2025-02-01 0.535
#> 3 2025-03-01 0.483
#> 4 2025-04-01 0.255
#> 5 2025-05-01 0.143
#> 6 2025-06-01 0.5

5. Network Drift Index (NDI)

The NDI combines edge turnover, degree shift, modularity change, and centralization change into a single composite score.

ndi_tbl <- network_drift(gs)
ndi_tbl
#> # A tibble: 6 × 6
#>   period     edge_turnover degree_shift modularity_change centralization_change
#>   <chr>              <dbl>        <dbl>             <dbl>                 <dbl>
#> 1 2025-01-01        NA           NA              NA                    NA      
#> 2 2025-02-01         0.990        0.196           0.00823               0.00673
#> 3 2025-03-01         0.971        0.135           0.0262                0.0295 
#> 4 2025-04-01         0.983        0.144           0.0264                0.0102 
#> 5 2025-05-01         0.974        0.156           0.0277                0.0581 
#> 6 2025-06-01         0.990        0.209           0.0654                0.0803 
#> # ℹ 1 more variable: ndi <dbl>
plot_network_drift(ndi_tbl)


6. User role trajectories

Each node is classified into one of six structural roles: isolated, peripheral, broadcaster, popular, core, bridge.

roles_tbl <- role_trajectories(gs)
head(roles_tbl, 12)
#> # A tibble: 12 × 6
#>    period     node  indegree outdegree betweenness role      
#>    <chr>      <chr>    <dbl>     <dbl>       <dbl> <chr>     
#>  1 2025-01-01 u1           6         4     0.128   bridge    
#>  2 2025-01-01 u10          1         4     0.0411  bridge    
#>  3 2025-01-01 u11          3         1     0.0461  bridge    
#>  4 2025-01-01 u13          3         1     0.00780 popular   
#>  5 2025-01-01 u14          5         1     0.00808 popular   
#>  6 2025-01-01 u15          4         3     0.0644  bridge    
#>  7 2025-01-01 u16          2         2     0.00763 peripheral
#>  8 2025-01-01 u17          3         1     0.0262  popular   
#>  9 2025-01-01 u18          1         2     0.0112  peripheral
#> 10 2025-01-01 u20          5         4     0.117   bridge    
#> 11 2025-01-01 u21          2         1     0.00920 peripheral
#> 12 2025-01-01 u22          0         1     0       peripheral
plot_role_trajectories(roles_tbl, type = "stacked")

Role Mobility Index:

role_mobility_index(roles_tbl)
#> # A tibble: 1 × 3
#>   rmi_global n_users_tracked mean_transitions
#>        <dbl>           <int>            <dbl>
#> 1      0.659              60             3.07

7. Visibility & inequality

visibility_concentration_index(gs)
#> # A tibble: 6 × 2
#>   period       vci
#>   <chr>      <dbl>
#> 1 2025-01-01 0.396
#> 2 2025-02-01 0.459
#> 3 2025-03-01 0.408
#> 4 2025-04-01 0.434
#> 5 2025-05-01 0.461
#> 6 2025-06-01 0.355

Creator concentration (Gini + top-10% share):

creator_concentration(gs, p = 0.10)
#> # A tibble: 6 × 3
#>   period     indegree_gini top_p_share
#>   <chr>              <dbl>       <dbl>
#> 1 2025-01-01         0.463       0.295
#> 2 2025-02-01         0.540       0.337
#> 3 2025-03-01         0.460       0.329
#> 4 2025-04-01         0.519       0.306
#> 5 2025-05-01         0.529       0.357
#> 6 2025-06-01         0.410       0.272

8. Group disparity audit

If your event data includes group membership, you can audit structural disparities across groups over time.

audit_group_disparities(ev, gs, group_var = "actor_group", window = "month")
#> # A tibble: 18 × 7
#>    period     group n_users mean_indegree mean_outdegree mean_betweenness
#>    <chr>      <chr>   <int>         <dbl>          <dbl>            <dbl>
#>  1 2025-01-01 A          15         1.53            2.53           0.0307
#>  2 2025-01-01 B          17         2.29            1.82           0.0374
#>  3 2025-01-01 C          18         1.56            2              0.0304
#>  4 2025-02-01 A          18         1.83            1.94           0.0382
#>  5 2025-02-01 B          16         1               1.62           0.0178
#>  6 2025-02-01 C          14         1.5             2.43           0.0447
#>  7 2025-03-01 A          13         1               2.23           0.0302
#>  8 2025-03-01 B          15         2               1.67           0.0482
#>  9 2025-03-01 C          15         1.33            2.07           0.0297
#> 10 2025-04-01 A          15         1.67            2.27           0.0148
#> 11 2025-04-01 B          17         1.35            2              0.0112
#> 12 2025-04-01 C          15         1.27            2              0.0169
#> 13 2025-05-01 A          17         2.06            2.47           0.0643
#> 14 2025-05-01 B          17         1.82            1.53           0.0318
#> 15 2025-05-01 C          17         0.941           1.76           0.0318
#> 16 2025-06-01 A          19         1.47            2.05           0.0475
#> 17 2025-06-01 B          16         1.56            1.75           0.0430
#> 18 2025-06-01 C          17         1.94            2.12           0.0725
#> # ℹ 1 more variable: isolation_rate <dbl>

Engagement gap between groups:

engagement_gap(ev, gs, group_var = "actor_group", window = "month")
#> # A tibble: 18 × 5
#>    period     engagement_ratio group mean_indegree mean_outdegree
#>    <chr>                 <dbl> <chr>         <dbl>          <dbl>
#>  1 2025-01-01            0.605 A             1.53            2.53
#>  2 2025-01-01            1.26  B             2.29            1.82
#>  3 2025-01-01            0.778 C             1.56            2   
#>  4 2025-02-01            0.943 A             1.83            1.94
#>  5 2025-02-01            0.615 B             1               1.62
#>  6 2025-02-01            0.618 C             1.5             2.43
#>  7 2025-03-01            0.448 A             1               2.23
#>  8 2025-03-01            1.20  B             2               1.67
#>  9 2025-03-01            0.645 C             1.33            2.07
#> 10 2025-04-01            0.735 A             1.67            2.27
#> 11 2025-04-01            0.676 B             1.35            2   
#> 12 2025-04-01            0.633 C             1.27            2   
#> 13 2025-05-01            0.833 A             2.06            2.47
#> 14 2025-05-01            1.19  B             1.82            1.53
#> 15 2025-05-01            0.533 C             0.941           1.76
#> 16 2025-06-01            0.718 A             1.47            2.05
#> 17 2025-06-01            0.893 B             1.56            1.75
#> 18 2025-06-01            0.917 C             1.94            2.12

Summary

A typical full audit in fewer than 15 lines:

library(socialdrift)

ev <- as_social_events(sim_social_events,
                       actor_group  = "actor_group",
                       target_group = "target_group")

gs       <- build_graph_series(ev, window = "month")
summary  <- summarize_network_series(gs)
ndi      <- network_drift(gs)
comm     <- detect_communities_ts(gs)
cfi      <- community_fragmentation_index(comm)
vci      <- visibility_concentration_index(gs)
roles    <- role_trajectories(gs)
rmi      <- role_mobility_index(roles)
gaps     <- audit_group_disparities(ev, gs)

plot_network_drift(ndi)
plot_role_trajectories(roles)