<figure><img src="image_1.png" /></figure>

HAL
open science

# Introduction to network analysis with R and the igraph package

Sébastien Plutniak

► To cite this version:

Sébastien Plutniak. Introduction to network analysis with R and the igraph package. Doctoral. Melbourne (AUS), Australia. 2025, pp.16. hal-05115696

HAL Id: hal-05115696

https://hal.science/hal-05115696v1

Submitted on 25 Jun 2025

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_2.png" /></figure>

Distributed under a Creative Commons Attribution 4.0 International License
# Introduction to network analysis with R and the igraph package

Sébastien PLUTNIAK — CNRS, CITERES-LAT, FRANCE

sebastien.plutniak@cnrs.fr

## Contents

1 Introduction 2

1.1 Preliminary note 2

1.2 Introducing relational data 2

1.3 Learning R in two lines 4

2 An introduction to igraph 4

2.1 Creating a graph 4

2.2 Inspection: get information about the graph 5

2.3 Modify a graph 6

2.3.1 Modify edges and vertices 6

2.3.2 Modify attributes 6

2.3.3 Boolean operations on graphs 7

2.4 Indexing and extraction: manipulate parts of a graph 7

2.4.1 Indexing 7

2.4.2 Extraction 8

2.5 Metrics: measuring graph properties 9

2.5.1 Global metrics 9

2.5.2 Local metrics 9

2.6 Visualisation: get a graphical representation of a graph 10

2.6.1 Print on screen (standard output) 10

2.6.2 Save as a file 11

2.7 Detecting particular vertex subsets 11

2.7.1 Deterministic methods 11

2.7.2 Clustering methods (“community detection”) 12

3 Exercises 13

3.1 Vertices deletion and degree centrality 14

3.2 Neighboring extraction and degree centrality 14

3.3 Edge betweenness and vertex names 15

3.4 Betweenness centrality and vertex attribute 15

3.5 Closeness centrality, subgraph from vertices, indexing 15

3.6 Graph splitting, articulation points, density 15

3.7 Graphical representation, degree centralization 15

3.8 Clustering methods, betweenness centralization 15

3.9 Graph splitting and union 16

3.10 Comparing clustering methods 16
<table><caption>Table 1: Classroom survey: matrix of the relationships between participants and concepts (i.e. types of relational data).</caption><thead><tr><th></th><th scope="col">type 1</th><th scope="col">type 2</th><th scope="col">type 3</th><th scope="col">...</th></tr></thead><tbody><tr><th scope="row">name 1</th><td></td><td>1</td><td></td><td></td></tr><tr><th scope="row">name 2</th><td>1</td><td>1</td><td></td><td></td></tr><tr><th scope="row">name 3</th><td></td><td></td><td>1</td><td></td></tr><tr><th scope="row">name 4</th><td></td><td></td><td></td><td></td></tr><tr><th scope="row">...</th><td></td><td></td><td></td><td></td></tr></tbody></table>

# 1 Introduction

## 1.1 Preliminary note

This document is intended for instructors willing to introduce students to graph ("network") analysis with the *igraph* R package<fnref n="1" /> for a 2-3 hours workshop. It is assumed that participants:

* have no prior knowledge of graph analysis,

* are false beginners in R (or any other programming language).

## 1.2 Introducing relational data

What is relational data? Let's clarify it using participants' insights. Each participant in the room is invited to briefly introduce himself/herself by mentioning:

* his/her name

* 1 or 2 types of relational observation he/she studies in his/her research

Assuming the seats are organised in a U-shape and that participants are asked in a row, record the answers on a board as in Table 1.

This dataset can be used to introduce some preliminary concepts.

* What sort of data structure do we have?

*A matrix. Matrices and matrix calculus are fundamental in graph analysis.*

* We found several types of relational data. What sort of relational data does this matrix contain?

*Individuals connected to concepts. This is an example of socio-epistemic network, enabling us to answer question such as "how the guys in this room are organised based on common research interest?"*

* But there's one more type of relational data we have also recorded and that we can extract from this matrix. Which one?

*Spatial proximity! Based on this list of names, a second matrix can be defined using the name rank as spatial distance, as in Table 2.*

What are the differences between the first and the second matrices? The second matrix has:

<footnote n="1">¹Gábor Csárdi and Tamás Nepusz [2006], "The igraph Software Package for Complex Network Research," *InterJournal*, 1695, 5, pp. 1-9, http://igraph.org.</footnote>
<table><caption>Table 2: Classroom survey: matrix of the spatial relative distance between participants.</caption><thead><tr><th></th><th>name 1</th><th>name 2</th><th>name 3</th><th>...</th></tr></thead><tbody><tr><td>name1</td><td>0</td><td>1</td><td>2</td><td></td></tr><tr><td>name2</td><td>1</td><td>0</td><td>1</td><td></td></tr><tr><td>name3</td><td>2</td><td>1</td><td>0</td><td></td></tr><tr><td>...</td><td></td><td></td><td></td><td></td></tr></tbody></table>

* The same numbers of rows and columns: that's a **square** matrix.

* The same type of information in row and column: the graph created from this matrix is **unipartite**

* Different values: the graph created from this matrix is a **weighted** graph.

* The upper and the lower triangles contain the same information, that's a **symmetrical** matrix. The graph created from this matrix is said to be **non-oriented**.

Let's now consider the first matrix, which is more complex. It has:

* Different numbers of rows and columns: that's a **rectangular** matrix.

* Different types of information in rows and columns. In network analysis, the graph built from this data structure is called **bipartite** or **2-mode**.

* Two types of values: 0 and 1. The graph created from it is said to be an **unweighted** graph.

* Different values in the matrix's upper triangle and lower triangles; consequently, the relationships can be regarded in two ways: either individuals connected to concepts or concepts connected to individuals. This type of graph is called an **oriented** graph.

To summarize: from this simple survey, we already see the relationships between matrices and graphs and their diversity! Many aspects and questions can be studied from this simple data-set. Some imply relational aspects and some do not.

What types of questions *do not* imply relational aspects?

* How many types of relational observation are there? (The answer is a count.)

* How diverse are the types of relational observation? (The answer is a measurement of diversity.)

* Are there particular associations between participants and research interests? (Answers can be provided with correspondence analysis.)

Now, what types of questions *do* imply relational aspects? Simple questions might include:

* What research interest is the most central?

* Considering research interests (i.e. type of relational data), which participants are more central? (i.e. are sharing common interests with more other participants)

And more complex questions can be:
* Can we identify coherent sub-groups of participants and/or research interests?

* Is there a correlation between the participants' research interests and their location in the room? (do the participants who share common research interests sit close to each other?)

These are only a few examples of research questions that can be addressed taking benefit from the relational dimension of this sort of data structure. In what follows, we will see how to manipulate this data and apply network analysis method using R and the *igraph* package.

## 1.3 Learning R in two lines

In what follows, a basic knowledge of R is assumed. However, it is worth reminding the, certainly, more crucial R function, namely: `?()`. Before using any other function for the first time, it is worth consulting its documentation. For example, considering that `+` is the function to compute a sum:

```
1+1
?"+"
```

The second fundamental function to learn is assignment: `<-`, which is also a function taking two operands: the name of a variable on its left and the value to assign on its right:

```
my variable <- 1
?"<-"
```

Note that the `+` and `<-` functions are quite unusual in R, since their parameters must be written on the left and right of the function. Most functions in R have a different syntax, namely: the function name, an open bracket, arguments, a closing bracket. See for example the `sum()` function:

```
sum(1, 1)
?sum
```

In what follows, no more R knowledge is required.

# 2 An introduction to igraph

## 2.1 Creating a graph

Let's start by loading the *igraph* package and the complementary *igraphdata* package, offering a convenient series of network data-sets

```
library(igraph)
library(igraphdata)
```

In this tutorial, we are going to use the classical “karate club” data-set, published by the sociologist Wayne Zachary in 1977². It represents friendship relationships between the 34 members of a university karate club, which came to be split in two after a conflict.

First, load the CSV files:

<footnote n="2">²Wayne W. Zachary [1977], “An Information Flow Model for Conflict and Fission in Small Groups,” *Journal of Anthropological Research*, 33, 4, pp. 452-473, DOI: 10.1086/jar.33.4.3629752.</footnote>
```python
# import vertices
karateVertices <- read.csv2("karateVertices.csv")
karateVertices          # table inspection
# importe edges
karate_edges <- read.csv2("karate_edges.csv")
karate_edges           # table inspection
```

The graph is built using the `graph.data.frame()` function:

```r
?graph_from_data_frame
```

This function takes three arguments:

* a data frame for the edges,

* a data frame for the vertices

* whether the graph should be directed or undirected

If the edge data frame has more than two columns, they are interpreted as edge attributes. Similarly, the first column of the vertices data frame is for vertices' unique identifier, and subsequent columns are interpreted as vertices attributes.

```r
karate.net <- graph_from_data_frame(karate_edges,
                                  vertices = karateVertices,
                                  directed = FALSE)
```

Alternatively, the data set can be loaded from the igraphdata package:

```r
data(karate)
karate
```

```r
## IGRAPH 4b458a1 UNW- 34 78 -- Zachary's karate club network
## + attr: name (g/c), Citation (g/c), Author (g/c), Faction (v/n), name
## | (v/c), label (v/c), color (v/n), weight (e/n)
## + edges from 4b458a1 (vertex names):
##   [1] Mr Hi --Actor 2 Mr Hi --Actor 3 Mr Hi --Actor 4 Mr Hi --Actor 5
##   [5] Mr Hi --Actor 6 Mr Hi --Actor 7 Mr Hi --Actor 8 Mr Hi --Actor 9
##   [9] Mr Hi --Actor 11 Mr Hi --Actor 12 Mr Hi --Actor 13 Mr Hi --Actor 14
##  [13] Mr Hi --Actor 18 Mr Hi --Actor 20 Mr Hi --Actor 22 Mr Hi --Actor 32
##  [17] Actor 2--Actor 3 Actor 2--Actor 4 Actor 2--Actor 8 Actor 2--Actor 14
##  [21] Actor 2--Actor 18 Actor 2--Actor 20 Actor 2--Actor 22 Actor 2--Actor 31
##  [25] Actor 3--Actor 4 Actor 3--Actor 8 Actor 3--Actor 9 Actor 3--Actor 10
## + ... omitted several edges
```

## 2.2 Inspection: get information about the graph

Compare the printed outputs of the two versions of the graph.

* **IGRAPH**: that's an igraph network object

* a character string: this graph object's unique identifier (it is randomly generated and changes every time a graph is created.

* `UNW-`: that's an *Undirected*, *Named*, and *Weighted* graph
* 34: the graph has 34 vertices

* 78: the graph has 78 edges

* attr:

- name (g/c): a **graph attribute**, of type **character strings**, called 'name'

- Faction (v/c): a **vertex attribute**, of type **character strings**, called 'Faction'

- weight (e/n): an **edge attribute**, of type **numerical**, called 'weight'

* 'edges from': the top of the edge list is printed

Note that the *igraphdata* version has more vertices attributes (Faction, color, label).

Other information can be retrieved using:

```
typeof(karate.net) # what is the type of this object?
```

```
class(karate.net) # what is the class of this object?
```

Let's have a closer look at the vertices and edges:

```
# vertices inspection:
V(karate.net)
```

```
# edges inspection:
E(karate.net)
```

And, now, let's examine the vertices and edges attributes:

```
# retrieve the values of the edge 'weight' attribute:
edge_attr(karate.net, "weight")
```

```
# Alternatively, use the shortest form:
E(karate.net)$weight
```

```
# retrieve the values of the vertex 'faction' attribute:
vertex_attr(karate.net, "faction")
```

```
# alternatively, use the shortest form:
V(karate.net)$faction
```

## 2.3 Modify a graph

### 2.3.1 Modify edges and vertices

Two functions enable deleting edges and vertices, respectively:

```
delete_edges()
```

```
delete Grantes()
```

Compare, for example:

```
karate.net
```

```
delete_edges(karate.net, E(karate.net)[1:2])
```

```
delete Grantes(karate.net, V(karate.net)[1:2])
```

### 2.3.2 Modify attributes

Let's add the age of the karateka as a vertex attribute.
V(karate).$age <- sample(15:30, 34, replace = TRUE)

Note that we use two functions to simulate ages:

* the `:` function, which creates a sequence of integers from its first operand to its second operand.

* the `sample()` function, which samples a given number of values (here 34) from the set of values given as the first argument (here 15:30).

Inspect the `karate.net` and find what has changed.
And now, what if we want to delete the `age` attribute?

delete vertex attr(karate, "age")

### 2.3.3 Boolean operations on graphs

`igraph` offers a series of functions to perform Boolean operations on two or several graphs, including:

union(graph1, graph2) # union of two or more graphs
graph1 %u% graph2

disjoint_union(graph1, graph2) # disjoint union of two or more graphs
graph1 %du% graph2 # (assuming that the graphs have different vertex sets)

difference(graph1, graph2) # difference of two or more graphs
graph1 %m% graph2

intersect(graph1, graph2) # intersection of two or more graphs
graph1 %s% graph2

complementer(graph1) # a graph with all edges absent in the input graph.

## 2.4 Indexing and extraction: manipulate parts of a graph

### 2.4.1 Indexing

Indexing is done using the following syntax:
`V( <graph object> )[ <numerical index OR string OR boolean> ]`

V(karate.net)[1] # the first vertex, retrieved from its index
V(karate.net)["Mr Hi"] # the first vertex, retrieved from its name
V(karate.net)[1:10] # first 10 vertices
V(karate.net)[c(1,11)]

Edges can be indexed and retrieved similarly, using the `E()` function.

In addition, more complex selections can be constructed, combining edge and vertex indexing. The following command selects all the edges between the vertex named "John A" and other vertices:
E(karate.net) [ V(karate.net) ["John A"] %--% V(karate.net) ]

This syntax is:

A %--% B edges between vertices A and B

For directed graphs, *igraph* offers two additional operators:

A %->% B edges from A to B

A %<-% B edges from B to A

Let's turn now to Booleans and see how they can be used for indexing. Say we are interested in the karateka more than 25 years old.

V(karate)$age > 25

This command returns a series of Boolean values reflecting if the condition is TRUE or FALSE for each vertex / karateka. Let's use it to select the corresponding vertices:

V(karate) [ V(karate)$age > 25 ]

The (R base) `which()` function returns the TRUE indices of a Boolean vector values:

which( V(karate)$age > 25 )

It is sometimes useful or convenient to combine these functions:

V(karate) [ which(V(karate)$age > 25) ]

### 2.4.2 Extraction

Multiple functions make it possible to extract parts of a graph (subgraphs), including:

induced_subgraph() # extract selected vertices

make_ego_graph() # extract a vertex and its neighbors

subgraph_from_edges() # extract from selected edges

An example using vertices selection:

karate.sub.net <- induced_subgraph(karate,
                                  V(karate) [ V(karate)$age > 25 ]
)
plot(karate.sub.net,
      main="Karate club: subgraph of the karateka more than 25 years old")

An example using edge selection:

karate.sub2.net <- subgraph_from_edges(karate.net,
                                     which(E(karate.net)$weight > 4))
plot(karate.sub2.net,
      main="Karate club: sub-graph of edges with weight > 4")

Extracting a vertex's neighboring:
mrhi.net <- make_ego_graph(karate.net, 1,
                          V(karate.net)["Mr Hi"]
)
plot(mrhi.net[[1]], main = "subgraph of Mr Hi and neighbors")

NB: here, we must use the `[]` syntax because the `make_ego_graph()` function returns a *list* of graphs (even if there is only one graph) and we want to select one element in this list.

## 2.5 Metrics: measuring graph properties

Modelling data as a graph is interesting only if we take advantage of the specific methods to analyse its structural properties. These methods are manifold. To introduce some of them, we distinguish between:

* "global" metrics: reflecting properties of the entire graph.

* "local" metrics: reflecting properties of the vertices.

### 2.5.1 Global metrics

```
gorder(karate.net) # number of vertices
gsize(karate.net)  # number of edges
```

Those metrics do not consider the graph's structure. The following metrics do:

```
diameter(karate.net) # diameter
mean_distance(karate.net) # (i.e. longest path of the graph)
edge_density(karate.net) # mean path length
#(i.e. ratio of the number of edges / maximum possible edges)
transitivity(karate.net, type="global") # transitivity (clustering coefficient)
centr_degree(karate.net) # Graph degree centralization
centr_betw(karate.net) # Graph betweenness centralization
```

Read `?transitivity` to learn about the different transitivity measures.

### 2.5.2 Local metrics

The following measures are computed for each vertex.

```
degree(karate.net, mode="") # vertex degrees
```

Read the doc to learn more about the different ways to compute edge centrality with `degree()`. The degree is one of many different definitions of vertex centrality in a graph! See other methods:

```
# vertex betweenness centrality
betweenness(karate.net)
# vertex closeness centrality
closeness(karate.net)
```

Many other statistics are offered:
```r
# the transitivity can also be computed at vertex level!
# same function, different parameter:
transitivity(karate.net, type="localundirected")
# centrality can also be computed for edges:
edgeBetweenness(karate.net)
# shortest paths between all vertices:
distances(karate.net)
```

Note that `distances()` returns a matrix with the length of the shortest path for each pair of vertices.

Once computed, these metrics can be used for further visualization and statistical analysis. For example, to study the degree distribution, combine the `degree_distribution()` function and the generic `plot()` function:

```r
degree_distribution(karate.net)
plot(degree_distribution(karate.net, cumulative = TRUE))
```

Is there a correlation between the age of the karateka and their degree centrality? We use the `cor()` R function:

```r
cor(V(karate)$age, degree(karate))
```

Well, not really (which is not surprising, since we determined the ages randomly).

## 2.6 Visualisation: get a graphical representation of a graph

### 2.6.1 Print on screen (standard output)

The `plot()` function is the R generic method to generate graphical outputs. This method has generic parameters and also parameters which are adapted to specific types of object, including “igraph” objects.

Reminder: in R, colors can be defined in several ways:

* character string for name, ex.: `"darkslategray4"` or `"brown"`

* characters string for color code, for example generated using the `rgb()` function, with the following syntax: `rgb(<red value>, <green value>, <blue value>, <transparency value>).` for example:

```r
rgb(.1, .8, .0, .95)
```

Graph visualization is useful, but is also potentially misleading, due to the effects of the vertices placement algorithm. The *igraph* package offers many algorithms. Compare:

```r
plot(karate.net)
plot(karate.net, layout = layout_in_circle(karate.net))
plot(karate.net, layout = layout_as_tree(karate.net))
```

By default, for a graph with less than 1000 vertices, *igraph* selects the Fruchterman-Reingold algorithm, i.e. the `layout_with_fr` function (see its documentation to get an idea of its many parameters; see also `?layout_nicely` for information about the different algorithms).

This is an example (far from being exhaustive) of the use of graphical parameters with `plot()` for *igraph* objects:
```python
plot(karate.net,
    # vertex parameters:
    vertex.size = 10 * degree(karate.net) / max(degree(karate.net)) + 3,
    vertex.color = "darkkhaki", # V(x)$color, rgb(.1, .5, .5, .9)
    vertex.label = V(karate.net)$name,
    vertex.label.cex = .6, # 2.2 * degree(x) / max(degree(x)) + .2
    vertex.label.color = "black",
    vertex.shape = "square", # "circle"
    vertex.label.color="black",
    # edge parameters:
    edge arrow.size = 0.5,
    edge.width = E(karate.net)$weight,
    edge.color = rgb(.1, .1, .1, .8),
    # graph parameters:
    layout = layout_with_gem(karate.net), #GEM force-directed algorithm
    # Generic plot() parameters:
    main = "graph title",
    sub = "subtitle")
```

### 2.6.2 Save as a file

To redirect the output of the `plot()` function to a file instead of on the standard output (i.e., Rstudio tab):

1. open a graphical output with one of the following functions: `png()`, `jpeg()`, `pdf()`, `svg()`, etc.

2. execute `plot()`

3. close the graphical output with `dev.off()`

```r
# Use one of:
png("karate.png", width=2000, height=2000, units="px", pointsize=40)
# or
svg("karate.svg", pointsize=20, width=12, height=12)
# or
pdf("karate.pdf", pointsize=11)
# and then:
plot(karate.net, main="Karate Club")
# and, finally:
dev.off()
```

## 2.7 Detecting particular vertex subsets

### 2.7.1 Deterministic methods

A first class of methods allows detecting subsets of vertices with specific properties, for example:

* **connected component**: a subgraph that is not part of any larger connected sub-graphs.

* **articulation points**: vertices whose removal increases the number of connected components.
* **cliques**: a subset of vertices in which every two distinct vertices are adjacent.

* **k-core**: a subgraph in which each vertex has at least a degree of value *k*.

```r
components(karate.net) # lists the number of connected components,
                       # their sizes and the membership of each vertex
articulation_points(karate.net) # returns the vertices which
                                   # are articulation points

count_max_cliques(karate.net) # returns the number of cliques in the graph
cliques(karate.net)         # return a list of cliques

coreness(karate.net)        # k-core decomposition
plot(karate.net, vertex.color = coreness(karate.net))
```

## 2.7.2 Clustering methods (“community detection”)

Besides the previous methods, multiple vertex clustering methods were developed. These methods are complex, have different logic and sensibility, and can be tweaked using multiple parameters. For these reasons, results might be method-dependent and variable from one method to the other.

Ten methods are currently implemented in *igraph*. All take a graph as their main argument and return an object of type *communities*. Let us consider the “edge betweenness” method (also called Girvan–Newman algorithm), which performs by successively removing edges with the highest betweenness, recalculating betweenness values after each removal, based on the idea that betweenness of the edges connecting two communities is typically high.

```r
karate.comm <- cluster_edgeBetweenness(karate.net)
```

A *communities* object contains several elements:

```r
names(karate.comm)
```

Its “membership” element contains the class attributed to each vertex. Let’s use this information to color the vertex:

```r
V(karate.net)$color <- as.character(
  factor(karate.comm$membership,
        labels = rainbow(length(karate.comm)
  ))
```

This command uses several R functions (i.e. non-specific to *igraph*):

* `factor()` to recode a vector of values:

* `length()`, which, applied to the *communities* object, returns the number of communities;

* `rainbow()`, which returns a vector with *n* different colors string.

* `as.character()`, which converts the output of the `factor()` function into a character string (this is required by the `plot()` function when applied to an *igraph* object)
We print the graph on the standard output:

```r
plot(karate.net, main="Karate club",
      sub="Method: edge betweenness")
```

More advanced settings are possible:

```r
plot(karate.net,
     main = "Karate Club: clustering",
     sub = paste("algorithm:", karate.comm$algorithm,
                 " | modularity: ", as.character(
                   round(modularity(karate.comm), 2)
                 ),
                 " | n community: ", as.character(
                   length(karate.comm)
                 )
             )
)
```

As illustrated in the second version of the `plot()` command above, many additional information can be added to the graphical output. The `paste()` function, which concatenates values as character strings, is very handy for this purpose.

The other algorithms implemented in igraph are:

```
cluster_fast_greedy()
cluster_walktrap()
cluster_spinglass()
cluster_leading_eigen()
cluster_label_prop()
cluster_infomap()
cluster_fluid_communities()
cluster_leiden()
cluster_optimal()
```

## 3 Exercises

* Participants join in groups of 4 or 5 persons.

* Each group receives one of the following data-sets:

- Participants' data-sets.

- Participants' network: data set built in the introduction of the workshop. NB: this requires an extra step, creating the data table

- karate: friendship network of a karate club

- UKfaculty: friendship network of a UK university faculty

- foodwebs$Narragan: Narragan Bay food web, directed graph of predator-prey relationships

- kite: a classical artificial data set

- macaque: visuotactile brain areas and connections of the macaque monkey. NB: that's a directed graph

* Each group works on the following exercises using a study case. Exercises require combining the functions presented above. No additional knowledge is needed.
* Results are then discussed altogether.

```python
# load the data set using the data() function, for example:
data(UKfaculty)
# and learn about it:
?UKfaculty
```

1. Remove the vertices whose degree is inferior or equal to 1.

2. Find out which vertex has the highest degree centrality value and extract its neighbors (note that two methods are possible for this, and the `sort` function will be useful).

3. Retrieve the vertex names of the vertices which are connected with the edges having the highest edge betweenness value (the `max()` function will be useful).

4. Compute the vertex betweenness centrality and store the values in a vertex attribute.

5. Create a subgraph including the 5 vertices with the highest closeness centrality value (the `sort()` and `names` functions will be useful).

6. Cut the graph at its articulation points and find which component has the highest density.

7. Create a graphical representation of the graph, printing its degree centralization in the graph title, and export it as a PDF file (use `$$` for indexation).

8. Apply a clustering method of your choice and measure the betweenness centralization of each cluster.

9. Cut the graph at its articulation points and merge (i.e. union) the two largest components (i.e. in vertices count).

10. Choose two clustering methods; apply them, and measure the correlation between the two results (i.e. the cluster assigned to vertices).

Note that these exercises are independent from each other and can be done in any order.

## 3.1 Vertices deletion and degree centrality

Remove the vertices whose degree is inferior or equal to 1.

```python
delete Grant(karate, degree(karate) >= 1)
```

## 3.2 Neighboring extraction and degree centrality

Find out which vertex has the highest degree centrality value and extract its neighbors (note that two methods are possible for this, and the `sort` function will be useful).

```python
v <- sort(degree(karate), decreasing = T)
v <- v[1]
make_ego_graph(karate, nodes = v)
```
### 3.3 Edge betweenness and vertex names

Retrieve the names of the vertices which are connected to the edges having the highest edge betweenness value.

```R
id <- which.edgeBetweenness(karate) == max.edgeBetweenness(karate) )
E(karate)[id]
```

### 3.4 Betweenness centrality and vertex attribute

Compute the vertex betweenness centrality and store the values in a vertex attribute.

```R
V(karate)$betw <- betweenness(karate)
```

### 3.5 Closeness centrality, subgraph from vertices, indexing

Create a subgraph including the 5 vertices with highest closeness centrality value.

```R
closeness.values <- sort(closeness(karate), decreasing = T)
closeness.values <- closeness.values[1:5]
selected Vertices <- V(karate)[V(karate)$name %in% names(closeness.values)]
induced_subgraph(karate, selected Vertices)
```

### 3.6 Graph splitting, articulation points, density

Cut the graph at its articulation points and find which component has the highest density.

```R
g <- deleteVertices(karate, articulation_points(karate))
components(g)
components(g)$csize
```

### 3.7 Graphical representation, degree centralization

Create a graphical representation of the graph, printing its degree centralization in the graph title, and export it as a PDF file.

```R
plot(karate, main = centr_degree(karate)$centralization)

pdf("output.pdf")
  plot(karate, main = paste("Centralization:", centr_degree(karate)$centralization))
  dev.off()
```

### 3.8 Clustering methods, betweenness centralization

Apply a clustering method of your choice and measure the betweenness centralization of each cluster.

```R
V(karate)$member <- cluster_walktrap(karate)$membership
g1 <- induced_subgraph(karate, V(karate)$member == 1)
 centr_betw(g1)
g2 <- induced_subgraph(karate, V(karate)$member == 2)
 centr_betw(g2)
```
## 3.9 Graph splitting and union

Cut the graph at its articulation points and merge (i.e. union) the two largest components (i.e. in vertices count).

```r
g <- deleteVertices(karate, articulation_points(karate))
components(g)
V(g)$member <- components(g)$membership
g1 <- induced_subgraph(g, V(g)$member == 1)
g2 <- induced_subgraph(g, V(g)$member == 2)
union(g1, g2)
```

## 3.10 Comparing clustering methods

Choose two clustering methods; apply them, and measure the correlation between the two results (i.e. the cluster assigned to vertices).

```r
clustering1 <- cluster infomap(karate) $membership
clustering2 <- cluster leiden(karate) $membership
cor(clustering1, clustering2)
```

## R session information

* R version 4.5.0 (2025-04-11), x86_64-pc-linux-gnu

* Running under: Ubuntu 22.04.5 LTS

* Matrix products: default

* BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.10.0

* LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0

* Base packages: base, datasets, graphics, grDevices, methods, stats, utils

* Other packages: igraph 2.1.4, igraphdata 1.0.1, knitr 1.50

* Loaded via a namespace (and not attached): cli 3.6.5, compiler 4.5.0, evaluate 1.0.3, highr 0.11, lifecycle 1.0.4, magrittr 2.0.3, pkgconfig 2.0.3, rlang 1.1.6, rstudioapi 0.17.1, tools 4.5.0, xfun 0.52