1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
|
---
title: "igraph (interfaz R)"
output:
rmarkdown::html_vignette:
toc: true
toc_depth: 4
vignette: >
%\VignetteIndexEntry{igraph (interfaz R)}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
`igraph` es una biblioteca rápida y de código abierto para el análisis de grafos o redes. El núcleo de ésta libreria se encuentra escrito en C y contiene enlaces para lenguajes de alto nivel como [R](https://r.igraph.org/), [Python](https://python.igraph.org/), y [Mathematica](http://szhorvat.net/pelican/igraphm-a-mathematica-interface-for-igraph.html). Esta viñeta pretende darte una visión general de las funciones disponibles de `igraph` en R. Para obtener información detallada de cada función, consulta <https://r.igraph.org/reference/>.
------------------------------------------------------------------------
**NOTA:** A lo largo de este tutorial, utilizaremos las palabras `grafo` y `red` como sinónimos, y también `vértice` o `nodo` como sinónimos.
------------------------------------------------------------------------
## Instalación
Para instalar la librería desde CRAN, usa:
```{r echo = TRUE, eval = FALSE}
install.packages("igraph")
```
Encontrarás más información sobre dependencias, requisitos y resolución de problemas sobre la instalación en la [página principal](https://r.igraph.org/).
## Uso de igraph
Para utilizar `igraph` en tu código de R, primero debes cargar la biblioteca:
```{r echo = FALSE}
knitr::opts_chunk$set(fig.width=6, fig.height=6)
```
```{r setup}
library("igraph")
```
Ahora tienes todas las funciones de `igraph` disponibles.
## Crear un grafo
`igraph` ofrece muchas formas de crear un grafo. La más sencilla es con la función `make_empty_graph()`:
```{r}
g <- make_empty_graph()
```
La forma más común de crear un grafo es con `make_graph()`, que construye un grafo basado en especificar las aristas. Por ejemplo, Para hacer un grafo con 10 nodos (numerados `1` a `10`) y dos aristas que conecten los nodos `1-2` y `1-5`:
```{r}
g <- make_graph(edges = c(1,2, 1,5), n=10, directed = FALSE)
```
A partir de igraph 0.8.0, también puedes incluir literales mediante la notación de fórmulas de igraph. En este caso, el primer término de la fórmula tiene que empezar con un carácter `~`, como comúnmente se usa en las fórmulas en R. Las expresiones constan de los nombres de los vértices y los operadores de las aristas. El operador de un arista es una secuencia de caracteres `-` y `+`, el primero es para indicar propiamente las aristas y el segundo para las puntas de flecha (dirección). Puedes utilizar tantos caracteres `-` como quieras para "dibujarlas". Si todos los operadores de un arista están formados únicamente por caracteres `-`, el grafo será no dirigido, mientras que un único carácter `+` implica un grafo dirigido. Por ejemplo, para crear el mismo grafo que antes:
```{r echo = TRUE}
g <- make_graph(~ 1--2, 1--5, 3, 4, 5, 6, 7, 8, 9, 10)
```
Podemos imprimir el grafo para obtener un resumen de sus nodos y aristas:
```{r echo = TRUE}
g
```
Esto significa: grafo no dirigido (**U**ndirected) con **10** vértices y **2** aristas, que se enlistan en la última parte. Si el grafo tiene un atributo [nombre], también se imprime.
------------------------------------------------------------------------
**NOTA**: `summary()` no enlista las aristas, lo cual es conveniente para grafos grandes con millones de aristas:
------------------------------------------------------------------------
```{r echo = TRUE}
summary(g)
```
También `make_graph()` puede crear algunos grafos destacados con sólo especificar su nombre. Por ejemplo, puedes generar el grafo que muestra la red social del club de kárate de Zachary, que refleja la amistad entre los 34 miembros del club de una universidad de los Estados Unidos en la década de los 70s:
```{r echo = TRUE}
g <- make_graph("Zachary")
```
Para observar un grafo puedes utilizar `plot()`:
```{r}
plot(g)
```
Más adelante en este tutorial se ofrece una descripción detallada de las opciones para graficar un grafo.
## IDs de vértices y aristas
Los vértices y las aristas tienen un identificador numérico en igraph. Los ID de los vértices son siempre consecutivos y empiezan por 1. Para un grafo con "n" vértices, los ID de los vértices están siempre entre 1 y "n". Si alguna operación cambia el número de vértices en los grafos, por ejemplo, se crea un subgrafo mediante `induced_subgraph()`, entonces los vértices se vuelven a enumerar para satisfacer este criterio.
Lo mismo ocurre con las aristas: los ID de las aristas están siempre entre 1 y "m", el número total de aristas del grafo.
------------------------------------------------------------------------
**NOTA**: Si estás familiarizado con C o con la interfaz [Python](https://python.igraph.org/en/stable/) de `igraph`, te habrás dado cuenta de que en esos lenguajes los IDs de vértices y aristas empiezan por 0. En la interfaz de R, ambos empiezan por 1, para mantener la coherencia con la convención de cada lenguaje.
------------------------------------------------------------------------
Además de los IDs, a los vértices y aristas se les puede asignar un nombre y otros atributos. Esto facilita su seguimiento cada vez que se altera un grafo. Más adelante en este tutorial se muestran ejemplos de cómo alterar estas características.
## Añadir y borrar vértices y aristas
Sigamos trabajando con el grafo del club de kárate. Para añadir uno o más vértices a un grafo existente, utiliza `add_vertices()`:
```{r}
g <- add_vertices(g, 3)
```
Del mismo modo, para añadir aristas puedes utilizar `add_edges()`:
```{r}
g <- add_edges(g, edges = c(1,35, 1,36, 34,37))
```
Las aristas se añaden especificando el ID del vértice origen y el vértice destino de cada arista. Con las instrucciones anteriores se añaden tres aristas, una que conecta los vértices `1` y `35`, otra que conecta los vértices `1` y `36` y otra que conecta los vértices `34` y `37`.
Además de las funciones `add_vertices()` y `add_edges()`, se puede utilizar el operador "+" para añadir vértices o aristas al grafo. La operación que se realice dependerá del tipo de argumento del lado derecho:
```{r echo = TRUE, eval=FALSE}
g <- g + edges(c(1,35, 1,36, 34,37))
```
Puedes añadir un solo vértice/arista a la vez usando `add_vertex()` y `add_edge()` (singular).
**Advertencia**: Si necesitas añadir múltiples aristas a un grafo, es mucho más eficiente usar `add_edges()` una vez que utilizar repetidamente `add_edge()` con una nueva arista a la vez. Lo mismo ocurre al eliminar aristas y vértices.
Si intentas añadir aristas a vértices con IDs no válidos (por ejemplo, intentas añadir una arista al vértice `38` cuando el grafo sólo tiene 37 vértices), `igraph` muestra un error:
```{r echo = TRUE, error = TRUE}
g <- add_edges(g, edges = c(38, 37))
```
Añadamos más vértices y aristas a nuestro grafo. En `igraph` podemos utilizar el paquete `magrittr`, que proporciona un mecanismo para encadenar comandos con el operador `%>%`:
```{r echo = TRUE}
g <- g %>%
add_edges(edges = c(1, 34)) %>%
add_vertices(3) %>%
add_edges(edges = c(38, 39, 39, 40, 40, 38, 40, 37))
g
```
Ahora tenemos un grafo no dirigido con 40 vértices y 89 aristas. Los IDs de los vértices y aristas son siempre *contiguos*, así que si borras un vértice, todos los vértices subsecuentes se vuelven a enumerar. Cuando se re-numera un vértice, las aristas **no** se vuelven a enumerar, pero sí sus vértices origen y destino. Puedes usar `delete_vertices()` y `delete_edges()` para realizar estas operaciones. Por ejemplo, para borrar la arista que conecta los vértices `1-34`, obtén su ID y luego bórrala:
```{r echo = TRUE}
edge_id_para_borrar <- get_edge_ids(g, c(1,34))
edge_id_para_borrar
```
```{r}
g <- delete_edges(g, edge_id_para_borrar)
```
Por ejemplo, para crear un grafo con forma de anillo y para partirlo:
```{r echo = TRUE}
g <- make_ring(10) %>% delete_edges("10|1")
plot(g)
```
El ejemplo anterior muestra que también puedes referirte a las aristas indicando los IDs de los vértices origen y destino, conectados por el símbolo `|`. En el ejemplo, `"10|1"` significa la arista que conecta el vértice `10` con el vértice `1`. Por supuesto, también puedes usar los IDs de las aristas directamente, o recuperarlos con la función `get_edge_ids()`:
```{r echo = TRUE}
g <- make_ring(5)
g <- delete_edges(g, get_edge_ids(g, c(1,5, 4,5)))
plot(g)
```
Veamos otro ejemplo, hagamos un grafo cordal. Recuerda que un grafo es cordal (o triangulado) si cada uno de sus ciclos de cuatro o más nodos tienen una "cuerda", que es una arista que une dos nodos que no son adyacentes en el ciclo. En primer lugar, vamos a crear el grafo inicial utilizando `graph_from_literal()`:
```{r}
g1 <- graph_from_literal(
A-B:C:I,
B-A:C:D,
C-A:B:E:H,
D-B:E:F,
E-C:D:F:H,
F-D:E:G,
G-F:H,
H-C:E:G:I,
I-A:H
)
plot(g1)
```
En este ejemplo, se ha utilizado el operador `:` para definir conjuntos de vértices. Si el operador de un arista conecta dos conjuntos de vértices, entonces cada vértice del primer conjunto estará conectado a cada vértice del segundo conjunto. A continuación utilizamos `is_chordal()` para evaluar si nuestro grafo es cordal y buscar qué aristas faltan para rellenar el grafo:
```{r echo = TRUE}
is_chordal(g1, fillin=TRUE)
```
Luego, en una sola línea podemos añadir las aristas necesarias para que el grafo inicial sea cordal:
```{r echo = TRUE}
chordal_graph <- add_edges(g1, is_chordal(g1, fillin=TRUE)$fillin)
plot(chordal_graph)
```
## Construcción de grafos
Además de `make_empty_graph()`, `make_graph()` y `make_graph_from_literal()`, `igraph` incluye muchas otras funciones para construir un grafo. Algunas son *deterministas*, es decir, producen el mismo grafo cada vez, por ejemplo `make_tree()`:
```{r echo = TRUE}
graph1 <- make_tree(127, 2, mode = "undirected")
summary(g)
```
Esto genera un grafo regular en forma de árbol con 127 vértices, cada vértice con dos hijos. No importa cuántas veces llames a `make_tree()`, el grafo generado será siempre el mismo si utilizas los mismos parámetros:
```{r}
graph2 <- make_tree(127, 2, mode = "undirected")
```
```{r echo = TRUE}
identical_graphs(graph1, graph2)
```
Otras funciones son *estocásticas*, lo cual quiere decir que producen un grafo diferente cada vez; por ejemplo, `sample_grg()`:
```{r echo = TRUE}
graph1 <- sample_grg(100, 0.2)
summary(graph1)
```
Esto genera un grafo geométrico aleatorio: Se eligen *n* puntos de forma aleatoria y uniforme dentro del espacio métrico, y los pares de puntos más cercanos entre sí respecto a una distancia predeterminada *d* se conectan mediante una arista. Si se generan GRGs con los mismos parámetros, serán diferentes:
```{r echo = TRUE}
graph2 <- sample_grg(100, 0.2)
identical_graphs(graph1, graph2)
```
Una forma un poco más relajada de comprobar si los grafos son equivalentes es mediante `isomorphic()`. Se dice que dos grafos son isomorfos si tienen el mismo número de componentes (vértices y aristas) y mantienen una correspondencia uno a uno entre vértices y aristas, es decir, están conectados de la misma manera:
```{r echo = TRUE}
isomorphic(graph1, graph2)
```
Comprobar el isomorfismo puede llevar un tiempo en el caso de grafos grandes (en este caso, la respuesta puede darse rápidamente comprobando la secuencia de grados de los dos grafos). `identical_graph()` es un criterio más estricto que `isomorphic()`: los dos grafos deben tener la misma lista de vértices y aristas, exactamente en el mismo orden, con la misma direccionalidad, y los dos grafos también deben tener idénticos atributos de grafo, vértice y arista.
## Establecer y recuperar atributos
Además de los IDs, los vértices y aristas pueden tener *atributos* como un nombre, coordenadas para graficar, metadatos y pesos. El propio grafo también puede tener estos atributos (por ejemplo, un nombre, que se mostrará en `summary`). En cierto sentido, cada grafo, vértice y arista puede ser utilizado como un espacio de nombres en R para almacenar y recuperar estos atributos.
Para demostrar el uso de los atributos, creemos una red social sencilla:
```{r}
g <- make_graph(
~ Alice-Boris:Himari:Moshe,
Himari-Alice:Nang:Moshe:Samira,
Ibrahim-Nang:Moshe,
Nang-Samira
)
```
Cada vértice representa a una persona, por lo que queremos almacenar sus edades, géneros y el tipo de conexión entre dos personas (`is_formal()` se refiere a si una conexión entre una persona y otra es formal o informal, es decir, colegas o amigos). El operador `$` es un atajo para obtener y establecer atributos de un grafo. Es más corto y tan legible como `graph_attr()` y `set_graph_attr()`.
```{r echo = TRUE}
V(g)$age <- c(25, 31, 18, 23, 47, 22, 50)
V(g)$gender <- c("f", "m", "f", "m", "m", "f", "m")
E(g)$is_formal <- c(FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE)
summary(g)
```
`V` y `E` son la forma estándar de obtener una secuencia de todos los vértices y aristas respectivamente. Esto asigna un atributo a *todos* los vértices/aristas a la vez. Otra forma de generar nuestra red social es con el uso de `set_vertex_attr()` y `set_edge_attr()` y el operador `%>%`:
```{r echo = TRUE, eval=FALSE}
g <- make_graph(
~ Alice-Boris:Himari:Moshe,
Himari-Alice:Nang:Moshe:Samira,
Ibrahim-Nang:Moshe,
Nang-Samira
) %>%
set_vertex_attr("age", value = c(25, 31, 18, 23, 47, 22, 50)) %>%
set_vertex_attr("gender", value = c("f", "m", "f", "m", "m", "f", "m")) %>%
set_edge_attr("is_formal", value = c(FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE))
summary(g)
```
Para asignar o modificar un atributo a un único vértice/arista:
```{r echo = TRUE}
E(g)$is_formal
E(g)$is_formal[1] <- TRUE
E(g)$is_formal
```
Los valores de los atributos pueden establecerse en cualquier objeto de R, pero ten en cuenta que almacenar el grafo en algunos formatos puede provocar la pérdida de valores en atributos complejos. Los vértices, las aristas y el propio grafo pueden utilizarse para establecer atributos, por ejemplo, para añadir una fecha al grafo:
```{r echo = TRUE}
g$date <- c("2022-02-11")
graph_attr(g, "date")
```
Para recuperar atributos, también puedes utilizar `graph_attr()`, `vertex_attr()` y `edge_attr()`. Para encontrar el ID de un vértice puedes utilizar la función `match()`:
```{r echo = TRUE}
match(c("Ibrahim"), V(g)$name)
```
Para asignar atributos a un subconjunto de vértices o aristas, puedes utilizar:
```{r echo = TRUE}
V(g)$name[1:3] <- c("Alejandra", "Bruno", "Carmina")
V(g)
```
Para eliminar atributos:
```{r echo = TRUE}
g <- delete_vertex_attr(g, "gender")
V(g)$gender
```
Si quieres guardar un grafo en R con todos los atributos utiliza la función estándar de R `dput` y recupéralo más tarde con `dget`. También puedes simplemente guardar el espacio de trabajo de R y restaurarlo más tarde.
## Propiedades estructurales de los grafos
igraph proporciona un amplio conjunto de métodos para calcular varias propiedades estructurales de los grafos. Está más allá del alcance de este tutorial documentar todos ellos, por lo que esta sección sólo presentará algunos de ellos con fines ilustrativos. Trabajaremos con la pequeña red social que construimos en la sección anterior.
Probablemente, la propiedad más sencilla en la que se puede pensar es el "grado del vértice". El grado de un vértice es igual al número de aristas incidentes a él. En el caso de los grafos dirigidos, también podemos definir el `grado de entrada` (el número de aristas que apuntan hacia el vértice) y el `grado de salida` (el número de aristas que se originan en el vértice). igraph es capaz de calcularlos todos utilizando una sintaxis sencilla:
```{r echo = TRUE}
degree(g)
```
Si el grafo fuera dirigido, podríamos calcular los grados de entrada y salida por separado utilizando `degree(mode = "in")` y `degree(mode = "out")`. También puedes pasar un único ID de un vértice o una lista de IDs de los vértices a `degree()` si quieres calcular los grados sólo para un subconjunto de vértices:
```{r echo = TRUE}
degree(g, 7)
```
```{r echo = TRUE}
degree(g, v = c(3,4,5))
```
La mayoría de las funciones que aceptan los IDs de los vértices también aceptan los "nombres" de los vértices (es decir, los valores del atributo `name` del vértice) siempre que los nombres sean únicos:
```{r echo = TRUE}
degree(g, v = c("Carmina", "Moshe", "Nang"))
```
También funciona para vértices individuales:
```{r echo = TRUE}
degree(g, "Bruno")
```
De igual manera, se utiliza una sintaxis similar para la mayoría de las propiedades estructurales que igraph puede calcular. Para las propiedades de los vértices, las funciones aceptan un ID, un nombre o una lista de IDs o nombres (y si se omiten, el valor predeterminado es el conjunto de todos los vértices). Para las propiedades de aristas, las funciones aceptan un único ID o una lista de IDs.
------------------------------------------------------------------------
**NOTA:** Para algunas mediciones, no tiene sentido calcularlas sólo para unos pocos vértices o aristas en lugar de para todo el grafo, ya que de todas formas llevaría el mismo tiempo. En este caso, las funciones no aceptan IDs de vértices o aristas, pero se puede restringir la lista resultante utilizando operaciones estándar. Un ejemplo es la centralidad de vectores propios (`evcent()`).
------------------------------------------------------------------------
Además del grado, igraph incluye funciones integradas para calcular muchas otras propiedades de centralidad, como la intermediación de vértices y aristas (`edge_betweenness()`) o el PageRank de Google (`page_rank()`), por nombrar algunas. Aquí sólo ilustraremos la intermediación de aristas:
```{r echo = TRUE}
edge_betweenness(g)
```
De este modo, ahora también podemos averiguar qué conexiones tienen la mayor centralidad de intermediación:
```{r echo = TRUE}
ebs <- edge_betweenness(g)
as_edgelist(g)[ebs == max(ebs), ]
```
## Búsqueda de vértices y aristas basada en atributos
### Selección de vértices
Tomando como ejemplo la red social anteriormente creada, te gustaría averiguar quién tiene el mayor grado. Puedes hacerlo con las herramientas presentadas hasta ahora y con la función `which.max()`:
```{r echo = TRUE}
which.max(degree(g))
```
Otro ejemplo sería seleccionar sólo los vértices que tienen IDs impares, utilizando la función `V()`:
```{r echo = TRUE}
graph <- graph.full(n=10)
only_odd_vertices <- which(V(graph)%%2==1)
length(only_odd_vertices)
```
Por supuesto, es posible seleccionar vértices o aristas mediante índices posicionales:
```{r echo = TRUE}
seq <- V(graph)[2, 3, 7]
seq
```
```{r echo = TRUE}
seq <- seq[1, 3] # filtrar un conjunto de vértices existente
seq
```
Al seleccionar un vértice que no existe se produce un error:
```{r echo = TRUE, error = TRUE}
seq <- V(graph)[2, 3, 7, "foo", 3.5]
```
Los nombres de los atributos también pueden utilizarse tal cual dentro de los operadores de indexación ("[]") de `V()` y `E()`. Esto puede combinarse con la capacidad de R de utilizar vectores booleanos para indexar y obtener expresiones muy concisas y legibles para recuperar un subconjunto del set de vértices o aristas de un grafo. Por ejemplo, el siguiente comando nos da los nombres de los individuos menores de 30 años de nuestra red social:
```{r echo = TRUE}
V(g)[age < 30]$name
```
Por supuesto, `<` no es el único operador booleano que puede utilizarse para esto. Otras posibilidades son las siguientes:
| Operador | Significado |
|----------|---------------------------------------------------------------|
| `==` | El valor del atributo/propiedad debe ser *igual* a |
| `!=` | El valor del atributo/propiedad debe *no ser igual* a |
| `<` | El valor del atributo/propiedad debe ser *menos* que |
| `<=` | El valor del atributo/propiedad debe ser *inferior o igual a* |
| `>` | El valor del atributo/propiedad debe ser *mayor que* |
| `>=` | El valor del atributo/propiedad debe ser *mayor o igual a* |
| `%in%` | El valor del atributo/propiedad debe estar *incluido en* |
También puede crear un operador "no incluido en" a partir de `%in%` utilizando el operador `Negate`:
```{r echo = TRUE}
`%notin%` <- Negate(`%in%`)
```
Si un atributo tiene el mismo nombre que una función de igraph, debes tener cuidado ya que la sintaxis puede llegar a ser un poco confusa. Por ejemplo, si hay un atributo llamado `degree` que representa las notas de un examen para cada persona, no debe confundirse con la función de igraph que calcula los grados de los vértices de una red:
```{r echo = TRUE}
V(g)$degree <- c("A", "B", "B+", "A+", "C", "A", "B")
V(g)$degree[degree(g) == 3]
```
```{r echo = TRUE}
V(g)$name[degree(g) == 3]
```
### Selección de aristas
Las aristas pueden seleccionarse basándose en atributos, igual que los vértices. Como ya se ha mencionado, la forma estándar de obtener aristas es `E`. Además, existen algunas propiedades estructurales especiales para seleccionar aristas.
El uso de `.from()` permite filtrar la serie de aristas desde los vértices de donde proceden. Por ejemplo, para seleccionar todas las aristas procedentes de Carmina (cuyo ID de vértice es el 3):
```{r echo = TRUE, warning = FALSE}
E(g)[.from(3)]
```
Por supuesto, también funciona con nombres de vértices:
```{r echo = TRUE, warning = FALSE}
E(g)[.from("Carmina")]
```
Al usar `.to()`, se filtran la serie de aristas en función de los vértices de destino o diana. Esto es diferente de `.from()` si el grafo es dirigido, mientras que da la misma respuesta para grafos no dirigidos. Con `.inc()` sólo se seleccionan las aristas que inciden en un único vértice o en al menos uno de los vértices, independientemente de la dirección de las aristas.
La expresión `%--%` es un operador especial que puede utilizarse para seleccionar todas las aristas entre dos conjuntos de vértices. Ignora las direcciones de las aristas en los grafos dirigidos. Por ejemplo, la siguiente expresión selecciona todas las aristas entre Carmina (su ID de vértice es el 3), Nang (su ID de vértice es el 5) y Samira (su ID de vértice es el 6):
```{r echo = TRUE}
E(g) [ 3:5 %--% 5:6 ]
```
Para que el operador `%--%` funcione con nombres, puedes construir vectores de caracteres que contengan los nombres y luego utilizar estos vectores como operandos. Por ejemplo, para seleccionar todas las aristas que conectan a los hombres con las mujeres, podemos hacer lo siguiente, luego de volver a añadir el atributo de género que hemos eliminado anteriormente:
```{r}
V(g)$gender <- c("f", "m", "f", "m", "m", "f", "m")
```
```{r echo = TRUE}
men <- V(g)[gender == "m"]$name
men
```
```{r echo = TRUE}
women <- V(g)[gender == "f"]$name
women
```
```{r echo = TRUE}
E(g)[men %--% women]
```
## Tratar un grafo como una matriz de adyacencia
Una matriz de adyacencia es otra manera de representar un grafo. En la matriz de adyacencia, las filas y columnas están indicadas por los vértices del grafo y los elementos de la matriz indican el número de aristas entre los vértices *i* y *j*. La matriz de adyacencia del grafo de nuestra red social imaginaria es:
```{r echo = TRUE}
as_adjacency_matrix(g)
```
Por ejemplo, Carmina (`1, 0, 0, 1, 1, 1, 0`) está directamente conectada con Alejandra (que tiene el índice 1), Moshe (índice 4), Nang (índice 5), Samira (índice 6) y , pero no con Bruno (índice 2) ni con Ibrahim (índice 7).
## Diseños y graficación
Un grafo es un objeto matemático abstracto sin una representación específica en el espacio 2D, 3D o cualquier espacio geométrico. Esto significa que, cuando queremos visualizar un grafo, primero tenemos que encontrar una correspondencia entre los vértices y las coordenadas en un espacio bidimensional o tridimensional, preferiblemente de una manera útil y/o agradable a la vista. Una rama separada de la teoría de grafos, denominada dibujo de grafos, trata de resolver este problema mediante varios algoritmos de diseño de grafos. igraph implementa varios algoritmos de diseño y también es capaz de dibujarlos en la pantalla o en cualquier formato de salida que soporte el propio R.
### Algoritmos de diseño
Las funciones de diseño en igraph siempre empiezan por `layout`. La siguiente tabla las resume:
| Nombre del método | Descripción del algoritmo |
|-----------------|-------------------------------------------------------|
| `layout_randomly` | Coloca los vértices de forma totalmente aleatoria |
| `layout_in_circle` | Disposición determinista que coloca los vértices en un círculo |
| `layout_on_sphere` | Disposición determinista que coloca los vértices de manera uniforme en la superficie de una esfera |
| `layout_with_drl` | El algoritmo DRL (*Distributed Recursive Layout*) para grafos grandes |
| `layout_with_fr` | El algoritmo dirigido Fruchterman-Reingold |
| `layout_with_kk` | El algoritmo dirigido Kamada-Kawai |
| `layout_with_lgl` | El algoritmo LGL (*Large Graph Layout*) para grafos grandes |
| `layout_as_tree` | Diseño de árbol de Reingold-Tilford, útil para grafos (casi) arbóreos |
| `layout_nicely` | Algoritmo de diseño que elige automáticamente uno de los otros algoritmos en función de determinadas propiedades del grafo |
Los algoritmos de diseño pueden ejecutarse directamente con un grafo como primer argumento. Devolverán una matriz con dos columnas y tantas filas como número de vértices del grafo; cada fila corresponderá a la posición de un único vértice, ordenado según el ID del vértice. Algunos algoritmos tienen una variante 3D; en este caso devuelven tres columnas en lugar de 2.
```{r}
layout <- layout_with_kk(g)
```
Algunos algoritmos de diseño toman argumentos adicionales; por ejemplo, cuando se diseña un grafo con la forma de un árbol, puede tener sentido especificar qué vértice debe colocarse en la raíz del diseño:
```{r}
layout <- layout_as_tree(g, root = 2)
```
### Dibujar un grafo utilizando un diseño
Podemos trazar nuestra red social imaginaria con el algoritmo de diseño Kamada-Kawai de la siguiente manera:
```{r}
layout <- layout_with_kk(g)
```
```{r}
plot(g, layout = layout, main = "Red social con el algoritmo de diseño Kamada-Kawai")
```
Esto debería abrir una nueva ventana mostrando una representación visual de la red. Recuerda que la ubicación exacta de los nodos puede ser diferente en tu máquina, ya que la disposición no es determinista.
El argumento `layout` también acepta funciones; en este caso, la función será llamada con el grafo como su primer argumento. Esto permite ingresar directamente el nombre de una función de diseño, sin tener que crear una variable de diseño, como en el ejemplo anterior:
```{r}
plot(
g,
layout = layout_with_fr,
main = "Red social con el algoritmo de disposición Fruchterman-Reingold"
)
```
Para mejorar el aspecto visual, una adición trivial sería colorear los vértices según el género. También deberíamos intentar colocar los nombres ligeramente fuera de los vértices para mejorar la legibilidad:
```{r}
V(g)$color <- ifelse(V(g)$gender == "m", "yellow", "red")
plot(
g,
layout = layout,
vertex.label.dist = 3.5,
main = "Red social - con los géneros como colores"
)
```
También puedes tratar el atributo `gender` como un factor y proporcionar los colores como un argumento a `plot()`, que tiene prioridad sobre el atributo `color` que se asigna de manera estándar a los vértices. Los colores se asignan automáticamente:
```{r}
plot(
g,
layout = layout,
vertex.label.dist = 3.5,
vertex.color = as.factor(V(g)$gender))
```
Como se vio anteriormente, con el argumento `vertex.color` puedes especificar propiedades visuales para `plot` en lugar de usar y/o manipular los atributos de vértices o aristas. El siguiente gráfico muestra las relaciones formales con líneas gruesas y las informales con líneas finas:
```{r}
plot(
g,
layout = layout,
vertex.label.dist = 3.5,
vertex.size = 20,
vertex.color = ifelse(V(g)$gender == "m", "yellow", "red"),
edge.width = ifelse(E(g)$is_formal, 5, 1)
)
```
Este último procedimiento es preferible si quieres modificar la representación visual de tu grafo, pero no quieres hacer modificaciones al grafo mismo.
En resumen, hay propiedades especiales de vértices y aristas que corresponden a la representación visual del grafo. Estos atributos pueden modificar la configuración predeterminada de igraph (es decir, color, peso, nombre, forma, diseño, etc.). Las dos tablas siguientes resumen los atributos visuales más utilizados para vértices y aristas, respectivamente:
### Atributos de los vértices para graficar
| Nombre del atributo | Argumento | Propósito |
|-----------------|-----------------|---------------------------------------|
| `color` | `vertex.color` | Color del vértice |
| `label` | `vertex.label` | Etiqueta del vértice. Se convertirán en caracteres. Especifique NA para omitir las etiquetas de los vértices. Las etiquetas de vértices por defecto son los IDs de los vértices. |
| `label.cex` | `vertex.label.cex` | Tamaño de fuente de la etiqueta del vértice, interpretado como un factor multiplicativo, de forma similar a la función `text` de R |
| `label.color` | `vertex.label.color` | Color de la etiqueta del vértice |
| `label.degree` | `vertex.label.degree` | Define la posición de las etiquetas de los vértices, en relación con el centro de los mismos. Se interpreta como un ángulo en radianes, cero significa 'a la derecha', y 'pi' significa a la izquierda, arriba es -pi/2 y abajo es pi/2. El valor por defecto es -pi/4 |
| `label.dist` | `vertex.label.dist` | Distancia de la etiqueta del vértice desde el propio vértice, en relación con el tamaño del vértice |
| `label.family` | `vertex.label.family` | Familia tipográfica del vértice, de forma similar a la función `text` de R |
| `label.font` | `vertex.label.font` | Fuente dentro de la familia de fuentes del vértice, de forma similar a la función `text` de R |
| `shape` | `vertex.shape` | La forma del vértice, actualmente "circle", "square", "csquare", "rectangle", "crectangle", "vrectangle", "pie" (consultar `vertex.shape.pie`), 'sphere' y "none" son admitidos, y sólo por el comando `plot.igraph` |
| `size` | `vertex.size` | El tamaño del vértice, un escalar numérico o vector, en este último caso el tamaño de cada vértice puede ser diferente |
### Atributos de las aristas para graficar
| Nombre del atributo | Argumento | Propósito |
|-------------------------|-----------------------------|------------------|
| `color` | `edge.color` | Color de la arista |
| `curved` | `edge.curved` | Un valor numérico especifica la curvatura de la arista; una curvatura cero significa aristas rectas, valores negativos significan que la arista se curva en el sentido de las agujas del reloj, valores positivos lo contrario. TRUE significa curvatura 0.5, FALSE significa curvatura cero |
| `arrow.size` | `edge.arrow.size` | Actualmente es una constante, por lo que es la misma para todas las aristas. Si se presenta un vector, sólo se utiliza el primer elemento, es decir, si se toma de un atributo de aristas, sólo se utiliza el atributo de la primera arista para todas las flechas |
| `arrow.width` | `edge.arrow.width` | El ancho de las flechas. Actualmente es una constante, por lo que es la misma para todas las aristas |
| `width` | `edge.width` | Anchura del borde en píxeles |
| `label` | `edge.label` | Si se especifica, añade una etiqueta al borde |
| `label.cex` | `edge.label.cex` | Tamaño de fuente de la etiqueta de la arista, interpretado como un factor multiplicativo, de forma similar a la función `text` de R |
| `label.color` | `edge.label.color` | Color de la etiqueta de la arista |
| `label.family` | `edge.label.family` | Familia tipográfica de la arista, de forma similar a la función `text` de R |
| `label.font` | `edge.label.font` | Fuente dentro de la familia de fuentes de la arista, de forma similar a la función `text` de R |
### Argumentos más comunes de `plot()`
Estos parámetros pueden especificarse como argumentos de la función `plot` para ajustar el aspecto general del gráfico.
| Argumento | Propósito |
|--------------------------------|----------------------------------------|
| `layout` | El diseño que se va a utilizar. Puede ser una instancia de `layout`, una lista de tuplas que contengan coordenadas X-Y, o el nombre de un algoritmo de diseño. El valor por defecto es `auto`, que selecciona un algoritmo de diseño automáticamente basado en el tamaño y la conectividad del grafo. |
| `margin` | La cantidad de espacio vacío debajo, encima, a la izquierda y a la derecha del gráfico, es un vector numérico de longitud cuatro |
## igraph y el mundo exterior
Ningún módulo de grafos estaría completo sin algún tipo de funcionalidad de importación/exportación que permita al paquete comunicarse con programas y kits de herramientas externos. igraph no es una excepción: proporciona funciones para leer los formatos de grafos más comunes y para guardar grafos en archivos que obedezcan estas especificaciones de formato. Las funciones principales para leer y escribir de/a un fichero son `read_graph()` y `write_graph()`, respectivamente. La siguiente tabla resume los formatos que igraph puede leer o escribir:
| Formato | Nombre corto | Método de lectura | Método de escritura |
|-----------------|-----------------|-------------------|-------------------|
| Lista de adyacencia (a.k.a. [LGL](https://lgl.sourceforge.net/#FileFormat)) | `lgl` | `read_graph(file, format = c("lgl"))` | `write_graph(graph, file, format = c("lgl"))` |
| Matriz de adyacencia | `adjacency` | `graph_from_adjacency_matrix(adjmatrix, mode = c("directed", "undirected", "max", "min", "upper","lower", "plus"), weighted = NULL, diag = TRUE, add.colnames = NULL, add.rownames = NA)` | `as.matrix(graph, "adjacency")` |
| DIMACS | `dimacs` | `read_graph(file, format = c("dimacs"))` | `write_graph(graph, file, format = c("dimacs"))` |
| Edge list | `edgelist` | `read_graph(file, format = c("edgelist"))` | `write_graph(graph, file, format = c("edgelist"))` |
| [GraphViz](https://www.graphviz.org) | `dot` | not supported yet | `write_graph(graph, file, format = c("dot"))` |
| GML | `gml` | `read_graph(file, format = c("gml"))` | `write_graph(graph, file, format = c("gml"))` |
| GraphML | `graphml` | `read_graph(file, format = c("graphml"))` | `write_graph(graph, file, format = c("graphml"))` |
| LEDA | `leda` | not supported yet | `write_graph(graph, file, format = c("leda"))` |
| Labeled edgelist (a.k.a. [NCOL](https://lgl.sourceforge.net/#FileFormat)) | `ncol` | `read_graph(file, format = c("ncol"))` | `write_graph(graph, file, format = c("ncol"))` |
| [Pajek](http://mrvar.fdv.uni-lj.si/pajek/) format | `pajek` | `read_graph(file, format = c("pajek"))` | `write_graph(graph, file, format = c("pajek"))` |
------------------------------------------------------------------------
**NOTA:** La mayoría de los formatos tienen sus propias limitaciones; por ejemplo, no todos pueden almacenar atributos. Tu mejor opción es probablemente GraphML o GML si quieres guardar los grafos de igraph en un formato que pueda ser leído desde un paquete externo y quieres preservar los atributos numéricos y de cadena. *Edge list* y NCOL también están bien si no tienes atributos (aunque NCOL admite nombres de vértices y pesos de aristas).
------------------------------------------------------------------------
## Dónde ir a continuación
Este tutorial es una breve introducción a `igraph` en R. Esperamos que hayas disfrutado de su lectura y que te resulte útil para tus propios análisis de redes.
Para una descripción detallada de funciones específicas, consulta <https://r.igraph.org/reference/>. Si tienes preguntas sobre cómo utilizar `igraph`, visita nuestro [Foro](https://igraph.discourse.group). Para informar de un error, abre una [incidencia en Github](https://github.com/igraph/rigraph/issues). Por favor, no hagas preguntas de uso en Github directamente, ya que está pensado para desarrolladores y no para usuarios.
## Información de la sesión
En favor de la reproducibilidad, la información de la sesión para el código anterior es la siguiente:
```{r session-info}
sessionInfo()
```
|