El desarrollo de la analítica de datos ha ido pareja a grandes avances en las posibilidades de captación de información.
La mayor parte de la información no está en formato numérico, y menos en una manera estructurada.
La minería de texto junto con las técnicas de Procesamiento de Lenguaje Natural nos permiten abordar con un esfuerzo razonable análisis que antes resultaban titánicos en tiempo y dinero. Las palabras ya forman parte de nuestro ámbito de información para la gestión. Y no sólo las escritas, el reconocimiento de voz nos abre un mundo de posibilidades.
El caso que mostraremos a continuación enlaza muy bien con nuestras sensibilidades, y es fácilmente interpretable por el conocimiento general de los temas tratados.
Pero es sólo un ejemplo de un amplio abanico de casos de uso para este tipo de técnicas.
Un uso muy habitual es la clasificación documental en las instituciones o empresas. ¿Cómo clasificar o etiquetar automáticamente nuevos documentos para facilitar su acceso a los usuarios?
También para el tratamiento de encuestas cualitativas, no sólo en investigación de mercados sino en cualquier encuesta lanzada a empleados de organizaciones, donde interesa especialmente analizar respuestas de tipo abierto sobre los temas de interés.
¿Y si queremos conocer la imagen que tienen los clientes o usuarios de nuestra empresa? Es muy factible monitorizar a través de redes sociales los comentarios y reviews y convertirlos en KPIs de reputación corporativa, o de aceptación de un producto, o incluso de comparación de nuestros productos con los de la competencia.
En fin, todo un mundo de utilidades por descubrir e incorporar a la gestión de decisiones basadas en datos.
Dicen que vivimos en un mundo cada vez más líquido donde no importa lo que se dice mientras no quede retratado por hechos ciertos y demostrados.
Lamentablemente, la realidad parece confirmarlo. La palabra ha dejado de tener carácter contractual. Incumplir lo dicho ya no tiene coste.
Sin embargo, las palabras importan, tanto las que se dicen como las que no son dichas.
Los procesos electorales se han convertido en momentos dorados para el uso e interpretación de la investigación de mercados (electores) y el desarrollo de productos (promesas electorales).
Pero las palabras importan, porque nos mueven a la acción. Y nos hacen tomar decisiones. Que no se las lleve el viento.
El objetivo de este estudio es extraer las claves de los diferentes programas electorales para confrontarlos a través de técnicas de PLN. Se han incluido únicamente los programas oficiales publicados de los partidos, de ámbito nacional, a los que el CIS estimó representación parlamentaria.
El estudio se realizará en lenguaje R, con las librerías habituales de PLN, además de paquetes personalizados.
# Librerías necesarias
paquetes = c("tidyverse","caret", "tm", "qdap", "dplyr","stringr",
"tidytext", "ggplot2", "tidyr","igraph","data.table",
"pdftools", "wordcloud", "topicmodels", "ggraph","scales")
# carga o instala en su caso
comprobacion.paquetes <- lapply(paquetes, FUN = function(x) {
if (!require(x, character.only = TRUE)) {
install.packages(x, dependencies = TRUE)
library(x, character.only = TRUE)
}
})
Los programas electorales fueron descargados de cada página web oficial, bien en formato pdf, bien en formato txt, y leídos desde R con funciones tipo pdftools.
Dado el razonable volumen de información es posible realizar algunos ajustes ad-hoc en cada programa para homogeneizar los documentos y facilitar su tratamiento. Después, y usando expresiones regulares, se identifican medidas electorales, temas de campaña (políticas), y se arreglan especificidades de diseño en cada programa (guiones de cambio de línea, etc).
Hay ciertas expresiones comunes equivalentes que hay que homogeneizar (CCAA - Comunidades Autónomas, FP - Formación Profesional).
También se descartan los textos introductorios en los programas, para centrar el análisis en la confrontación de las medidas.
Hay más acciones que realizar todavía en relación con calidad del dato, pero contamos ya con una tabla con características tidy que permite ser trabajada analíticamente.
tidy_10n = read.table("tidy_10n.csv", sep = ",", stringsAsFactors = FALSE,
header = TRUE, row.names = NULL)
#limpio encodings
tidy_10n$text = str_replace_all(tidy_10n$text, "<U\\+F0A7>", "")
tidy_10n$text = str_replace_all(tidy_10n$text, "<U\\+F0A0>", "")
Desplegamos tokens = palabras, que será nuestra base principal de análisis. Generamos un index de control del orden original.
tidy_10n_token <- tidy_10n %>% unnest_tokens(word, text) %>% mutate(orden_orig = seq_along(word))
De esta manera, tenemos ya una visión cuantitativa de la extensión de los diferentes programas. Vemos cómo hay gran disparidad en el despliegue, lo que sin duda tiene impacto en el análisis cuando pretendamos sacar conclusiones del conjunto de programas. Lo abordaremos más tarde a través de una normalización.
label_graficos = c("Cs", "MasPais", "PP", "PSOE", "UPodemos", "VOX")
tidy_10n_token %>% group_by(doc_id) %>%
summarise(n_words =n()) %>%
ggplot(aes(doc_id, n_words)) +
geom_col(fill = "#377EB8") +
ggtitle("Número de palabras en el Programa publicado")+
labs(y = NULL, x = NULL)+
geom_text(aes(y = 300, size = 30, label = label_graficos,
vjust = 0.2, hjust = 0),alpha = 0.8, color = "white")+
coord_flip(clip = "off", expand = TRUE)+
theme(axis.line=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
plot.title=element_text(size=15, hjust=0.5, vjust = 1, face="bold", colour="#377EB8"),
plot.margin = margin(2,2, 2, 2, "cm"),
legend.position="none")
Veamos este despliegue al detalle de temas (políticas) y promesas electorales (medidas) para tener una idea de la extensión de cada programa.
(camp_temas = tidy_10n_token %>% count(doc_id,capitulo) %>% add_count(doc_id) %>%
group_by(doc_id) %>% summarise(capits = max(n)) )
(camp_medidas = tidy_10n_token %>% filter(medida >0) %>% count(doc_id,medida) %>% add_count(doc_id) %>%
group_by(doc_id) %>% summarise(medids = max(n)) )
(camp_palabras = tidy_10n_token %>% count(doc_id) )
camp_temas %>% left_join(camp_medidas, by = "doc_id") %>%
left_join(camp_palabras, by = "doc_id") %>%
transmute(programa = doc_id,
politicas = capits,
medidas = medids,
total_palabras = n,
palabras_x_medida = round(n/medids,0))
En cada tema - política- se proponen una serie de medidas - promesas electorales.
Las medidas están identificadas en función de la estructura visual de cada programa, pudiendo contener cada una a su vez bloques de medidas más concretas y detalladas.
Procedemos a la preparación y limpieza de la información para su posterior tratamiento.
Primero eliminamos cualquier elemento que no sea texto (puntuaciones, dígitos, etc.) y convertimos todo a minúscula. También eliminamos espacios innecesarios.
Por último, quitamos todas las palabras que no aportan significado al texto desde el punto de vista analítico (artículos, adverbios, etc.), así como palabras concretas que no aportan en este contexto en particular.
replacePunctuation = function(x) { gsub("[[:punct:]]+", " ", x)}
replaceNumbers = function(x) { gsub("[[:digit:]]+", " ", x)}
tidy_10n_token$word = tidy_10n_token$word %>%
replacePunctuation() %>%
replaceNumbers() %>%
bracketX() %>%
tolower () %>%
stripWhitespace()
# quitamos espacios en blanco, que ya no debería haber:
tidy_10n_token$word <- gsub("\\s+","",tidy_10n_token$word)
# eliminar stopwords
stop_words_sp = as.data.frame(stopwords("spanish"))
names(stop_words_sp) = "word"
tidy_10n_token_cleared <- tidy_10n_token %>% anti_join(stop_words_sp, by = "word")
# eliminar términos para nuestro contexto :
stop_especiales = c("véase","apartado", "así", "todas", "toda", "ello", "cualquier", "quién",
"puedan", "asimismo", "ser", "través", "dicha", "contemple", "suficiente",
"manera","tipo","solo","parte", "resto", "sino", "correspondientes",
"además","menos", "cs", "docs", "unas", "tal")
stop_especiales = as.data.frame(stop_especiales, stringAsFactors = FALSE)
names(stop_especiales) = "word"
tidy_10n_token_cleared = tidy_10n_token_cleared %>% anti_join(stop_especiales, by = "word")
tidy_10n_token_cleared = tidy_10n_token_cleared %>% filter(word != "")
# tabla preparada
tidy_10n_token_cleared %>% count(word, sort = TRUE)