Bolivia

A Network Analysis of the 2019 Bolivian Political Crisis.

Walid Medani https://walidmedani.github.io/networks-blog/
2022-05-01

Introduction

In the wake of Bolivia’s 2019 general elections, the Organization of American States (OAS) claimed in its report that the socialist party of Evo Morales was rigging the election. As a result, protests from the opposing party erupted, eventually leading to a coup supported by the police and military. As a result, Jeanine Áñez of the opposition Democrat Social Movement, assumed the interim presidency of Bolivia. Evo Morales and other members of his party sought political asylum in Mexico citing concerns over their livelihood.

Months later, multiple studies analyzing the election results found no statistical evidence for fraud and also pointed out errors in the coding of OAS’s report. With concerns of the OAS’s history of being a US sponsored organization that was utilized to stop communism in Latin America during the cold war; many left-wing proponents believed that the US backed right-wing coup of Bolivia was a ploy to exploit the country’s lithium reserves — its deposits being the second largest in the world.

Earlier this year, Morales’s party won the re-election by a landslide and ousted Jeanine Áñez’s interim presidency that was marred by controversy and massacres. But concern over the US government’s and US media’s actions and influence still looms.

Data

To analyze the network structure of this political crisis, 63,965 tweets were retrieved from the Academic Twitter API. Tweets were collected from the month of the 2019 crisis to the month before the 2020 election (09/10/2019 - 09/18/2020). The search query string only including tweets that matched two keywords from the following list: bolivia, coup, democracy, evo, morales, anez, jeanine.

The original data was in RDS format and converted into an edgelist representing the universe of cases. Data was transformed by only including tweets that mention another user in the dataframe column “mention_screen_name” with a minimum of 2 retweets. There are 5,539 nodes and what constitutes a node within this dataset is the original sender of a tweet. Ties are defined when a “sender” has a tweet mentioning another user. There are 6,986 ties and the ties are weighted by the count of retweets.

Descriptive Statistics

Many users are interconnected within this sparsely connected graph due to direct ties measuring mentions rather than retweets, meaning the authors had to mention the user within their tweet rather than simply retweeting. There are 966 total components with the main component having 3327 points whereas the minor component only contains 14. The network is fairly centralized (49%) with a small number of nodes representing the information flow, however the centralization score almost doubles when we focus on the main component. The low global transivity score showcases that there isn’t a shared attribute (ideology, opinions, etc.) clustering nodes together when they’re mentioning users in their tweets. This transitivity trend continues at the local level of the ego network (when users mention each other) even when the proportion of mutual connections is measured via reciprocity.

The average number of steps along the shortest paths of network nodes is 27.6, showing an extent of inefficiency in information. The distribution of degrees shows a lack of statistical dispersion, with the average user only having one link for both mentioning another twitter user or receiving a mention.

# DESCRIPTIVE STATS

vcount(mt)
[1] 5539
ecount(mt)
[1] 6986
graph.density(mt, loops = TRUE)
[1] 0.0002277015
igraph::components(mt)$no
[1] 966
head(igraph::components(mt)$csize)
[1]    2    2    3 3327    2    2
centr_degree(mt, mode = c("in"), loops = TRUE,normalized = TRUE)$centralization
[1] 0.4873129
maincomponent <- induced_subgraph(
  mt, V(mt)[components(mt)$membership == which.max(components(mt)$csize)]
)
centr_degree(maincomponent, mode = c("in"), loops = TRUE,normalized = TRUE)$centralization
[1] 0.8112694
transitivity(mt, type = "global")
[1] 0.0006518992
transitivity(mt, type="average")
[1] 0.04946828
[1] 0.005725737
igraph::dyad.census(mt)
$mut
[1] 18

$asym
[1] 5116

$null
[1] 15332357
igraph::triad_census(mt) 
 [1] 28280145269    24949093     2125336        6977      665147
 [6]        3462         116         335         128           0
[11]           7           6           7           4           2
[16]           0
[1] 27.597
head(igraph::degree(mt))
     DFalconeti   joshua__frank  goffmansmasker    AleFerruzcaL 
              1               1               1               6 
      SabatoFan Maryest66760646 
              1               1 
# Degrees Histogram
mt.nodes <- data.frame(name=V(mt)$name, degree=igraph::degree(mt)) %>%
  arrange(desc(degree))


summary(mt.nodes)
     name               degree        
 Length:5539        Min.   :   1.000  
 Class :character   1st Qu.:   1.000  
 Mode  :character   Median :   1.000  
                    Mean   :   2.522  
                    3rd Qu.:   1.000  
                    Max.   :2700.000  
hist(mt.nodes$degree, main="2019 Bolivian Political Crisis (Degrees)", xlab = "") 
# scale_fill_manual(values 

head(closeness(mt))
     DFalconeti   joshua__frank  goffmansmasker    AleFerruzcaL 
      0.5000000       0.5000000       0.5000000       0.0062500 
      SabatoFan Maryest66760646 
      0.5000000       0.1428571 
mt.nodes <- mt.nodes %>%
    mutate(indegree=igraph::degree(mt, mode="in", loops=TRUE),
           outdegree=igraph::degree(mt, mode="out", loops=TRUE))
summary(mt.nodes) %>% 
kbl() %>%
kable_material_dark(c("striped", "hover")) %>% 
scroll_box(width = "100%", height = "auto")
name degree indegree outdegree
Length:5539 Min. : 1.000 Min. : 0.000 Min. : 0.000
Class :character 1st Qu.: 1.000 1st Qu.: 0.000 1st Qu.: 0.000
Mode :character Median : 1.000 Median : 0.000 Median : 1.000
NA Mean : 2.522 Mean : 1.261 Mean : 1.261
NA 3rd Qu.: 1.000 3rd Qu.: 1.000 3rd Qu.: 1.000
NA Max. :2700.000 Max. :2700.000 Max. :464.000

Data Visualization

The first graph shows the overall network graph, when a user mentions their own username.

#VIZ
kcore <- coreness(mt, mode="all") 
twocore <- induced_subgraph(mt, kcore>=1)

graph1 <- visIgraph(twocore,idToLabel = TRUE,layout = "layout_nicely") %>%
  visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE) 
graph1

Here kcore = 4, to identify a smaller subset of interconnected users that have at least 4 edges with other users in the core.

#VIZ 2
kcore4 <- coreness(mt, mode="all") 
fourcore <- induced_subgraph(mt, kcore4>=4)

graph2 <- visIgraph(fourcore,idToLabel = TRUE,layout = "layout_nicely") %>%
  visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE)
graph2

This visualization lowers kcore = 1 but focuses on the main component.

#comp viz

kcore1 <- coreness(maincomponent, mode="all") 
onecore <- induced_subgraph(maincomponent, kcore1>=1)

graph3 <- visIgraph(onecore,idToLabel = TRUE,layout = "layout_nicely") %>%
  visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE) 
graph3

Prominent Nodes

Measuring popularity/status with in/out degree centrality made the most sense for this dataset due to it being directed and is simply looking at mentions of usernames. The ousted president Evo Morales (evoespueblo) received the most mentions by twitter users followed by La Razón, one of the largest newspaper publications within Bolivia.

indegree_mt <- sort(degree(mt,mode = "in"),decreasing = TRUE)
indegree_mt[1:10] %>% #show the top 10 users ranked by in-degree 
kbl() %>%
kable_material_dark(c("striped", "hover")) %>% 
scroll_box(width = "100%", height = "auto")
x
evoespueblo 2700
LaRazon_Bolivia 92
JeanineAnez 74
entrammbasaguas 65
Almagro_OEA2015 58
yeidckol 54
CarmenEGonzale2 40
DiazCanelB 38
JohnMAckerman 34
CMonteroOficial 29

And below we see the users who most mentioned other users, finding a mix of journalists, supporters, and opponents of Evo Morales.

outdegree_mt <- sort(degree(mt,mode = "out"),decreasing = TRUE)
outdegree_mt[1:10] %>% #show the top 10 users ranked by out-degree
kbl() %>%
kable_material_dark(c("striped", "hover")) %>% 
scroll_box(width = "100%", height = "auto")
x
GiovannaZeball4 464
rayleon1515 295
DAVlD51 157
MonicaAparicioA 107
FranzRBarriosG 96
BenjaminNorton 72
Libverd 46
t_taeki 43
elinforman_t 38
YaotlAltan 33

Below are users who acted as a bridge between the other nodes within the network by measuring betweenness centrality.

# BETWEENESS 
bt <- sort(betweenness(mt, directed=T), decreasing = TRUE)
bt[1:10] %>%  #show the top 10 nodes by betweenness centrality 
kbl() %>%
kable_material_dark(c("striped", "hover")) %>% 
scroll_box(width = "100%", height = "auto")
x
GiovannaZeball4 6259.0
CarmenEGonzale2 4953.5
MonicaAparicioA 4845.0
alopezescarre 3462.5
DAVlD51 3052.0
marcoci56595060 2717.0
Libverd 2636.5
rayleon1515 2627.0
entrammbasaguas 2320.0
Amp_Bald63 1166.0

Using Google’s PageRank algorithm we can find the biggest influencers in this network through the ranks of search results.

pr <- page_rank(mt, algo = c("prpack"))
pr <- sort(pr$vector,decreasing = TRUE)
pr[1:10] %>% #show the top 10 users ranked by PageRank
kbl() %>%
kable_material_dark(c("striped", "hover")) %>% 
scroll_box(width = "100%", height = "auto")
x
evoespueblo 0.1036595
LaRazon_Bolivia 0.0057484
CarmenEGonzale2 0.0053971
yeidckol 0.0044487
entrammbasaguas 0.0038097
JeanineAnez 0.0036367
Almagro_OEA2015 0.0033602
JohnMAckerman 0.0023502
DiazCanelB 0.0022066
ChalecosAmarill 0.0020588

Total measures of popularity/status

# NODES
degree.nodes<-data.frame(name=V(mt)$name,
         totdegree=igraph::degree(mt, loops = TRUE), 
         indegree=igraph::degree(mt, loops = TRUE), 
         outdegree=igraph::degree(mt,loops = TRUE),
          eigen <- centr_eigen(mt, directed=T),
         bt)

arrange(degree.nodes, desc(indegree)) %>%
  head() %>% 
kbl() %>%
kable_material_dark(c("striped", "hover")) %>% 
scroll_box(width = "100%", height = "auto")
name totdegree indegree outdegree vector value options.bmat options.n options.which options.nev options.tol options.ncv options.ldv options.ishift options.maxiter options.nb options.mode options.start options.sigma options.sigmai options.info options.iter options.nconv options.numop options.numopb options.numreo centralization theoretical_max bt
evoespueblo evoespueblo 2700 2700 2700 1.0000000 4.19761 I 5539 LR 1 0 0 0 1 1000 1 1 1 0 0 0 2 1 29 0 29 0.9998964 5538 0
GiovannaZeball4 GiovannaZeball4 473 473 473 0.0063064 4.19761 I 5539 LR 1 0 0 0 1 1000 1 1 1 0 0 0 2 1 29 0 29 0.9998964 5538 0
rayleon1515 rayleon1515 300 300 300 0.0015024 4.19761 I 5539 LR 1 0 0 0 1 1000 1 1 1 0 0 0 2 1 29 0 29 0.9998964 5538 86
DAVlD51 DAVlD51 169 169 169 0.0005272 4.19761 I 5539 LR 1 0 0 0 1 1000 1 1 1 0 0 0 2 1 29 0 29 0.9998964 5538 0
MonicaAparicioA MonicaAparicioA 115 115 115 0.0005835 4.19761 I 5539 LR 1 0 0 0 1 1000 1 1 1 0 0 0 2 1 29 0 29 0.9998964 5538 113
FranzRBarriosG FranzRBarriosG 103 103 103 0.0002780 4.19761 I 5539 LR 1 0 0 0 1 1000 1 1 1 0 0 0 2 1 29 0 29 0.9998964 5538 0

Communities

To discover communities, the walktrap community detection method was used due to it handling weights, increasing the probability of it going towards its direction. Below is a visualization of the communities color coded by the walktrap method.

# COMMUNITY

#mt.eb <- cluster_edge_betweenness(mt) 

mt.wt <- walktrap.community(mt, weights = TRUE)
membership(mt.wt)[1:20]   #list only 10 nodes.
     DFalconeti   joshua__frank  goffmansmasker    AleFerruzcaL 
              1               2               3               4 
      SabatoFan Maryest66760646   yaciramendoza        _KevFal_ 
              5               6               7               8 
         JpFair mundodedesigual        Nsnc2019        deborakr 
              9              10              11              12 
        GDAEman   MarKaAbyaYala     quimogrande       Markbearz 
             13              14              15              16 
     Gaby_Joy97 descuajeringado     Jckarlos521      Barry__Ale 
             17              18              19              20 
rt <- mt
V(rt)$color <- membership(mt.wt)

kcorewt <- coreness(rt, mode="all") 
twocorewt <- induced_subgraph(rt, kcorewt>=2)

graph4 <- visIgraph(twocorewt,idToLabel = TRUE,layout = "layout_nicely") %>%
  visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE) 
graph4

Inferential Statistics

By conditioning the transitivity (clustering) of the network on size, density, and dyads. We find that in 1,000 random networks, the variable number of nodes is able to produce a greater value than the observed graph’s transitivity.

CUG Plot

This network graph has a low centralization score. Below it will be evaluated on indegree and outdegree vertices and conditioned on the variables size, density, and dyads. We find that in 1,000 random networks, none of the variables above were able to produce a value higher than the observed centralization score when based on indegree. However, the variable number of nodes was able to produce a greater value when evaluating for outdegrees.