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 |
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 AConvert to a standardized social_events object:
ev <- as_social_events(
sim_social_events,
actor_group = "actor_group",
target_group = "target_group"
)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 : 97summarize_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.68Plot 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"
)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 0Community 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.5The 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>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 peripheralRole 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.07visibility_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.355Creator 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.272If 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.12A 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)