Chapter 5 Ego-network structure
5.1 Overview
Ego-network structure refers to the distribution of ties among alters. As usual, we first illustrate analyses on one ego-network, then replicate them on many ego-networks at once.
This chapter covers the following topics:
- Calculating measures of ego-network structure.
- R lists and how they can be used to represent many ego-networks.
- Split-apply-combine on lists with
purrr
to analyze the structure of many ego-networks at once.
5.2 Measures of ego-network structure
- Ego-network structure can be described using different measures, either at the alter level (e.g., alter centrality measures) or at the ego level (e.g., ego-network density).
- Some of these structural measures are calculated on the alter-alter network excluding the ego. Other measures (e.g., ego betweenness, constraint) are calculated on the ego-network including the ego. We will see examples of both.
- Certain measures combine information about both structure and composition: they are based on data about both the distribution of alter-alter ties and the distribution of alter attributes. An example is the average degree centrality (network structure) of alters who are family members (network composition).
- Note that whenever ego-network structure is considered (whether by itself or in combination with composition), we need data on alter-alter ties. Unlike in Chapter 4, just the alter attribute data frame will not be sufficient.
- So in this section we need to work with the
igraph
objects containing the alter-alter tie information for our ego-networks (and possibly also incorporate alter attribute data frames if our measures are a combination of structure and composition).
- So in this section we need to work with the
- What we do in the following code.
- Consider ego-network structural measures based on the distribution of alter-alter ties, with the ego excluded: density, number of components, average alter degree, maximum alter betweenness, number of isolates. Calculate these using
igraph
functions. - Consider structural measures that require the ego to be included in the
igraph
object: ego betweenness, constraint. Calculate these withigraph
. - Calculate measures combining ego-network structure and composition: type of relationship between ego and the most between-central alter; average degree centrality of alters who are family members; density of ties among alters in certain categories (e.g., alters who live in Sri Lanka).
- Consider ego-network structural measures based on the distribution of alter-alter ties, with the ego excluded: density, number of components, average alter degree, maximum alter betweenness, number of isolates. Calculate these using
# Load packages.
library(tidyverse)
library(igraph)
# Load data.
load("./Data/data.rda")
# For structural measures we need the ego-network as an igraph.
# The data.rda file, which we loaded earlier, includes the igraph object of
# ego ID 28's network.
gr.28
## IGRAPH 7b94903 UNW- 45 259 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from 7b94903 (vertex names):
## [1] 2801--2802 2801--2803 2801--2804 2801--2805 2801--2806 2801--2807
## [7] 2801--2808 2801--2809 2801--2810 2801--2811 2801--2812 2801--2813
## [13] 2801--2814 2801--2815 2801--2818 2801--2820 2801--2823 2801--2825
## [19] 2801--2827 2801--2828 2801--2829 2801--2831 2801--2840 2801--2841
## [25] 2802--2803 2802--2804 2802--2805 2802--2806 2802--2807 2802--2808
## + ... omitted several edges
# Let's reassign it to a new, generic object name. This makes the code more
# generic and more easily re-usable on any ego-network igraph object
# (of any ego ID).
gr <- gr.28
# Measures based only on the structure of alter-alter ties ----
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Structural characteristics of the network.
# Network density.
edge_density(gr)
## [1] 0.2616162
## $membership
## 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816
## 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832
## 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845
## 1 1 1 1 1 1 1 1 1 1 1 1 1
##
## $csize
## [1] 45
##
## $no
## [1] 1
## [1] 1
# Summarization of structural characteristics of the alters.
# Average alter degree.
degree(gr) |> mean()
## [1] 11.51111
## [1] 257.0168
## 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816
## 24 17 13 12 12 13 13 12 10 9 11 8 18 14 16 9
## 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832
## 9 10 2 2 9 18 27 13 10 8 4 3 3 3 15 16
## 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845
## 16 12 21 3 10 14 14 16 13 7 12 5 12
## [1] 0
# All sorts of more complicated structural analyses can be run on an ego-network
# using igraph or statnet functions. For example, here are the results of the
# Girvan-Newman community-detection algorithm on the ego-network.
cluster_edge_betweenness(gr, weights= NA)
## IGRAPH clustering edge betweenness, groups: 10, mod: 0.41
## + groups:
## $`1`
## [1] "2801" "2802" "2803" "2804" "2805" "2806" "2807" "2808" "2809" "2810"
## [11] "2811" "2812" "2840" "2841"
##
## $`2`
## [1] "2813" "2814" "2815" "2816" "2817" "2818" "2824" "2831"
##
## $`3`
## [1] "2819" "2836"
##
## + ... omitted several groups/vertices
# What if we want to calculate the same structural measure (e.g. density) on all
# ego-networks? We can take the list of all ego-networks and run the same
# function on every list element with the purrr package in tidyverse.
# List that contains all our ego-networks.
class(gr.list)
## [1] "list"
## [1] 102
## $`28`
## IGRAPH 7b94903 UNW- 45 259 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from 7b94903 (vertex names):
## [1] 2801--2802 2801--2803 2801--2804 2801--2805 2801--2806 2801--2807
## [7] 2801--2808 2801--2809 2801--2810 2801--2811 2801--2812 2801--2813
## [13] 2801--2814 2801--2815 2801--2818 2801--2820 2801--2823 2801--2825
## [19] 2801--2827 2801--2828 2801--2829 2801--2831 2801--2840 2801--2841
## [25] 2802--2803 2802--2804 2802--2805 2802--2806 2802--2807 2802--2808
## + ... omitted several edges
##
## $`29`
## IGRAPH 1a1e625 UNW- 45 202 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from 1a1e625 (vertex names):
## [1] 2901--2902 2901--2904 2901--2906 2901--2907 2901--2908 2901--2909
## [7] 2901--2910 2901--2911 2901--2915 2901--2916 2901--2917 2901--2918
## [13] 2901--2919 2901--2927 2901--2928 2901--2929 2901--2930 2901--2931
## [19] 2901--2936 2901--2942 2901--2945 2902--2903 2902--2904 2902--2905
## [25] 2902--2906 2902--2907 2902--2908 2902--2909 2902--2910 2902--2911
## + ... omitted several edges
##
## $`33`
## IGRAPH c941edc UNW- 45 207 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from c941edc (vertex names):
## [1] 3301--3302 3301--3303 3301--3304 3301--3305 3301--3306 3301--3307
## [7] 3301--3308 3301--3309 3301--3310 3301--3311 3301--3312 3301--3313
## [13] 3301--3314 3301--3315 3301--3316 3301--3317 3301--3318 3301--3319
## [19] 3301--3320 3301--3321 3301--3322 3301--3323 3302--3303 3302--3304
## [25] 3302--3305 3302--3306 3302--3307 3302--3308 3302--3309 3302--3310
## + ... omitted several edges
##
## $`35`
## IGRAPH 0c4b65d UNW- 45 221 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from 0c4b65d (vertex names):
## [1] 3501--3502 3501--3503 3501--3504 3501--3505 3501--3506 3501--3507
## [7] 3501--3508 3501--3509 3501--3510 3501--3512 3501--3513 3501--3514
## [13] 3501--3516 3501--3517 3501--3522 3501--3523 3501--3524 3501--3525
## [19] 3501--3526 3501--3527 3501--3528 3501--3529 3501--3530 3501--3532
## [25] 3501--3533 3501--3534 3501--3535 3501--3536 3501--3540 3501--3543
## + ... omitted several edges
##
## $`39`
## IGRAPH cc4d2c3 UNW- 45 92 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from cc4d2c3 (vertex names):
## [1] 3901--3902 3901--3903 3901--3904 3901--3905 3901--3906 3901--3907
## [7] 3901--3911 3901--3913 3901--3920 3901--3924 3901--3925 3901--3931
## [13] 3901--3932 3901--3933 3901--3937 3901--3938 3901--3940 3901--3941
## [19] 3902--3903 3902--3904 3902--3905 3902--3906 3902--3907 3902--3908
## [25] 3902--3909 3902--3910 3902--3913 3902--3924 3902--3925 3902--3931
## + ... omitted several edges
##
## $`40`
## IGRAPH 6ab185b UNW- 45 255 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from 6ab185b (vertex names):
## [1] 4001--4003 4001--4007 4001--4008 4001--4009 4001--4036 4001--4040
## [7] 4001--4045 4002--4003 4002--4004 4002--4006 4002--4008 4002--4009
## [13] 4002--4010 4002--4011 4002--4012 4002--4013 4002--4014 4002--4015
## [19] 4002--4016 4002--4017 4002--4021 4002--4029 4002--4036 4002--4037
## [25] 4002--4038 4002--4040 4002--4041 4002--4042 4002--4043 4002--4044
## + ... omitted several edges
# We can use purrr::map() to run the same function on every element of the list.
purrr::map_dbl(gr.list, edge_density)
## 28 29 33 35 39 40 45
## 0.26161616 0.20404040 0.20909091 0.22323232 0.09292929 0.25757576 0.18484848
## 46 47 48 49 51 52 53
## 0.26969697 0.53737374 0.20303030 0.42828283 0.16969697 0.12929293 0.16565657
## 55 56 57 58 59 60 61
## 0.23838384 0.22828283 0.60000000 0.20505051 0.50505051 0.28888889 0.37676768
## 62 64 65 66 68 69 71
## 0.30202020 0.28282828 0.36060606 0.30909091 0.23333333 0.23636364 0.47777778
## 73 74 78 79 80 81 82
## 0.40707071 0.42525253 0.17575758 0.29292929 0.33434343 0.29797980 0.35353535
## 83 84 85 86 87 88 90
## 0.38888889 0.27575758 0.12525253 0.18989899 0.24444444 0.40101010 0.45353535
## 91 92 93 94 95 97 99
## 0.47171717 0.43535354 0.14141414 0.26767677 0.36767677 0.35050505 0.24040404
## 102 104 105 107 108 109 110
## 0.27777778 0.22929293 0.26666667 0.16262626 0.10606061 0.33535354 0.48686869
## 112 113 114 115 116 118 119
## 0.13939394 0.23636364 0.28787879 0.33232323 0.22929293 0.37676768 0.33131313
## 120 121 122 123 124 125 126
## 0.20101010 0.23838384 0.35959596 0.39090909 0.61111111 0.26464646 0.72828283
## 127 128 129 130 131 132 133
## 0.28080808 0.26262626 0.22727273 0.22020202 0.27979798 0.29090909 0.27575758
## 135 136 138 139 140 141 142
## 0.22525253 0.28686869 0.19797980 0.31515152 0.29292929 0.26868687 0.21010101
## 144 146 147 149 151 152 153
## 0.24949495 0.14747475 0.23030303 0.20707071 0.27272727 0.25151515 0.34242424
## 154 155 156 157 158 159 160
## 0.31818182 0.39393939 0.24646465 0.37777778 0.23737374 0.37979798 0.36767677
## 161 162 163 164
## 0.41010101 0.41111111 0.35454545 0.32222222
# We'll talk more about this and show more examples in the section about
# multiple ego-networks.
# Structural measures requiring ego in the network ----
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# We now need the ego-network with ego included. The file data.rda, loaded earlier,
# includes this ego-network for ego ID 28.
gr.ego.28
## IGRAPH 856e8ff UNW- 46 304 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from 856e8ff (vertex names):
## [1] 2801--2802 2801--2803 2801--2804 2801--2805 2801--2806 2801--2807
## [7] 2801--2808 2801--2809 2801--2810 2801--2811 2801--2812 2801--2813
## [13] 2801--2814 2801--2815 2801--2818 2801--2820 2801--2823 2801--2825
## [19] 2801--2827 2801--2828 2801--2829 2801--2831 2801--2840 2801--2841
## [25] 2802--2803 2802--2804 2802--2805 2802--2806 2802--2807 2802--2808
## + ... omitted several edges
# Let's reassign it to another, generic object name.
gr.ego <- gr.ego.28
# Ego's betweenness centrality.
# weights = NA so the function doesn't use the $weight edge attribute to
# calculate weighted betweenness.
betweenness(gr.ego, v = "ego", weights = NA)
## ego
## 434.0271
## ego
## 0.07344785
# Measures combining composition and structure: From structure to composition ----
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Non-network attribute of alters selected based on structural characteristics.
# Type of relationship with alter with max betweenness.
# Logical index for alter with max betweenness.
ind <- betweenness(gr, weights = NA) == max(betweenness(gr, weights = NA))
# Get that alter.
V(gr)[ind]
## + 1/45 vertex, named, from 7b94903:
## [1] 2801
## [1] "Close family"
# Note that there might be multiple alters with the same (maximum) value of
# betweenness. For that case, we'll need more complicated code (see exercise).
# Measures combining composition and structure: From composition to structure ----
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Structural characteristics of alters selected based on composition.
# Average degree of Close family.
# Vertex sequence of Close family members.
(clo.fam.vs <- V(gr)[alter.rel=="Close family"])
## + 5/45 vertices, named, from 7b94903:
## [1] 2801 2803 2804 2805 2806
## [1] 14.8
# Count of ties between alters who live in Sri Lanka.
# First get the vertex sequence of alters who live in Sri Lanka.
(alters.sl <- V(gr)[alter.res=="Sri Lanka"])
## + 17/45 vertices, named, from 7b94903:
## [1] 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2827 2828 2829
## [16] 2840 2841
## + 88/259 edges from 7b94903 (vertex names):
## [1] 2801--2802 2801--2803 2801--2804 2801--2805 2801--2806 2801--2807
## [7] 2801--2808 2801--2809 2801--2810 2801--2811 2801--2812 2801--2827
## [13] 2801--2828 2801--2829 2801--2840 2801--2841 2802--2803 2802--2804
## [19] 2802--2805 2802--2806 2802--2807 2802--2808 2802--2809 2802--2810
## [25] 2802--2811 2802--2812 2802--2840 2802--2841 2803--2804 2803--2805
## [31] 2803--2806 2803--2807 2803--2808 2803--2809 2803--2810 2803--2811
## [37] 2803--2812 2803--2840 2803--2841 2804--2805 2804--2806 2804--2807
## [43] 2804--2808 2804--2809 2804--2810 2804--2811 2804--2840 2804--2841
## [49] 2805--2806 2805--2807 2805--2808 2805--2809 2805--2810 2805--2811
## [55] 2805--2840 2805--2841 2806--2807 2806--2808 2806--2809 2806--2810
## + ... omitted several edges
# How many edges are there between alters who live in Sri Lanka?
E(gr)[alters.sl %--% alters.sl] |>
length()
## [1] 88
# Density between alters who live in Sri Lanka.
# Get subgraph of relevant alters
induced_subgraph(gr, vids = alters.sl)
## IGRAPH 103e612 UNW- 17 88 --
## + attr: .egoID (g/n), ego_ID (g/c), name (v/c), alter_num (v/n),
## | alter.sex (v/c), alter.age.cat (v/c), alter.rel (v/c), alter.nat
## | (v/c), alter.res (v/c), alter.clo (v/n), alter.loan (v/c), alter.fam
## | (v/c), alter.age (v/n), weight (e/n)
## + edges from 103e612 (vertex names):
## [1] 2801--2802 2801--2803 2802--2803 2801--2804 2802--2804 2803--2804
## [7] 2801--2805 2802--2805 2803--2805 2804--2805 2801--2806 2802--2806
## [13] 2803--2806 2804--2806 2805--2806 2801--2807 2802--2807 2803--2807
## [19] 2804--2807 2805--2807 2806--2807 2801--2808 2802--2808 2803--2808
## [25] 2804--2808 2805--2808 2806--2808 2807--2808 2801--2809 2802--2809
## + ... omitted several edges
## [1] 0.6470588
5.3 R lists
- A lists is simply a collection of objects. It can contain any kind of object, with no restriction. A list can contain other lists.
- Lists have
list
astype
andclass
. - Data frames are a type of list. Other complex objects in R are also stored as lists (have
list
astype
), although theirclass
is notlist
: for example, results from statistical estimations or network community detection procedures. - Use
str(list)
to display the types and lengths of elements in a list. - List may be named, that is, have element names. You can view or assign names with the
names
function (base R) or theset_names
function (tidyverse). - Three different notations to index lists:
[ ]
notation, e.g.my.list[3]
.[[ ]]
notation, e.g.my.list[[3]]
ormy.list[["element.name"]]
.- The
$
notation. This only works for named lists. E.g.,list$element.name
. This is the same as the[[ ]]
notation:list$element.name
is the same aslist[["element.name"]]
orlist[[i]]
(wherei
is the position of the element called element.name in the list).
- These three indexing methods work in exactly the same way as for data frames (see Section 2.5.1).
- What we do in the following code.
- Create, display, and index a list.
## [1] 1 2 3 4 5 6 7 8 9 10
## [,1] [,2]
## [1,] 1 3
## [2,] 2 4
## [1] "white" "aliceblue" "antiquewhite" "antiquewhite1"
## [5] "antiquewhite2"
## [[1]]
## [1] 1 2 3 4 5 6 7 8 9 10
##
## [[2]]
## [,1] [,2]
## [1,] 1 3
## [2,] 2 4
##
## [[3]]
## [1] "white" "aliceblue" "antiquewhite" "antiquewhite1"
## [5] "antiquewhite2"
## $numbers
## [1] 1 2 3 4 5 6 7 8 9 10
##
## $matrix
## [,1] [,2]
## [1,] 1 3
## [2,] 2 4
##
## $colors
## [1] "white" "aliceblue" "antiquewhite" "antiquewhite1"
## [5] "antiquewhite2"
## [1] "list"
## [1] "list"
## $numbers
## [1] 1 2 3 4 5 6 7 8 9 10
## [1] 1 2 3 4 5 6 7 8 9 10
## [1] 1 2 3 4 5 6 7 8 9 10
## [1] 1 2 3 4 5 6 7 8 9 10
## List of 3
## $ numbers: int [1:10] 1 2 3 4 5 6 7 8 9 10
## $ matrix : int [1:2, 1:2] 1 2 3 4
## $ colors : chr [1:5] "white" "aliceblue" "antiquewhite" "antiquewhite1" ...
5.4 Analyzing the structure of many ego-networks
- In base R, functions of the
apply
family, such aslapply
andsapply
, are the traditional way to take a function and apply it to every element of a list. In tidyverse, thepurrr
package does the same thing but in more efficient ways and with more readable code. - We use
purrr
to apply the same function to each element of a list of ego-networks, that is, to each ego-network. Depending on the function, the result for each ego-network may be a single number or a more complex object (for example, a data frame). purrr
provides type-stable functions, which always return the same type of output:map
always returns a list. (This is the equivalent oflapply
in base R).map_dbl
always returns a vector of double-precision numbers.map_int
returns a vector of integer numbers.map_chr
returns a character vector.map_lgl
returns a logical vector.map_dfr
returns a data frame. It assumes that you are applying an operation to each list element, which returns one data frame row for that element. It then binds together the data frame rows obtained for each element, combining them into a single data frame.
- In addition to type-stable output, the
map
functions also offer a convenient formula syntax:~ f(.x)
, where: (1).x
represents each element of the input list; (2)f
is the function to be executed on that element.- For example,
map(L, ~ .x * 2)
takes each element of the listL
(represented by.x
) and multiplies it times 2.
- For example,
- The
map
functions preserve list names in their output. If we have a list of ego-networks whose names are the ego IDs, this means that ego IDs will be preserved in result lists and vectors. map
is also useful when you want to run functions that take a list element as argument (e.g. anigraph
ego-network), and return another list element as output (e.g. anotherigraph
object). This is the case whenever you want to manipulate the ego-networks (for example, only keep a certain type of ties or vertices in the networks) and store the results in a new list.map_dbl
is useful when you want to run functions that take a list element as argument (e.g. anigraph
ego-network), and return a number as output. This is the case whenever you want to run a structural measure such as tie density or centralization on each network.- What we do in the following code.
- Use
purrr
functions to calculate the same structural measures on every ego-network in the data.
- Use
# A simple structural measure such as network density can be applied to every
# ego-network (i.e., every element of our list), and returns a scalar for each
# network. All the scalars can be put in a vector.
gr.list |>
purrr::map_dbl(edge_density)
## 28 29 33 35 39 40 45
## 0.26161616 0.20404040 0.20909091 0.22323232 0.09292929 0.25757576 0.18484848
## 46 47 48 49 51 52 53
## 0.26969697 0.53737374 0.20303030 0.42828283 0.16969697 0.12929293 0.16565657
## 55 56 57 58 59 60 61
## 0.23838384 0.22828283 0.60000000 0.20505051 0.50505051 0.28888889 0.37676768
## 62 64 65 66 68 69 71
## 0.30202020 0.28282828 0.36060606 0.30909091 0.23333333 0.23636364 0.47777778
## 73 74 78 79 80 81 82
## 0.40707071 0.42525253 0.17575758 0.29292929 0.33434343 0.29797980 0.35353535
## 83 84 85 86 87 88 90
## 0.38888889 0.27575758 0.12525253 0.18989899 0.24444444 0.40101010 0.45353535
## 91 92 93 94 95 97 99
## 0.47171717 0.43535354 0.14141414 0.26767677 0.36767677 0.35050505 0.24040404
## 102 104 105 107 108 109 110
## 0.27777778 0.22929293 0.26666667 0.16262626 0.10606061 0.33535354 0.48686869
## 112 113 114 115 116 118 119
## 0.13939394 0.23636364 0.28787879 0.33232323 0.22929293 0.37676768 0.33131313
## 120 121 122 123 124 125 126
## 0.20101010 0.23838384 0.35959596 0.39090909 0.61111111 0.26464646 0.72828283
## 127 128 129 130 131 132 133
## 0.28080808 0.26262626 0.22727273 0.22020202 0.27979798 0.29090909 0.27575758
## 135 136 138 139 140 141 142
## 0.22525253 0.28686869 0.19797980 0.31515152 0.29292929 0.26868687 0.21010101
## 144 146 147 149 151 152 153
## 0.24949495 0.14747475 0.23030303 0.20707071 0.27272727 0.25151515 0.34242424
## 154 155 156 157 158 159 160
## 0.31818182 0.39393939 0.24646465 0.37777778 0.23737374 0.37979798 0.36767677
## 161 162 163 164
## 0.41010101 0.41111111 0.35454545 0.32222222
# Note that the vector names (taken from gr.list names) are the ego IDs.
# If you want the same result as a nice data frame with ego IDs, use enframe().
gr.list |>
map_dbl(edge_density) |>
enframe(name = "ego_ID", value = "density")
## # A tibble: 102 × 2
## ego_ID density
## <chr> <dbl>
## 1 28 0.262
## 2 29 0.204
## 3 33 0.209
## 4 35 0.223
## 5 39 0.0929
## 6 40 0.258
## 7 45 0.185
## 8 46 0.270
## 9 47 0.537
## 10 48 0.203
## # ℹ 92 more rows
# Same thing, with number of components in each ego network. Note the ~ .x
# syntax
gr.list |>
map_dbl(~ components(.x)$no) |>
enframe()
## # A tibble: 102 × 2
## name value
## <chr> <dbl>
## 1 28 1
## 2 29 4
## 3 33 7
## 4 35 2
## 5 39 20
## 6 40 2
## 7 45 1
## 8 46 2
## 9 47 1
## 10 48 1
## # ℹ 92 more rows
# With map_dfr() we can calculate multiple structural measures at once on every
# ego-network, and return the results as a single ego-level data frame.
# This assumes that we are applying an operation that returns one data frame row
# for each ego-network. For example, take one ego-network from our list.
gr <- gr.list[[10]]
# Apply an operation to that ego-network, which returns one data frame row.
tibble(dens = edge_density(gr),
mean.deg = mean(igraph::degree(gr)),
mean.bet = mean(igraph::betweenness(gr, weights = NA)),
deg.centr = centr_degree(gr)$centralization)
## # A tibble: 1 × 4
## dens mean.deg mean.bet deg.centr
## <dbl> <dbl> <dbl> <dbl>
## 1 0.203 8.93 33.8 0.365
# Now we can do the same thing but for all ego-networks at once. The result is a
# single ego-level data frame, with one row for each ego.
# Note the .id argument in map_dfr().
gr.list |>
map_dfr(~ tibble(dens= edge_density(.x),
mean.deg= mean(igraph::degree(.x)),
mean.bet= mean(igraph::betweenness(.x, weights = NA)),
deg.centr= centr_degree(.x)$centralization),
.id = "ego_ID")
## # A tibble: 102 × 5
## ego_ID dens mean.deg mean.bet deg.centr
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 28 0.262 11.5 22.9 0.352
## 2 29 0.204 8.98 23.9 0.273
## 3 33 0.209 9.2 4.56 0.291
## 4 35 0.223 9.82 22.0 0.481
## 5 39 0.0929 4.09 7.18 0.316
## 6 40 0.258 11.3 21.3 0.492
## 7 45 0.185 8.13 20.6 0.679
## 8 46 0.270 11.9 19.6 0.503
## 9 47 0.537 23.6 10.5 0.372
## 10 48 0.203 8.93 33.8 0.365
## # ℹ 92 more rows
# ***** EXERCISES
#
# (1) Get the graph for ego ID 28 from gr.list. Get the max betweenness of
# Italian alters on this graph. Based on this code, use map() to get the same
# measure that for all egos.
#
# (2) Given a personal network gr, the subgraph of close family in the personal
# network is induced_subgraph(gr, V(gr)[alter.rel=="Close family"]). Use
# purrr::map() to get the family subgraph for every personal network in gr.list,
# and put the results in a new list called gr.list.family. Use the formula
# notation (~ .x).
#
# *****