Skip to contents

This vignette shows how netify handles ego networks—the network surrounding a specific actor and their immediate connections.

what are ego networks?

An ego network focuses on one actor (the “ego”) and includes:

  • The ego: Your focal actor
  • The alters: Actors directly connected to the ego
  • Alter-alter ties: Connections between the ego’s neighbors

Think of it as zooming in on one node and asking: “Who are their connections, and how are those connections related to each other?”

examples

Pakistan’s diplomatic neighborhood includes not just major powers (US, China) but regional rivals (India) and neighbors (Afghanistan). The connections between these alters matter—does Pakistan broker between disconnected partners or navigate a densely connected region?

A senator’s ego network reveals their coalition partners, but also whether those partners work together or represent distinct constituencies the senator must balance.

An activist organization’s ego network shows both allies and the broader movement structure—are they bridging disconnected groups or embedded in a tight cluster?

why ego networks matter

Ego networks let you apply network thinking without needing complete network data. You can study:

  • How actors manage competing relationships
  • Whether similar actors cluster together (homophily)
  • How an actor’s position changes across relationships
  • Whether an actor brokers between otherwise disconnected groups

getting started: extract an ego network

We’ll use data from the Integrated Crisis Early Warning System (ICEWS) to demonstrate. ICEWS records events from each sender to each target, so we sum both directions to get one undirected tie per country pair and year:

data(icews)

# one row per country pair and year, summing both directions
icews_undirected <- icews |>
    group_by(actor_a = pmin(i, j), actor_b = pmax(i, j), year) |>
    summarize(
        across(c(verbCoop, matlCoop, verbConf), sum),
        .groups = "drop"
    )

# one row per country and year
nodal_data <- icews |>
    distinct(i, year, i_polity2, i_log_gdp, i_region)

# verbal cooperation between countries
netlet <- netify(
    icews_undirected,
    actor1 = "actor_a", actor2 = "actor_b",
    time = "year",
    weight = "verbCoop",
    dyad_vars = c("matlCoop", "verbConf")
)

netlet <- add_node_vars(netlet, nodal_data, actor = "i", time = "year")

extract your first ego network

The ego_netify() function makes extraction straightforward:

pakistan_ego_net <- ego_netify(
    netlet,
    ego = "Pakistan"
    )

print(pakistan_ego_net)
head(summary(pakistan_ego_net)[, c("net", "num_actors", "density", "num_edges")])
#>    net num_actors   density num_edges
#> 1 2002         31 0.8580645       399
#> 2 2003         29 0.9211823       374
#> 3 2004         49 0.7891156       928
#> 4 2005         45 0.8919192       883
#> 5 2006         40 0.8705128       679
#> 6 2007         39 0.9163293       679

key features

1. network statistics

ngbd_summ <- summary(pakistan_ego_net)
head(ngbd_summ)
#>    net    layer num_actors   density num_edges prop_edges_missing
#> 1 2002 Pakistan         31 0.8580645       399                  0
#> 2 2003 Pakistan         29 0.9211823       374                  0
#> 3 2004 Pakistan         49 0.7891156       928                  0
#> 4 2005 Pakistan         45 0.8919192       883                  0
#> 5 2006 Pakistan         40 0.8705128       679                  0
#> 6 2007 Pakistan         39 0.9163293       679                  0
#>   mean_edge_weight sd_edge_weight median_edge_weight min_edge_weight
#> 1         447.7093      1130.1598               97.0               1
#> 2         577.3797      1389.1304              100.5               1
#> 3         252.4741       802.3923               45.0               1
#> 4         292.1699       933.7845               48.0               1
#> 5         334.5773      1064.4979               54.0               1
#> 6         397.9087      1116.5289               74.0               1
#>   max_edge_weight competition sd_of_actor_means transitivity
#> 1           11763  0.08302184          489.8849    0.8868895
#> 2            9768  0.08263100          639.6115    0.9336033
#> 3            9804  0.05972350          279.3920    0.8390159
#> 4           11759  0.06377164          360.3542    0.9118179
#> 5           14169  0.07270890          407.4732    0.9020027
#> 6           13823  0.06602829          463.5857    0.9310701

Pakistan’s network shows high density (0.79-0.97)—most countries that interact with Pakistan also interact with each other. The network size fluctuates between 26-49 actors across years, but that high density persists. This isn’t a hub-and-spoke pattern where Pakistan connects otherwise isolated actors; it’s a dense web where multilateral dynamics likely matter.

plot_graph_stats(ngbd_summ) +
    scale_x_discrete(
        breaks = seq(2002, 2014, by = 4)
    ) +
    theme(
        axis.text.x=element_text(angle=45, hjust=1)
    )

Notice the consistently high transitivity (~0.84-0.97). Pakistan’s partners tend to cooperate with each other, creating a tightly clustered neighborhood rather than structural holes Pakistan could exploit.

2. actor-level analysis

ngbd_actor_summ <- summary_actor(pakistan_ego_net)
head(ngbd_actor_summ)
#>         actor    layer time degree prop_ties strength_sum strength_avg
#> 1    Pakistan Pakistan 2002     30 1.0000000        20757     691.9000
#> 2 Afghanistan Pakistan 2002     29 0.9666667        16772     578.3448
#> 3   Australia Pakistan 2002     27 0.9000000         5427     201.0000
#> 4  Azerbaijan Pakistan 2002     23 0.7666667         4863     211.4348
#> 5  Bangladesh Pakistan 2002     24 0.8000000         3029     126.2083
#> 6      Canada Pakistan 2002     28 0.9333333         3469     123.8929
#>   strength_std strength_median network_share closeness betweenness eigen_vector
#> 1    1286.4883           199.5   0.058098592  787.4105  0.00000000   0.38000799
#> 2     834.6176           208.0   0.046944625  732.1774  0.06666667   0.28510565
#> 3     409.1559            50.0   0.015190107  606.4470  0.00000000   0.11900674
#> 4     455.2181            26.0   0.013611478  596.3886  0.00000000   0.08960396
#> 5     208.1527            27.5   0.008478134  394.8759  0.00000000   0.04610106
#> 6     232.8033            40.5   0.009709689  495.3738  0.00000000   0.07397748

Beyond degree (number of connections), look at eigenvector centrality—it captures importance based on connections to other important actors. The US has the highest eigenvector centrality in Pakistan’s neighborhood every year, reflecting not just bilateral ties with Pakistan but its connections to other key players in the region.

plot_actor_stats(ngbd_actor_summ,
    across_actor=FALSE,
    specific_actors=c('United States', 'China')
    ) +
    scale_x_discrete(
        breaks = seq(2002, 2014, by = 4)
    ) +
    theme(
        axis.text.x=element_text(angle=45, hjust=1)
    )

Both powers are tied to every other actor in Pakistan’s neighborhood in every year, so the differences lie in intensity. The US consistently leads, with roughly twice China’s cooperation volume (strength), while China’s eigenvector centrality rose through the mid-2000s and then fluctuated at lower levels.

3. visualization

By default, plot() highlights the ego:

plot(pakistan_ego_net)

4. advanced visualization

For clearer visualization of alter-alter relationships, remove the ego’s edges—we already know everyone connects to Pakistan:

# attach actor stats so we can map them to node aesthetics
pakistan_ego_net <- add_node_vars(
    pakistan_ego_net,
    summary_actor(pakistan_ego_net),
    "actor", "time"
)

# strip ties incident to ego so the alter-alter structure stands on its own
pakistan_no_ego_edges <- remove_ego_edges(pakistan_ego_net)
plot(pakistan_no_ego_edges,
    layout = "hierarchical",  
    node_size_by = "i_log_gdp",
    node_size_label = "Log(GDP)",
    highlight = c("Pakistan", "United States", "China"),
    highlight_color = c(
        "Pakistan" = '#01411cff', 
        "United States" = "#0A3161",
        "China" = "#EE1C25",
        "Other" = 'grey40'),
    edge_linewidth = 0.05,
    mutate_weight = log1p,
    edge_alpha_label = 'Log(Verb.\n Coop.)',
    time_filter = as.character(seq(2002, 2014, 4))
    ) +
    labs(
        title = "Pakistan's Diplomatic Neighborhood",
        subtitle = "Node size = GDP; Edges show cooperation between Pakistan's partners") +
    theme(legend.position = 'right')

The visualization reveals how Pakistan’s partners form a dense web of relationships among themselves, with major powers like the US and China occupying central positions.

comparing ego networks

5. compare across actors

One strength of ego network analysis is systematic comparison:

powers <- c("United States", "China", "Russian Federation", "India", "Pakistan")
ego_networks <- lapply(powers, function(country) {
    ego_netify(netlet, ego = country)
})
names(ego_networks) <- powers

summaries <- lapply(ego_networks, summary)
comparison_df <- bind_rows(summaries, .id = "country")

ggplot(comparison_df,
    aes(x = net, y = density, color = country, group = country)) +
    geom_line(linewidth = 1.2) +
    labs(title = "Ego Network Density Over Time",
         subtitle = "How interconnected are different countries' diplomatic neighborhoods?",
         x = "Year", y = "Density") +
    theme_bw() +
    theme(
        panel.border = element_blank(),
        axis.ticks = element_blank(),
        legend.position = "top"
    )

Pakistan and India maintain the highest density—their partners are highly interconnected. The US and China show the lowest density, suggesting partners drawn from many regions that interact less with each other. Russia sits in between and stays fairly stable (roughly 0.70-0.78) across the period.

6. test for homophily

Do birds of a feather flock together? Let’s test if similar regime types cluster:

pakistan_homophily <- homophily(
    pakistan_ego_net,
    attribute = "i_polity2",
    method = "correlation"
)

plot_homophily(pakistan_homophily, type = "temporal") +
    labs(title = "Do Democracies Cluster in Pakistan's Network?",
         subtitle = "Testing regime type homophily over time")

The results show weak and fluctuating homophily: regime type is not consistently associated with cooperation patterns in Pakistan’s neighborhood. Geopolitics appears more central than ideological alignment in this example.

7. control neighborhood boundaries

Sometimes you want to focus on only the strongest relationships:

# default includes all connections
pakistan_all <- ego_netify(netlet, ego = "Pakistan")

# only strong connections (threshold = 50 cooperation events)
pakistan_strong <- ego_netify(netlet, ego = "Pakistan", threshold = 50)

all_size <- mean(summary(pakistan_all)$num_actors)
strong_size <- mean(summary(pakistan_strong)$num_actors)

tibble(
  Network = c("All connections", "Strong only (>50)"),
  `Average Size` = round(c(all_size, strong_size), 1)
) |>
  knitr::kable()
Network Average Size
All connections 34.5
Strong only (>50) 29.6

8. different relationship types

Networks of cooperation and conflict often follow different logics:

conflict_net <- netify(
    icews,
    actor1 = "i", actor2 = "j",
    time = "year",
    sum_dyads = TRUE,
    weight = "verbConf"
)

# cooperation vs conflict ego networks for pakistan
pak_coop_ego <- ego_netify(netlet, ego = "Pakistan")
pak_conf_ego <- ego_netify(conflict_net, ego = "Pakistan")

coop_stats <- summary(pak_coop_ego)
conf_stats <- summary(pak_conf_ego)

comparison <- tibble(
  Metric = c("Average actors", "Average density"),
  Cooperation = c(
    round(mean(coop_stats$num_actors), 1),
    round(mean(coop_stats$density), 3)
  ),
  Conflict = c(
    round(mean(conf_stats$num_actors), 1),
    round(mean(conf_stats$density), 3)
  )
)

knitr::kable(comparison)
Metric Cooperation Conflict
Average actors 34.500 22.000
Average density 0.909 0.756

Pakistan’s conflict network is smaller but still substantial. The lower average density in conflict (about 0.76 vs 0.91) suggests conflicts are more bilateral while cooperation tends to be multilateral.

9. track network evolution

How stable are relationships over time?

pakistan_comparison <- compare_networks(pakistan_ego_net, what = "edges")

# zoom in on one transition
changes_2010_2011 <- pakistan_comparison$edge_changes$`2010_vs_2011`

stability_ratio <- round(
  changes_2010_2011$maintained / 
  (changes_2010_2011$maintained + changes_2010_2011$removed), 3)

tibble(
  Transition = "2010 to 2011",
  Added = changes_2010_2011$added,
  Removed = changes_2010_2011$removed,
  Maintained = changes_2010_2011$maintained,
  `Stability Ratio` = stability_ratio
) |>
  knitr::kable()
Transition Added Removed Maintained Stability Ratio
2010 to 2011 148 247 253 0.506

A stability ratio of 0.506 indicates moderate turnover – about 49% of the 2010 relationships don’t persist into 2011.

10. compare network structures: rising vs established powers

How do the US and China structure their diplomatic neighborhoods differently?

us_ego <- ego_netify(netlet, ego = "United States")
china_ego <- ego_netify(netlet, ego = "China")

us_summary <- summary(us_ego)
china_summary <- summary(china_ego)

# average yearly statistics for each ego network
avg_props <- bind_rows(
    "US" = us_summary,
    "China" = china_summary,
    .id = "Network"
) |>
    group_by(Network) |>
    summarise(
        `Avg. Nodes` = round(mean(num_actors), 0),
        `Avg. Density` = round(mean(density), 3),
        `Avg. Transitivity` = round(mean(transitivity), 3),
        .groups = 'drop'
    )

knitr::kable(avg_props)
Network Avg. Nodes Avg. Density Avg. Transitivity
China 109 0.586 0.710
US 120 0.572 0.698

Averaged across years, the US maintains a larger network than China, but the two are structured similarly: density and transitivity are close, with China’s only slightly higher. The difference between the two powers lies more in the reach of their neighborhoods than in how interconnected their partners are.

Let’s look at the temporal patterns:

temporal_comparison <- bind_rows(
    us_summary |> mutate(network = "US"),
    china_summary |> mutate(network = "China")
)

temporal_comparison$year <- as.integer(temporal_comparison$net)

ggplot(temporal_comparison, aes(x = year, y = density, color = network)) +
    geom_line(linewidth = 1.2) +
    geom_point(size = 2) +
    scale_color_manual(values = c("US" = "#377eb8", "China" = "#e41a1c")) +
    labs(title = "Ego Network Density: US vs China",
         subtitle = "China's increasing density reflects growing multilateral engagement",
         x = "Year", y = "Density") +
    theme_bw() +
    theme(
        panel.border = element_blank(),
        axis.ticks = element_blank(),
        legend.position = "top"
    )

Now let’s examine how similar their cooperation patterns are:

comp_2012 <- compare_networks(
    list(
        "US_2012" = subset(us_ego, time = "2012"),
        "China_2012" = subset(china_ego, time = "2012")
    ),
    what = "edges",
    method = "all"
)

edge_stats <- comp_2012$summary
edge_changes <- comp_2012$edge_changes[[1]]

tibble(
  Metric = c("Edge correlation", "Jaccard similarity", 
             "Unique to US", "Unique to China", "Shared"),
  Value = c(
    round(edge_stats$correlation, 3),
    round(edge_stats$jaccard, 3),
    edge_changes$removed,
    edge_changes$added,
    edge_changes$maintained
  )
) |>
  knitr::kable()
Metric Value
Edge correlation 1
Jaccard similarity 1
Unique to US 1183
Unique to China 433
Shared 3111

Similarity measures are computed on the actors that appear in both ego networks. Because both ego networks are drawn from the same ICEWS data, ties among shared partners are identical, so the correlation and Jaccard similarity are 1. The difference lies in who each ego network includes: the US ego network holds 1,183 ties that China’s lacks, China’s holds 433 that the US network lacks, and 3,111 ties appear in both.

Finally, who’s in these networks?

# compare node composition in 2012
node_comp <- compare_networks(
    list(
        "US" = subset(us_ego, time = "2012"),
        "China" = subset(china_ego, time = "2012")
    ),
    what = "nodes"
)

knitr::kable(
    node_comp$summary,
    col.names = c(
        "Comparison", "US actors", "China actors", "Shared actors",
        "Jaccard similarity", "Unique to China", "Unique to US"
    ),
    digits = 2
)
Comparison US actors China actors Shared actors Jaccard similarity Unique to China Unique to US
US vs China 122 109 97 0.72 12 25

The two neighborhoods overlap heavily (Jaccard similarity of 0.72), reflecting shared major partners, but the US network includes 25 actors absent from China’s, while China’s includes only 12 absent from the US network, consistent with broader US global reach.

tl;dr

# extract ego network
ego_net <- ego_netify(netlet, ego = "Actor Name")

# basic analysis
summary(ego_net)                    # network-level stats
summary_actor(ego_net)              # actor-level stats
plot(ego_net)                       # visualize
plot(remove_ego_edges(ego_net))     # focus on alter-alter ties

# advanced analysis
homophily(ego_net, attribute = "democracy")     # test homophily
compare_networks(ego_net, what = "edges")       # temporal comparison
compare_networks(list(ego1, ego2), what = "edges")  # cross-sectional comparison

references

Boschee, E., Lautenschlager, J., O’Brien, S., Shellman, S., Starz, J., & Ward, M. (2015). ICEWS Coded Event Data. Harvard Dataverse. DOI 10.7910/DVN/28075.