Esta práctica se basa en una de las aplicaciones de la Minería de Textos, que consiste en poder clasificar documentos en función de su temática …. Esto es lo que se conoce como Topic Model.
Por Topic Model entendemos procesos de aprendizaje automático que tienen por objetivo descubrir el tema subyacente en una colección de documentos. Generalizando un poco más, Topic Model busca patrones en el contenido de los documentos y lo hace en base a la frecuencia de aparición de palabras.
Es de esperar que en documentos pertenecientes a un mismo tema aparecerán palabras que se repetirán con cierta frecuencia. Esta frecuencia se almacenará en una matriz de frecuencias que será la base para que puedan trabajar algoritmos de aprendizaje automático.
En este ámbito de conocimiento se basan los sistemas de clasificación documental, búsqueda de contenidos y recomendación entre otros.
Recursos en la web:
Los responsables de una empresa quieren ofrecer a sus clientes un producto nuevo. Se trata de un servicio de Recomendación de contenidos basado en un resumen de noticias de actualidad agrupadas según temáticas.
Será un producto orientado a empresas con capacidad para realizar inversiones financieras, como entidades bancarias, aseguradoras y fondos de inversión.
Este tipo de empresas tienen equipos de profesionales dedicados a analizar la situación y las oportunidades de inversión en diferentes sectores económicos. Para ellos es fundamental recibir información actualizada y de calidad sobre los temas que tienen en cartera.
Para cubrir las necesidades de la empresa, se utiliza un sistema de clasificación de documentos que se basa en el algoritmo de aprendizaje automático K-NN (K-Nearest Neighbors o K vecinos más próximos).
Para ello, en nuestro directorio de trabajo, dispondremos de dos carpetas:
txt/acq con 50 noticias suministradas por la agencia Reuters sobre la temática de las adquisiciones empresariales.
txt/crude con 20 noticias suministradas también por la agencia de noticias Reuters, sobre el sector del Petróleo o Crudo.
Para mostrar el funcionamiento del motor de clasificación usaremos 49 de los 70 documentos (un 70%) para entrenar un modelo de aprendizaje, con la idea de aplicarlo sobre los 21 documentos (un 30%) restantes con el objetivo de predecir su temática: “Adquisiciones” o “Crudo”.
El código R que utilizaremos en la práctica se divide en apartados según las tareas que iremos realizando:
Instalamos los packages de R que necesitaremos para realizar la práctica: install.packages(“tm”) install.packages(“plyr”) install.packages(“class”) install.packages(“ggplot2”) install.packages(“SnowballC”) install.packages(“wordcloud”) Generamos campos para guardar las dos temáticas y la ruta básica del directorio en el que tenemos los documentos.
# Para la función Corpus()
library(tm)
# Para la función rbind.fill
library(plyr)
# Para la función knn()
library(class)
# En R una variable tipo factor es un tipo de variable categórica
# que puede contener tanto números como carácteres. Se trata de un
# tipo de variable muy útil para realizar tareas de modelado estadístico.
# En R, por defecto, las columnas con carácteres no numéricos son
# tratados como factores. Para evitarlo y garantizar que estas columnas
# sigan siendo consideradas carácteres, fijaremos el siguiente parámetro
options(stringsAsFactors = FALSE)
# Temas que distinguiremos
temas <- c("Adq", "Crudo")
# Ruta principal de los documentos de noticias Reuters
setwd("D:\\UOC\\E2\\B2.325 - Business Analytics\\PEC 1\\Scripts")
nombreruta <- paste(getwd(),"/txt", sep = "")
La función acondicionaCorpus() realizará tareas de acondicionado de texto, como:
acondicionaCorpus <- function(corpus) {
corpus.tmp <- tm_map(corpus, removePunctuation)
corpus.tmp <- tm_map(corpus.tmp, stripWhitespace)
corpus.tmp <- tm_map(corpus.tmp, content_transformer(tolower))
v_stopwords <- c(stopwords("english"),c("dont","didnt","arent","cant","one","also","said"))
corpus.tmp <- tm_map(corpus.tmp, removeWords, v_stopwords)
corpus.tmp <- tm_map(corpus.tmp, removeNumbers)
corpus.tmp <- tm_map(corpus.tmp, stemDocument, language="english")
return(corpus.tmp)
}
La función generaTDM() recibirá como parámetro la ruta raíz de los documentos Reuters y el tema de los documentos que deberá recuperar.
Cargará los distintos documentos XML (noticias Reuters) y guardará cada documento en el vector “documentos”.
Posteriormente fusionará todos los documentos en un solo Corpus y realizará tareas de limpieza de texto. Una vez el texto esté acondicionado se construirá una matriz de palabras o términos donde las columnas serán documentos, las filas términos y las celdas contendrán el número de veces que el término aparece en el documento.
Atención ! Los nombres de los documentos XML que se usan para esta práctica, contienen un número secuencial que tiene saltos, de modo que en la carpeta txt/acq hay 50 documentos y no 56. Del mismo modo, en la carpeta txt/crude hay 20 documentos y no 23.
generaTDM <- function(tema, ruta){
# Rellenamos el campo "carpeta" en función del parámetro "tema"
if (tema=="Adq"){carpeta <- "/acq"} else {carpeta <- "/crude"}
fuente <- paste(ruta,carpeta, sep = "")
# Lectura de los ficheros XML
ficheros <- Corpus(DirSource(fuente),readerControl=list(reader=readReut21578XMLasPlain))
# Inicializamos el vector de caracteres "documentos"
documentos <- character(length(ficheros))
# ------------------------------------------------
# Inicio del proceso de acceso a las noticias Reuters
# ------------------------------------------------
for (i in 1:length(ficheros)) {
# Carga del fichero XML
doc.text <- ficheros[[i]]
# Substituir \n por espacio en blanco
doc.text <- gsub('\\n', ' ', doc.text)
doc.text <- gsub('"', ' ', doc.text)
# Llegados a este punto tenemos cada párrafo en un elemento de la lista.
# A continuación uniremos todos los párrafos en un único elemento de la lista doc.text
doc.text <- paste(doc.text, collapse = ' ')
documentos[i] <- doc.text
}
# ---------------------------------------------
# Fin del proceso de acceso a las noticias Reuters
# ---------------------------------------------
# Generamos un corpus con todos los documentos del tema seleccionado
s.cor <- Corpus(VectorSource(documentos))
# Aplicamos las tareas de acondicionado de texto previstas en la función acondicionaCorpus
s.cor.cl <- acondicionaCorpus(s.cor)
# Generamos una matriz de palabras donde las filas son los documentos y las columnas son las palabras o términos.
s.tdm <- TermDocumentMatrix(s.cor.cl)
# Eliminamos las palabras coloquiales que suelen repetirse y que no aportan significado
s.tdm <- removeSparseTerms(s.tdm, 0.85)
# Devolvemos una lista donde el primer valor "name" es el Tema de los documentos y el segundo valor "tdm" es la matriz de palabras TDM
result <- list(name = tema, tdm = s.tdm)
}
Una vez definida la función generaTDM(), la usaremos para conseguir una doble matriz de términos. Decimos que es doble porque en realidad está estructurada como dos matrices superpuestas, es decir, tdm[[1]] la matriz correspondiente a los documentos de tema Adquisiciones y tdm[[2]] la matriz correspondiente a los documentos de tema Crudo.
library(SnowballC)
# Construimos un bucle sobre los dos Temas y les aplicamos la función generaTDM() pasando como parámetro la ruta de los documentos XML con las noticias Reuters.
tdm <- lapply(temas, generaTDM, ruta = nombreruta)
# Al imprimir la estructura del objeto tdm, observamos cómo está compuesta por 2 Matrices de Términos: "Adq" y "Crudo"
str(tdm)
## List of 2
## $ :List of 2
## ..$ name: chr "Adq"
## ..$ tdm :List of 6
## .. ..$ i : int [1:817] 1 9 13 14 15 17 23 24 27 30 ...
## .. ..$ j : int [1:817] 1 1 1 1 1 1 1 1 1 1 ...
## .. ..$ v : num [1:817] 1 1 3 4 1 4 1 2 1 1 ...
## .. ..$ nrow : int 60
## .. ..$ ncol : int 50
## .. ..$ dimnames:List of 2
## .. .. ..$ Terms: chr [1:60] "acquir" "acquisit" "agre" "agreement" ...
## .. .. ..$ Docs : chr [1:50] "1" "2" "3" "4" ...
## .. ..- attr(*, "class")= chr [1:2] "TermDocumentMatrix" "simple_triplet_matrix"
## .. ..- attr(*, "weighting")= chr [1:2] "term frequency" "tf"
## $ :List of 2
## ..$ name: chr "Crudo"
## ..$ tdm :List of 6
## .. ..$ i : int [1:546] 8 10 14 15 17 19 20 22 23 28 ...
## .. ..$ j : int [1:546] 1 1 1 1 1 1 1 1 1 1 ...
## .. ..$ v : num [1:546] 2 1 2 2 2 2 1 2 1 1 ...
## .. ..$ nrow : int 92
## .. ..$ ncol : int 20
## .. ..$ dimnames:List of 2
## .. .. ..$ Terms: chr [1:92] "accord" "agreement" "analyst" "april" ...
## .. .. ..$ Docs : chr [1:20] "1" "2" "3" "4" ...
## .. ..- attr(*, "class")= chr [1:2] "TermDocumentMatrix" "simple_triplet_matrix"
## .. ..- attr(*, "weighting")= chr [1:2] "term frequency" "tf"
Con las dos Matrices de frecuencias tdm[[1]]$tdm para Adquisiciones y tdm[[2]]$tdm para Crudo, podemos realizar algunas visualizaciones básicas.
# Frecuencia de los 30 primeros términos en los 10 primeros documentos del tema Adquisiciones
inspect(tdm[[1]]$tdm[1:30,1:10])
## <<TermDocumentMatrix (terms: 30, documents: 10)>>
## Non-/sparse entries: 73/227
## Sparsity : 76%
## Maximal term length: 9
## Weighting : term frequency (tf)
##
## Docs
## Terms 1 2 3 4 5 6 7 8 9 10
## acquir 1 0 0 1 1 0 0 0 0 0
## acquisit 0 3 0 0 0 0 1 0 0 0
## agre 0 0 1 0 0 0 0 0 0 0
## agreement 0 0 0 0 0 0 0 0 0 0
## alreadi 0 0 0 0 0 0 0 0 0 0
## approv 0 0 1 0 0 0 0 0 1 0
## bank 0 0 0 0 0 0 0 0 0 0
## board 0 0 0 1 0 0 0 0 0 0
## buy 1 0 0 0 0 0 0 0 0 0
## capit 0 1 0 1 0 0 3 0 0 0
## cash 0 0 0 2 0 0 1 0 0 0
## close 0 0 0 0 0 0 1 0 0 1
## common 3 0 0 0 0 2 0 2 0 0
## compani 4 1 0 9 1 0 5 1 0 1
## complet 1 0 1 0 0 0 0 0 0 0
## corp 0 0 1 1 0 2 0 1 1 0
## dlrs 4 2 0 4 0 1 2 1 0 1
## exchang 0 0 0 0 0 1 1 1 0 0
## expect 0 0 1 1 0 0 0 0 0 0
## firm 0 0 0 0 0 1 2 1 0 0
## four 0 0 0 0 0 0 1 0 0 0
## group 0 0 0 0 1 2 1 0 0 0
## hold 1 0 0 0 0 0 0 0 0 0
## inc 2 1 3 3 2 0 1 0 0 1
## industri 0 0 1 0 2 0 0 2 0 0
## interest 0 0 0 1 0 0 0 0 0 0
## make 1 0 0 0 0 0 2 0 0 0
## manag 0 0 0 6 0 0 1 0 0 0
## march 0 1 0 1 0 0 0 0 0 0
## market 1 0 0 0 0 0 9 0 0 0
inspect(tdm[[1]]$tdm[1:30,])
## <<TermDocumentMatrix (terms: 30, documents: 50)>>
## Non-/sparse entries: 399/1101
## Sparsity : 73%
## Maximal term length: 9
## Weighting : term frequency (tf)
##
## Docs
## Terms 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
## acquir 1 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 2 0 0 0 1 2
## acquisit 0 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0
## agre 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0
## agreement 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0
## alreadi 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
## approv 0 0 1 0 0 0 0 0 1 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0
## bank 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 2 0 0
## board 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0
## buy 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 0 1 0 0 2 0 0
## capit 0 1 0 1 0 0 3 0 0 0 0 0 0 0 0 0 0 0 2 2 1 0 0 0
## cash 0 0 0 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
## close 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 2 0 0 0 0 1
## common 3 0 0 0 0 2 0 2 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 2
## compani 4 1 0 9 1 0 5 1 0 1 0 1 3 0 0 1 0 2 5 0 0 2 1 0
## complet 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0
## corp 0 0 1 1 0 2 0 1 1 0 2 0 0 1 1 0 0 0 0 0 0 2 0 0
## dlrs 4 2 0 4 0 1 2 1 0 1 1 1 0 0 0 1 3 0 0 0 4 5 4 3
## exchang 0 0 0 0 0 1 1 1 0 0 1 1 0 0 0 0 2 0 0 0 0 0 0 1
## expect 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 1
## firm 0 0 0 0 0 1 2 1 0 0 1 0 0 0 0 0 0 2 2 0 0 0 0 1
## four 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0
## group 0 0 0 0 1 2 1 0 0 0 3 0 0 0 0 2 0 0 6 0 0 0 1 0
## hold 1 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 2 0 4 0 0
## inc 2 1 3 3 2 0 1 0 0 1 0 2 0 0 1 1 2 0 0 0 1 1 1 1
## industri 0 0 1 0 2 0 0 2 0 0 1 0 1 0 0 0 1 2 3 0 0 0 0 0
## interest 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 0 0 0
## make 1 0 0 0 0 0 2 0 0 0 1 0 0 0 0 0 0 0 1 2 0 0 0 0
## manag 0 0 0 6 0 0 1 0 0 0 3 1 0 0 0 0 0 0 1 0 0 1 0 0
## march 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0
## market 1 0 0 0 0 0 9 0 0 0 0 1 0 0 0 0 0 0 1 0 0 2 1 1
## Docs
## Terms 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
## acquir 0 0 0 1 2 0 0 0 1 0 0 1 1 0 0 0 1 4 0 2 1
## acquisit 1 1 0 0 4 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1
## agre 0 0 0 0 2 0 1 0 0 0 0 2 0 0 1 0 0 2 0 3 0
## agreement 0 0 0 0 0 1 0 0 1 1 0 2 0 0 1 0 0 0 0 0 0
## alreadi 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 2 0 1 0
## approv 0 0 1 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 1 0
## bank 0 2 0 0 1 1 0 0 0 0 1 0 0 0 4 0 0 0 0 1 0
## board 0 1 1 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0
## buy 0 2 0 0 3 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0
## capit 3 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0
## cash 1 0 0 0 2 0 0 0 1 2 0 0 2 0 0 0 1 1 0 1 0
## close 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
## common 0 2 0 1 2 0 0 2 0 0 1 2 2 0 0 2 0 0 0 0 0
## compani 3 1 1 1 7 1 0 0 0 3 2 1 0 0 1 0 0 7 0 3 1
## complet 0 1 0 0 1 0 0 1 2 0 0 0 0 0 1 0 1 0 0 0 0
## corp 0 1 2 0 1 1 1 0 0 0 0 0 1 0 2 1 1 1 0 0 0
## dlrs 3 2 0 1 11 0 1 1 4 6 0 4 2 0 0 0 2 2 1 4 0
## exchang 0 0 0 2 0 0 0 0 0 1 0 2 0 0 0 1 0 0 0 0 0
## expect 0 0 0 0 0 0 0 0 1 0 1 0 0 0 2 1 0 4 0 1 0
## firm 4 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## four 1 1 0 0 0 0 2 0 0 0 1 0 0 0 0 0 0 0 0 0 0
## group 1 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0
## hold 0 0 0 0 0 0 0 0 0 4 1 0 0 0 6 0 0 1 0 0 0
## inc 2 1 0 4 3 0 0 1 1 4 1 0 1 1 0 0 1 0 2 2 1
## industri 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
## interest 0 0 1 1 0 0 0 0 0 2 0 0 0 0 3 0 0 5 0 0 0
## make 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
## manag 1 0 0 0 2 0 0 0 0 1 0 0 0 1 3 0 0 0 0 1 0
## march 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 0
## market 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## Docs
## Terms 46 47 48 49 50
## acquir 1 0 1 0 0
## acquisit 0 0 0 1 2
## agre 1 3 1 0 0
## agreement 0 2 0 0 0
## alreadi 0 1 0 0 1
## approv 0 0 0 1 0
## bank 0 3 0 0 0
## board 0 1 0 0 0
## buy 0 1 0 0 0
## capit 0 0 0 0 0
## cash 0 4 0 0 0
## close 1 0 0 0 0
## common 0 2 0 0 0
## compani 1 6 3 0 1
## complet 0 2 0 1 0
## corp 1 0 0 0 1
## dlrs 1 17 0 1 0
## exchang 0 1 0 0 0
## expect 1 0 0 0 0
## firm 1 0 0 0 0
## four 2 0 0 0 0
## group 0 7 0 0 0
## hold 0 2 0 0 0
## inc 1 5 0 1 0
## industri 0 0 0 0 0
## interest 0 0 0 0 0
## make 0 0 0 0 0
## manag 0 4 0 1 0
## march 1 0 0 1 0
## market 0 0 0 0 2
# Frecuencia de los 15 primeros términos en todos los documentos del tema Crudo
inspect(tdm[[2]]$tdm[0:15,])
## <<TermDocumentMatrix (terms: 15, documents: 20)>>
## Non-/sparse entries: 75/225
## Sparsity : 75%
## Maximal term length: 9
## Weighting : term frequency (tf)
##
## Docs
## Terms 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
## accord 0 0 0 0 0 0 0 0 0 5 1 0 2 0 0 0 0 0 4 0
## agreement 0 2 0 0 0 0 0 0 0 2 1 1 0 0 0 0 0 0 0 0
## analyst 0 5 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 1 0
## april 0 1 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0 0 2 0
## arab 0 0 0 0 0 1 0 1 0 1 0 2 0 0 0 0 0 0 0 0
## arabia 0 0 0 0 0 0 0 0 0 3 3 1 2 0 0 0 0 0 0 0
## ask 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 0 0 0 0
## barrel 2 0 1 1 0 4 0 0 1 3 3 0 1 1 0 3 3 1 0 2
## bpd 0 4 0 0 0 7 0 0 0 2 8 0 0 2 0 0 0 0 0 0
## bring 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
## buyer 0 2 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0
## ceil 0 0 0 0 0 1 0 0 1 2 0 0 1 0 0 0 0 0 0 0
## chang 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 1 4 0
## compani 2 1 1 1 0 2 0 0 0 0 0 0 0 0 0 0 0 2 0 0
## contract 2 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0
# Inventario de los primeros términos del Tema Adquisiciones
head(tdm[[1]]$tdm$dimnames$Terms)
## [1] "acquir" "acquisit" "agre" "agreement" "alreadi" "approv"
# Inventario de documentos del Tema Crudo
tdm[[2]]$tdm$dimnames$Docs
## [1] "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "13" "14"
## [15] "15" "16" "17" "18" "19" "20"
# Número de documentos
nDocs(tdm[[2]]$tdm)
## [1] 20
# Número de términos
nTerms(tdm[[2]]$tdm)
## [1] 92
# Visualizamos los términos con más de 2 apariciones en documentos de temática Adquisiciones
findFreqTerms(tdm[[1]]$tdm, lowfreq=2)
## [1] "acquir" "acquisit" "agre" "agreement" "alreadi"
## [6] "approv" "bank" "board" "buy" "capit"
## [11] "cash" "close" "common" "compani" "complet"
## [16] "corp" "dlrs" "exchang" "expect" "firm"
## [21] "four" "group" "hold" "inc" "industri"
## [26] "interest" "make" "manag" "march" "market"
## [31] "may" "merger" "mln" "new" "offer"
## [36] "oper" "outstand" "own" "pct" "plan"
## [41] "previous" "profit" "purchas" "receiv" "reuter"
## [46] "sale" "secur" "sell" "share" "sharehold"
## [51] "stake" "stock" "subsidiari" "term" "total"
## [56] "transact" "unit" "valu" "will" "year"
# Visualizamos los términos con más de 2 apariciones en documentos de temática Crudo
findFreqTerms(tdm[[2]]$tdm, lowfreq=2)
## [1] "accord" "agreement" "analyst" "april" "arab"
## [6] "arabia" "ask" "barrel" "bpd" "bring"
## [11] "buyer" "ceil" "chang" "compani" "contract"
## [16] "countri" "crude" "current" "cut" "day"
## [21] "decemb" "dlrs" "effect" "emerg" "estim"
## [26] "expect" "face" "fall" "futur" "group"
## [31] "gulf" "help" "high" "industri" "intern"
## [36] "kuwait" "last" "level" "lower" "market"
## [41] "may" "meet" "member" "mid" "minist"
## [46] "mln" "month" "nation" "never" "new"
## [51] "offici" "oil" "opec" "output" "pct"
## [56] "per" "petroleum" "plan" "polici" "posit"
## [61] "post" "present" "price" "produc" "product"
## [66] "protect" "quot" "quota" "recent" "reiter"
## [71] "remain" "report" "reserv" "reuter" "saudi"
## [76] "say" "sell" "set" "sever" "sheikh"
## [81] "sourc" "state" "today" "total" "trader"
## [86] "unit" "way" "weak" "week" "will"
## [91] "world" "year"
Construiremos una nube de palabras para cada TDM (Term Document Matrix) contenida en el objeto tdm, Adquisiciones y Crudo.
# Seleccionamos la TDM de Adquisiciones y la convertimos en formato matriz
mat_adq <- as.matrix(tdm[[1]]$tdm)
# Agregamos las frecuencias por términos y las ordenamos de mayor a menor
v_adq <- sort(rowSums(mat_adq), decreasing=TRUE)
# Creamos un data.frame con términos y frecuencias
d_adq <- data.frame(word=names(v_adq), freq=v_adq)
# Gráfico de barras con las palabras más frecuentes
library(ggplot2)
ggplot(subset(d_adq, freq>20), aes(word, freq))+ geom_bar(stat="identity",fill="blue", colour="darkblue")+theme(axis.text.x=element_text(angle=45, hjust=1))
# Cargamos la librería wordcloud
require(wordcloud)
# Construimos la nube de palabras o términos
wordcloud(d_adq$word, d_adq$freq, min.freq=3, random.color=TRUE, colors=rainbow(7))
****** # Creación de un data.frame apto para K-NN ****** Procedemos ahora a definir una nueva función,
unirTemaTDM(), que nos construirá un data.frame con columnas para términos, filas para Documentos y celdas para frecuencias del término o palabra en cada documento.
La siguiente función se diseña pensando que recibirá de una en una y secuencialmente las distintas filas de la lista tdm.
# Añadir el Tema a cada documento
unirTemaTDM <- function(tdm){
# Por un lado guardamos el segundo valor de la lista tdm, es decir, la matriz TDM de términos o palabras.
# La función t() nos ayudará a trasponer la matriz, es decir, a intercambiar filas por columnas.
s.mat <- t(data.matrix(tdm[["tdm"]]))
# La convertimos a data.frame que vendría a ser como un formato excel (filas, columnas y celdas con valores)
# En este data.frame o excel, tenemos que cada fila es un documento, cada columna una palabra y las celdas contienen la frecuencia en que cada palabra aparece en cada documento.
s.df <- as.data.frame(s.mat, stringsAsFactors = FALSE)
# En la última columna colocaremos el Tema de cada documento tdm[["name"]. Para ello usaremos dos funciones cbind() y rep()
# Recordemos que en la lista tdm habíamos almacenado el tema en el valor "name"
# Mediante la función rep() repetiremos el tema del documento tantas veces como filas hay en el data.frame
nueva_columna <- rep(tdm[["name"]], nrow(s.df))
# Recordemos que la función cbind() sirve para concatenar columnas
s.df <- cbind(s.df, nueva_columna)
# Finalmente asignamos un nombre a la última columna para que nos sea más fácil identificarla
colnames(s.df)[ncol(s.df)] <- "temaObjetivo"
return(s.df)
}
Aplicaremos la función unirTemaTDM() a cada fila de la lista tdm con el doble objetivo de convertir la lista en data.frame y de añadir una nueva columna con el Tema de cada documento.
# Construimos un bucle para llamar a la función unirTemaTDM para cada fila de la matriz tdm.
temaTDM <- lapply(tdm, unirTemaTDM)
# Visualizamos la frecuencia de 5 términos en los 20 documentos de tema Crudo.
n2 <- ncol(temaTDM[[2]])
n1 <- n2-5
temaTDM[[2]][,n1:n2]
## weak week will world year temaObjetivo
## 1 2 0 0 0 0 Crudo
## 2 0 1 3 1 0 Crudo
## 3 0 0 1 0 0 Crudo
## 4 0 0 1 0 0 Crudo
## 5 0 0 0 0 1 Crudo
## 6 1 1 2 1 1 Crudo
## 7 1 0 0 3 1 Crudo
## 8 0 0 1 0 0 Crudo
## 9 0 0 0 1 5 Crudo
## 10 0 0 1 1 1 Crudo
## 11 1 5 1 0 0 Crudo
## 12 0 0 1 0 0 Crudo
## 13 0 0 1 1 0 Crudo
## 14 1 1 0 1 0 Crudo
## 15 0 0 0 0 0 Crudo
## 16 0 0 0 0 1 Crudo
## 17 0 0 0 0 1 Crudo
## 18 0 0 0 0 0 Crudo
## 19 0 0 6 1 0 Crudo
## 20 0 0 0 0 0 Crudo
En realidad en temaTDM tenemos una agrupación de dos data.frames (uno por Tema).
Lo que queremos hacer ahora es fusionarlos en un data.frame simple, y lo haremos apilando uno sobre el otro, es decir, agregando o concatenando sus filas. Pare ello usaremos la función rbind().
Atención! porque esto nos genera la siguiente situación:
Tendremos combinaciones vacías, es decir, cruces de palabras y documentos de los que no tenemos datos de frecuencias.
Dicho de otro modo, tendremos palabras de los documentos de Adquisiciones que en las filas correspondientes a los documentos de Crudo, no tendrán valores. En estos casos la función rbind.fill asignará el valor “na” (not available).
# Aplicaremos la función rbind.fill del paquete plyr, con esto conseguiremos una fusión o unión de los dos data.frame, el de Adquisiciones y el de Crudo. Para las celdas de las que no se tengan valores de frecuencias se tomará el valor "na"" (not available). En definitiva, construimos la matriz de datos sobre la que aplicaremos el algoritmo K-NN.
tdm.pila <- do.call(rbind.fill, temaTDM)
# Para facilitar la suma de frecuencias, substituimos el valor "na" por el numérico 0.
tdm.pila[is.na(tdm.pila)] <- 0
# Podemos observar como ya tenemos en un único data.frame las palabras en columnas, los documentos en filas y las frecuencias en celdas.
# Visualizamos las 8 primeras palabras en los 10 primeros documentos.
tdm.pila[1:10,1:8]
## acquir acquisit agre agreement alreadi approv bank board
## 1 1 0 0 0 0 0 0 0
## 2 0 3 0 0 0 0 0 0
## 3 0 0 1 0 0 1 0 0
## 4 1 0 0 0 0 0 0 1
## 5 1 0 0 0 0 0 0 0
## 6 0 0 0 0 0 0 0 0
## 7 0 1 0 0 0 0 0 0
## 8 0 0 0 0 0 0 0 0
## 9 0 0 0 0 0 1 0 0
## 10 0 0 0 0 0 0 0 0
Cada fila representa un documento, cada columna una palabra y las celdas son la frecuencia de aparición de esa palabra en ese documento.
# tenemos 70 documentos
nrow(tdm.pila)
## [1] 70
# tenemos n palabras en los 70 documentos
ncol(tdm.pila)
## [1] 135
Construimos un juego de datos de entrenamiento con el 70% de los documentos, es decir, 49 documentos.
Así mismo construiremos un juego de datos de pruebas con el 30% de documentos restante, es decir 21 documentos.
# Fijamos una semilla para poder repetir la práctica obteniendo los mismos resultados.
set.seed(111)
# 70% de los documentos para entrenamiento
entrena.idx <- sample(nrow(tdm.pila), ceiling(nrow(tdm.pila) * 0.7))
# El resto de documentos para pruebas
test.idx <- (1:nrow(tdm.pila))[-entrena.idx]
# Documentos usados para el entrenamiento
entrena.idx
## [1] 42 51 26 35 25 28 1 34 27 6 63 67 4 3 9 66 10 52 17 32 22 14 53
## [24] 19 45 15 29 13 60 55 57 20 18 38 43 41 58 68 21 59 39 54 37 33 46 44
## [47] 11 30 49
# Documentos usados para la verificación
test.idx
## [1] 2 5 7 8 12 16 23 24 31 36 40 47 48 50 56 61 62 64 65 69 70
Para poder aplicar el algoritmo de aprendizaje por vecindad K-NN necesitamos realizar unas pequeñas adaptaciones.
Éstas consisten en separar por un lado los temas y por otro la matriz de frecuencias.
# guardamos por un lado los temas
tdm.tema <- tdm.pila[, "temaObjetivo"]
# y por otro lado el resto de palabras
tdm.pila.nl <- tdm.pila[, !colnames(tdm.pila) %in% "temaObjetivo"]
Aplicamos el modelo K-NN, pasándole como parámetros la matriz de frecuencias de los documentos de entrenamiento, la matriz de frecuencias de los documentos de pruebas y los temas de los documentos de entrenamiento.
Los temas de los documentos de prueba no se los pasamos, porque precisamente es lo que el algoritmo debe predecir.
Recordamos que el objetivo del modelo será el de predecir el tema de los documentos de pruebas.
# Modelo KNN
knn.pred <- knn(tdm.pila.nl[entrena.idx, ], tdm.pila.nl[test.idx, ], tdm.tema[entrena.idx])
Una vez aplicado el modelo K-NN sobre el juego de documentos de prueba, podemos utilizar una matriz de confusión para valorar el nivel de acierto del modelo.
# Matriz de confusión
# Las filas son predicciones y las columnas son observaciones reales
conf.mat <- table("Predicción" = knn.pred, Real = tdm.tema[test.idx])
conf.mat
## Real
## Predicción Adq Crudo
## Adq 14 2
## Crudo 0 5
Observamos como K-NN, de los 21 documentos, ha clasificado correctamente 19:
y ha fallado en 2 documentos, puesto que los ha clasificado como Adquisición cuando en realidad eran Crudo.
Como medida de precisión del algoritmo, podemos tomar la suma de la diagonal de la matriz de confusión (clasificaciones acertadas), dividido por el número de documentos de prueba.
# Medida de precisión
precision <- sum(diag(conf.mat)) / length(test.idx) * 100
precision
## [1] 90.47619
Observamos como K-NN ha superado el 90% de acierto.
En el apartado 6.3 se construye un gráfico de barras con las palabras más frecuentes para la temática adquisiciones. Haced dos gráficos similares para las temáticas “crudo” y “adquisiciones”, respectivamente, en los que representéis aquellas palabras con una frecuencia superior a 15. Investigar si hay coincidencias y comentad que implicación tendrían estas si el objetivo es construir un modelo de clasificación de nuevos documentos en una u otra temática.
En el apartado 6.4. se construye una nube de palabras para la temática “adquisiciones”, tenéis que construir una similar para la temática “crudo”. Intentad cambiar algunos valores del as opciones (frecuencia, color,.) de la función wordcloud() para per como cambia la nube.
¿Es cierta la siguiente afirmación?: Cuanto mayor es el número de elementos iguales a cero en la matríz de términos menor es la dispersión asociada a dicha matriz y, por tanto, menor es el ratio “Sparsity” y mayor la “Density”.
Justificad la respuesta, comentando cual sería la implicación en la capacidad predictiva de un algoritmo de clasificación como es el K-NN.
En el apartado 7 se presenta la matriz de confusión del modelo generado. De esta matriz se extrae el ratio de precisión o porcentaje de clasificación correcta, pero pueden deducirse otros ratios. Calculad e interpretad dicho porcentaje y los ratios exactitud, especificidad y prevalencia, suponiendo que la categoría de referencia o “verdadera” es Adquisición. Interpretad los resultados.
Mirando la ayuda de la función K-NN observaréis que por defecto se define k=1. Volved a ejecutar la función K-NN cambiando el valor del parámetro k. Describid cuál es el papel de este parámetro y que efecto tiene sobre los resultados. Para responder al ejercicio tendréis que reproducir la matriz de confución para cada valor de R e analizar las diferencias. Comentad también que pensáis acerca de pensáis sobre la influencia del valor de k.
El algoritmo K-NN que se define en R se basa en una distancia matemática, ¿de qué distancia se trata?. Describid cuál es el papel de dicha distancia en la fase de predicción del algoritmo K-NN.
Proponed alguna o algunas distancias alternativas que podrían utilizarse en el K-NN.