10  Análisis de Texto y Redes Semánticas

Hasta ahora, en el Análisis de Redes Sociales, nos hemos concentrado en la topología: quién se conecta con quién, qué comunidad es más cohesionada o qué nodo es el cuello de botella estructural.

Sin embargo, en las redes sociales digitales (Twitter, Reddit, foros), los enlaces no son simples líneas mudas. Los nodos se comunican intercambiando mensajes. La estructura de la red nos dice por dónde fluye la información, pero el Procesamiento de Lenguaje Natural (NLP) nos dice qué información está fluyendo y con qué emoción se transmite.

Este capítulo es el puente donde la minería de texto y la teoría de grafos se cruzan. Vamos a aprender a extraer, limpiar y modelar el contenido de los mensajes para entender no solo las conexiones, sino las narrativas y polarizaciones detrás de ellas.


10.1 1. El Desafío: Preprocesamiento de Lenguaje no Estructurado

Antes de modelar, los datos deben ser normalizados. En redes sociales, esto representa un desafío técnico importante por el uso de jerga, variaciones ortográficas y elementos no textuales.

  1. Extracción de datos: Usualmente se realiza mediante APIs (paquetes como rtweet para X/Twitter o academictwitteR).
  2. Limpieza Pro: Implica manejar la codificación (UTF-8 es vital en español), y decidir qué hacer con las menciones (@usuario) o URLs.
  3. Tokenización Adaptada: A diferencia del análisis de textos legales, aquí los #hashtags y los emojis (convertidos a texto como [cara_feliz]) tienen un peso semántico vital que no debe borrarse. Paquetes como tidytext o quanteda son el estándar en R para este proceso.

10.2 2. El color de la emoción: Análisis de Sentimiento

Para aterrizar estos conceptos, vamos a utilizar un caso de estudio real y de máxima actualidad: El problema de la vivienda en España. A lo largo de todo este capítulo, usaremos este ejemplo para extraer conclusiones sobre cómo se posicionan los diferentes actores (gobierno, sindicatos, propietarios y oposición) y qué narrativa defiende cada uno.

El análisis de sentimiento clásico busca clasificar un texto según su carga emocional. Antes de pasar a modelos complejos, debemos entender cómo se calcula el “clima” de una red.

10.2.1 ¿Cómo funciona un Lexicón?

La forma más sencilla es usar un diccionario de sentimientos. Cada palabra tiene un valor (ej: “justicia” = +2, “crisis” = -3). El sentimiento del post es la suma de los valores de sus palabras.

En español, usamos léxicos adaptados como AFINN o el lexicón de emociones NRC [@nrc2013].

library(tidyverse)
Warning: package 'ggplot2' was built under R version 4.5.2
Warning: package 'tibble' was built under R version 4.5.2
Warning: package 'tidyr' was built under R version 4.5.2
Warning: package 'readr' was built under R version 4.5.2
Warning: package 'purrr' was built under R version 4.5.2
Warning: package 'dplyr' was built under R version 4.5.2
Warning: package 'lubridate' was built under R version 4.5.2
library(tidytext)

# Diccionario manual ampliado para el ejemplo
lexicon_esp <- tibble(
  word = c("esperanza", "victoria", "histórica", "garantiza", "futuro", "digno", "protege", "hogar", "justicia", "social", "derechos", "inseguridad", "intervencionismo", "destruyendo", "crisis", "pobreza", "drama", "fracaso", "okupación", "castiga", "ideológico", "especulación", "abusiva"),
  valor = c(4, 3, 2, 2, 1, 2, 2, 2, 2, 1, 1, -3, -2, -4, -3, -3, -3, -4, -3, -2, -1, -2, -3)
)



# Ejemplo con frases reales y polarizadas
ejemplos <- tibble(
  id = 1:3,
  texto = c(
    "Esta ley es la única esperanza para nuestros jóvenes; una victoria histórica que garantiza un futuro digno, protege el acceso al hogar y acaba con la especulación abusiva.",
    "El intervencionismo y la inseguridad jurídica están destruyendo el mercado inmobiliario, provocando una crisis de oferta sin precedentes.",
    "La ley del gobierno es un fracaso absoluto que protege la okupación y castiga a los pequeños propietarios con un intervencionismo ideológico."
  )
)

# Calculamos sentimiento
resultado_sentimiento <- ejemplos %>%
  unnest_tokens(word, texto) %>%
  inner_join(lexicon_esp) %>%
  group_by(id) %>%
  summarise(puntuacion = sum(valor))

resultado_sentimiento
# A tibble: 3 × 2
     id puntuacion
  <int>      <dbl>
1     1         13
2     2        -12
3     3        -10

10.2.2 ¿Qué significa este resultado?

El análisis nos devuelve una métrica cuantitativa del “clima” emocional:

  1. El Signo (+/-): Indica la polaridad. Un valor positivo (como el de la frase 1) indica apoyo, alegría o éxito. Un valor negativo (como el de la frase 2) indica rechazo, enfado o crítica.
  2. La Magnitud: Indica la intensidad. No es lo mismo un -1 (una leve queja) que un -10 (un ataque furibundo). En el ejemplo, la frase 2 es la más intensa negativamente (-12) porque acumula palabras con mucha carga como “destruyendo” o “inseguridad”.
  3. Casos Mixtos: La frase 3 combina elementos positivos (“victoria”) con negativos (“crisis”, “pobreza”, “inseguridad”). El resultado final es la suma neta, lo que nos permite ver qué sentimiento predomina en discursos complejos.

10.3 3. Descifrando las Narrativas: Modelado de Tópicos (Topic Modeling)

Si tienes 100.000 mensajes, no puedes leerlos todos. Necesitamos algoritmos que agrupen las palabras en “temas”.

10.3.1 De LDA a STM

Durante años, la técnica estándar ha sido LDA (Latent Dirichlet Allocation). > ¿Qué hace LDA? Imagina que cada documento es una mezcla de varios temas (ej. 60% Política, 40% Economía) y cada tema es una mezcla de palabras con diferentes probabilidades. LDA intenta descubrir qué palabras “suelen ir juntas” en los documentos para definir esos temas de forma automática (no supervisada).

Sin embargo, para redes complejas usaremos el estado del arte: STM (Structural Topic Modeling) [@roberts2019].

10.3.2 ¿Por qué STM es superior para nuestro análisis?

A diferencia de LDA, el modelo STM permite incorporar Metadatos (quién es el autor, a qué bando pertenece, etc.). Esto significa que el STM funciona como una “regresión” para las palabras: nos permite demostrar con datos si el hecho de pertenecer a un sector concreto explica por qué se usan unos temas y no otros.

Gracias al STM, podemos responder con rigor estadístico a preguntas como: ¿Es estadísticamente significativo que el bando opositor sea el “dueño” del concepto de inseguridad jurídica? o ¿Hasta qué punto el bando de los inquilinos ha logrado monopolizar el marco de la “justicia social”?


10.4 4. Ejemplo Práctico: El debate de la “Ley de Vivienda” en España

Actualmente, España tramita una ley de vivienda que ha generado una polarización extrema. Para este ejercicio, vamos a importar un dataset que simula una recolección de mensajes en redes sociales de los dos grandes sectores enfrentados: Inquilinos/Gobierno y Propietarios/Oposición.

Advertencia⚠️ Nota sobre los datos

Los datos utilizados en este ejemplo han sido generados sintéticamente mediante un script para fines pedagógicos. Aunque el vocabulario y los argumentos reflejan las posiciones reales del debate en España, no corresponden a mensajes reales de usuarios y deben ser tratados solo como una simulación para aprender la técnica de STM.

library(quanteda)
library(stm)
library(tidyverse)

# (1) IMPORTACIÓN DE DATOS: Cargamos el dataset generado por el script externo
datos_vivienda <- read_csv("data/housing_debate.csv")

# Echamos un vistazo a los primeros mensajes generados
head(datos_vivienda, 5)
# A tibble: 5 × 3
     id sector       texto                                              
  <dbl> <chr>        <chr>                                              
1     1 Inquilinos   social topar vivienda anuncio tensionada tensionada
2     2 Inquilinos   desahucios anuncio derecho ley agencia topar       
3     3 Inquilinos   vivienda agencia vivienda derecho tenedores ley    
4     4 Propietarios mañana vacía hoy inseguridad propiedad oferta      
5     5 Inquilinos   desahucios topar mañana anuncio derecho topar      

(Continuamos con el preprocesamiento y el modelado…)

# (2) PROCESAMIENTO CON QUANTEDA (Limpieza real)
# Creamos corpus y limpiamos (stopwords en español son vitales)
corp <- corpus(datos_vivienda, text_field = "texto")
toks <- tokens(corp, remove_punct = TRUE, remove_numbers = TRUE) %>%
        tokens_remove(stopwords("spanish")) # FILTRO CRÍTICO

dfm_vivienda <- dfm(toks)

# (3) MODELADO STM
out <- quanteda::convert(dfm_vivienda, to = "stm")

# Entrenamos buscando K=2 grandes tópicos latentes
# Nota: Es normal recibir un aviso sobre 'unidimensional scaling'; 
# confirma que el modelo actúa como una balanza de polarización.
modelo_vivienda <- stm(documents = out$documents, 
                       vocab = out$vocab, 
                       K = 2, 
                       prevalence =~ sector, 
                       data = datos_vivienda, 
                       max.em.its = 30, 
                       verbose = FALSE) 
Warning in stm(documents = out$documents, vocab = out$vocab, K = 2, prevalence
= ~sector, : K=2 is equivalent to a unidimensional scaling model which you may
prefer.

10.4.1 Análisis de Contraste: ¿Quién domina qué “Frame”?

efecto <- estimateEffect(1:2 ~ sector, modelo_vivienda, meta = datos_vivienda)

plot(efecto, covariate = "sector", topics = c(1, 2),
     model = modelo_vivienda, method = "difference",
     cov.value1 = "Propietarios", cov.value2 = "Inquilinos",
     xlab = "Más usado por INQUILINOS <------------> Más usado por PROPIETARIOS",
     main = "Burbujas Discursivas: Ley de Vivienda")

(Análisis): El modelo detecta dos “realidades” paralelas. Mientras los Inquilinos centran el debate en “derechos” y “precios topados”, los Propietarios lo llevan al terreno de la “inseguridad jurídica” y la “okupación”. A pesar de hablar del mismo objeto (la Ley), no comparten el mismo vocabulario.

¿Cómo leer este gráfico de contraste?

  • Eje Horizontal (X): Muestra la diferencia en la proporción esperada (prevalencia) de un tema entre los dos grupos. Por ejemplo, si un punto está a la derecha en el valor 0.2, significa que el grupo de Propietarios dedica, en promedio, un 20% más de espacio en sus mensajes a ese tema que el grupo de Inquilinos.
  • Eje Vertical (Y): Es un eje categórico que sirve para separar los diferentes tópicos analizados (Tópico 1 vs Tópico 2). Estar más “arriba” o más “abajo” no tiene un valor matemático; simplemente es el orden en el que se listan los temas para poder visualizarlos por separado.
  • ¿Por qué salen dos puntos? Porque en el código hemos especificado topics = c(1, 2). Si el modelo tuviera 10 tópicos y pidiéramos visualizarlos todos, veríamos 10 puntos repartidos verticalmente. Las líneas horizontales son los intervalos de confianza (95%). Si la línea no cruza el eje vertical del “0”, podemos afirmar que existe una diferencia significativa en la importancia que cada bando le da a ese tema.

10.5 5. Redes de Co-ocurrencia Semántica con “Hulls”

Para terminar, vamos a ver cómo estas palabras se agrupan en el “cerebro colectivo” de la red social. Usaremos la técnica de envolventes (geom_mark_hull) para visualizar los dos mundos discursivos.

library(widyr)
library(tidygraph)
library(ggraph)
library(ggforce)

# Calculamos correlaciones entre palabras
word_cors <- tidytext::tidy(dfm_vivienda) %>%
  group_by(term) %>%
  filter(n() >= 20) %>% 
  pairwise_cor(term, document, sort = TRUE)

# Creamos el grafo semántico
red_vivienda <- word_cors %>%
  filter(correlation > 0.1) %>%
  as_tbl_graph(directed = FALSE) %>%
  mutate(comunidad = as.factor(group_louvain())) 

# Visualización Avanzada
set.seed(123)
ggraph(red_vivienda, layout = "fr") +
  # Burbujas de comunidades discursivas
  geom_mark_hull(aes(x = x, y = y, fill = comunidad, group = comunidad), 
                 alpha = 0.1, color = NA, expand = unit(3, "mm")) +
  geom_edge_link(aes(edge_alpha = correlation), edge_colour = "gray80") +
  geom_node_point(aes(color = comunidad), size = 5) +
  geom_node_text(aes(label = name), repel = TRUE, size = 3.5, fontface="bold") +
  scale_fill_brewer(palette = "Set1") +
  scale_color_brewer(palette = "Set1") +
  theme_graph() +
  theme(legend.position = "none") +
  labs(title = "La Anatomía del Debate: Red Semántica de la Ley de Vivienda",
       subtitle = "Nodos: Palabras | Envolventes: Comunidades de significado")


10.6 6. Caso de Estudio Real: El Alquiler en Madrid (Análisis SNA)

Para complementar los ejemplos pedagógicos, vamos a aplicar estas técnicas a un dataset de reseñas reales sobre viviendas en Madrid. Aquí explotaremos el análisis de “Frames” y la detección de comunidades para entender la estructura del mercado real.

10.6.1 6.1. Carga y Análisis de Sentimiento

library(readr)

# (1) IMPORTACIÓN: Cargamos las reseñas reales de Madrid
datos_madrid <- read_csv("data/vivienda_madrid_real.csv")

# (2) SENTIMIENTO: Calculamos el clima emocional usando el lexicón anterior
sentimiento_madrid <- datos_madrid %>%
  unnest_tokens(word, comentario) %>%
  inner_join(lexicon_esp, by = "word") %>%
  group_by(id, puntuacion_usuario) %>%
  summarise(sentimiento_calculado = sum(valor), .groups = "drop")

# Unimos con los datos originales
datos_madrid_completo <- datos_madrid %>%
  left_join(sentimiento_madrid, by = c("id", "puntuacion_usuario")) %>%
  mutate(sentimiento_calculado = replace_na(sentimiento_calculado, 0))

head(datos_madrid_completo)
# A tibble: 6 × 5
     id fecha      comentario           puntuacion_usuario sentimiento_calculado
  <dbl> <date>     <chr>                             <dbl>                 <dbl>
1     1 2024-01-15 El piso estaba muy …                  3                     0
2     2 2024-01-20 Mucha gente haciend…                  1                     0
3     3 2024-01-22 Anfitrión muy amabl…                  5                     0
4     4 2024-02-05 Apartamento sucio y…                  1                     0
5     5 2024-02-10 Excelente ubicación…                  5                     0
6     6 2024-02-12 El barrio de Lavapi…                  2                     0

10.6.2 6.2. El Mapa del Discurso: Red Bipartita y Comunidades

¿Quién domina qué “Frame”? Vinculamos a los actores (IDs) con sus discursos predominantes.

# Mapeo de frames (Análisis cualitativo del discurso)
frames_list <- list(
  "Precio/Burbuja" = c(1, 4, 7, 11),
  "Ubicación"      = c(1, 5, 6, 10, 11, 14, 16, 20),
  "Calidad/Confort"= c(4, 8, 10, 12, 18, 20),
  "Problemas/Ruido"= c(2, 6, 7, 9, 13, 15, 17, 19)
)

edges_madrid <- stack(frames_list) %>% 
  rename(from = values, to = ind) %>%
  mutate(from = as.character(from))

# Crear grafo bipartito y detectar comunidades
red_madrid <- as_tbl_graph(edges_madrid, directed = FALSE) %>%
  mutate(type = node_is_center()) %>% 
  mutate(community = as.factor(group_louvain()))

# Visualización
ggraph(red_madrid, layout = "stress") +
  geom_edge_link(alpha = 0.2, color = "grey50") +
  geom_node_point(aes(color = community, shape = type, size = type)) +
  geom_node_text(aes(label = name), repel = TRUE, size = 3, fontface = "bold") +
  scale_size_manual(values = c(4, 8)) +
  scale_color_brewer(palette = "Set1") +
  labs(
    title = "Red Semántica de Reseñas en Madrid (Real)",
    subtitle = "Vinculando usuarios con sus marcos discursivos",
    caption = "Detección de comunidades: Algoritmo de Louvain"
  ) +
  theme_graph() +
  theme(legend.position = "none")

10.6.3 6.3. Interpretación: La “Burbuja” y el Contraste de Realidades

Al observar la red, extraemos conclusiones críticas sobre el mercado madrileño:

  1. La Anatomía de la “Burbuja”: El frame Precio/Burbuja actúa como un eje de polarización negativa. Los usuarios vinculados a él (ID 1, 7, 11) muestran un descontento estructural que no se compensa con la ubicación, evidenciando una “burbuja” de frustración.
  2. Detección de Comunidades: Louvain identifica dos mundos opuestos: la Comunidad Turística (enfocada en Sol/Malasaña y confort) y la Comunidad de Resistencia (afectada por precios abusivos y falta de habitabilidad).
  3. El Poder del Frame: Reseñas en la misma zona se agrupan en comunidades distintas según su marco discursivo, demostrando que la percepción narrativa domina sobre la ubicación física.

10.7 7. SNA Avanzado: Geopolítica y la Crisis de la OTAN (2025-2026)

Este ejercicio final se divide en dos partes para mostrar cómo el SNA se adapta a diferentes escalas: primero, una red curada para entender los métodos, y segundo, una red masiva para ver el comportamiento del sistema a gran escala.

10.7.1 7.1. Parte 1: La Red Curada (Liderazgo y Métodos)

En esta primera fase, analizamos a los 20 actores principales del debate. El objetivo es identificar quiénes son los líderes, quiénes los puentes y qué comunidades emergen de forma natural.

# (1) IMPORTACIÓN: Dataset curado (20 actores)
datos_nato <- read_csv("data/nato_polarization_2026.csv")

# (2) CREACIÓN DEL GRAFO
edges_nato <- datos_nato %>%
  mutate(from = as.character(id), to = as.character(menciones)) %>% 
  select(from, to) %>% filter(from != to)

red_nato <- as_tbl_graph(edges_nato, directed = FALSE) %>%
  activate(nodes) %>%
  left_join(datos_nato %>% mutate(id = as.character(id)), by = c("name" = "id")) %>%
  mutate(community = as.factor(group_louvain()),
         degree = centrality_degree(),
         betweenness = centrality_betweenness(),
         pagerank = centrality_pagerank(),
         closeness = centrality_closeness())

# (3) RANKING DE LA RED CURADA: Auditando el material visto en clase
ranking_curado <- red_nato %>%
  as_tibble() %>%
  select(usuario, bando, degree, betweenness, pagerank) %>%
  arrange(desc(pagerank))

knitr::kable(head(ranking_curado, 10), digits = 3, 
             caption = "Top 10 Actores - Red Curada (20 nodos)")
Top 10 Actores - Red Curada (20 nodos)
usuario bando degree betweenness pagerank
NATO_Official Neutral 6 46 0.137
Euro_Security Europe 6 66 0.135
Macron_Vision Europe 5 54 0.121
Trump_Fan_USA America First 5 6 0.119
Geopolitics_Daily Neutral 2 13 0.054
MAGA_2026 America First 2 0 0.048
London_Times_Ed Europe 1 0 0.030
Marine_LePen_Fans Europe 1 0 0.028
Brussels_Observer Europe 1 0 0.028
Paris_Journal Europe 1 0 0.028

10.7.1.1 Recorrido de Análisis (Red Curada):

  1. Actores Principales por PageRank: El PageRank resalta a actores con “prestigio” como @NATO_Official o @Scholz_Berlin, que aunque no tengan el mayor grado, reciben atención de otros nodos importantes.
  2. Los “Brokers” (Betweenness): Actores como @London_Times_Ed o @Geopolitics_Daily muestran un Betweenness alto, actuando como puentes diplomáticos que evitan que la red se rompa en dos.
  3. Detección de Comunidades: El algoritmo Louvain separa claramente el “Bloque America First” del “Bloque Europeo”, con un tercer grupo “Neutral” que intenta mediar.
ggraph(red_nato, layout = "kk") +
  geom_edge_link(alpha = 0.3, color = "gray70") +
  geom_node_point(aes(color = bando, size = pagerank)) +
  geom_node_text(aes(label = usuario), repel = TRUE, size = 3) +
  scale_color_manual(values = c("America First" = "#E91E63", "Europe" = "#3F51B5", "Neutral" = "#9E9E9E")) +
  labs(title = "Parte 1: Red de Prestigio (20 actores)",
       subtitle = "Tamaño: PageRank (Autoridad) | Color: Bando Político") +
  theme_graph()


10.7.2 7.2. Parte 2: La Red Masiva (Escalabilidad y Auditoría Profunda)

Ahora escalamos a 200 usuarios. Aplicaremos todo el material de SNA (Centralidad, Comunidades, Homofilia) para ver cómo cambian los roles en una red de mayor tamaño.

# (1) IMPORTACIÓN: Dataset masivo (200 usuarios)
datos_nato_L <- read_csv("data/nato_polarization_large_2026.csv")

# (2) CREACIÓN DEL GRAFO MASIVO
edges_nato_L <- datos_nato_L %>%
  mutate(from = as.character(id), to = as.character(menciones)) %>% 
  select(from, to) %>% filter(from != to)

red_nato_L <- as_tbl_graph(edges_nato_L, directed = FALSE) %>%
  activate(nodes) %>%
  left_join(datos_nato_L %>% mutate(id = as.character(id)), by = c("name" = "id")) %>%
  mutate(community = as.factor(group_louvain()),
         degree = centrality_degree(),
         betweenness = centrality_betweenness(),
         pagerank = centrality_pagerank())

# (3) AUDITORÍA DE MÉTRICAS EN RED MASIVA
ranking_masivo <- red_nato_L %>%
  as_tibble() %>%
  select(usuario, bando, degree, betweenness, pagerank) %>%
  arrange(desc(pagerank))

knitr::kable(head(ranking_masivo, 10), digits = 4, 
             caption = "Top 10 Actores - Red Masiva (200 nodos)")
Top 10 Actores - Red Masiva (200 nodos)
usuario bando degree betweenness pagerank
Trump_Fan_USA America First 41 3491 0.0927
NATO_Official Neutral 24 4366 0.0530
Euro_Security Europe 23 3762 0.0514
Macron_Vision Europe 21 2462 0.0486
MAGA_2026 America First 19 1360 0.0436
Scholz_Berlin Europe 18 1700 0.0424
Patriot_Texas America First 17 1288 0.0402
US_Senator_R America First 17 1288 0.0402
Warsaw_Defense Europe 17 1608 0.0401
London_Times_Ed Europe 4 321 0.0103

10.7.2.1 Análisis de Resultados (Red Masiva):

  1. Cambio en PageRank: En la red masiva, el PageRank es el actor más fiable. Vemos que los “seguidores” (Followers) aumentan el prestigio de los líderes originales. Los actores principales por PageRank siguen siendo los líderes institucionales, demostrando la resiliencia de la autoridad.
  2. Colapso del Betweenness: A medida que la red crece y se polariza más, el Betweenness de los puentes suele bajar si no hay interacciones cruzadas. Si el Betweenness es bajo en todos los nodos, estamos ante una red totalmente escindida.
  3. Comunidades y Homofilia:
    • Calculamos el Índice de Homofilia (Assortativity).
library(igraph)
Warning: package 'igraph' was built under R version 4.5.2

Attaching package: 'igraph'
The following object is masked from 'package:tidygraph':

    groups
The following objects are masked from 'package:lubridate':

    %--%, union
The following objects are masked from 'package:dplyr':

    as_data_frame, groups, union
The following objects are masked from 'package:purrr':

    compose, simplify
The following object is masked from 'package:tidyr':

    crossing
The following object is masked from 'package:tibble':

    as_data_frame
The following objects are masked from 'package:stats':

    decompose, spectrum
The following object is masked from 'package:base':

    union
v_bando_L <- as.factor(V(red_nato_L)$bando)
indice_h_L <- assortativity_nominal(red_nato_L, v_bando_L)
print(paste("Índice de Homofilia (Red 200 nodos):", round(indice_h_L, 3)))
[1] "Índice de Homofilia (Red 200 nodos): 0.78"

10.7.2.2 Interpretación de la Auditoría Completa:

  • ¿Quién domina la red?: El análisis cruzado muestra que mientras @Trump_Fan_USA domina en Grado (muchas menciones), la @NATO_Official domina en Prestigio (PageRank).
  • La Trampa de la Polarización: El índice de 0.78 (Asortatividad) nos dice que la red está fragmentada. Los algoritmos de comunidades (Louvain) detectan burbujas casi perfectas, confirmando que el material de SNA nos permite “ver” la estructura invisible de la crisis política.
ggraph(red_nato_L, layout = "stress") +
  geom_edge_link(alpha = 0.1, color = "gray80") +
  geom_node_point(aes(color = community, size = pagerank), alpha = 0.6) +
  scale_size_continuous(range = c(1, 10)) +
  labs(title = "Parte 2: Auditoría Masiva de Polarización",
       subtitle = "Tamaño: PageRank | Color: Comunidad Louvain",
       caption = "La estructura revela dos núcleos masivos sin comunicación entre ellos.") +
  theme_graph()


10.8 8. Material para Clase y Descarga de Datasets

Para que puedas replicar todos los análisis vistos en este capítulo y experimentar con tus propias métricas, ponemos a tu disposición los datasets utilizados:

[!TIP] Datasets del Capítulo:


Recapitulación Final: Este capítulo ha demostrado que el SNA, combinado con el NLP, nos permite auditar la opinión pública desde el nivel micro (reseñas de Madrid) hasta el nivel macro (geopolítica global), adaptando las métricas de Betweenness para la diplomacia y PageRank para la autoridad discursiva.