1 Base teórica


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.

El análisis que planteamos se basa en el hecho de que en documentos pertenecientes a un mismo tema aparecerán palabras que se repetirán con mayor frecuencia. Por lo tanto, el análisis que presentamos plantea la clasificación de documentos utilizando como criterio las palabras que son más frecuentes en cada temática. Estas frecuencias se almacenarán en una matriz de datos que será la base para que puedan trabajar los algoritmos de aprendizaje automático como el K-Nearest Neighbors (K-NN) utilizado en esta práctica.

En este ámbito de conocimiento se basan los sistemas de clasificación documental, búsqueda de contenidos y sistemas de recomendación entre otros.

Recursos en la web:


2 Caso de estudio


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 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:

  1. txt/acq con 50 noticias suministradas por la agencia Reuters sobre la temática de las Adquisiciones Empresariales (temática “Adquisiciones”).

  2. txt/crude con 20 noticias suministradas también por la agencia de noticias Reuters, sobre el sector del Petróleo o Crudo (Temática “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”.


3 Apartados de la práctica


El código R que utilizaremos en la práctica se divide en apartados según las tareas que iremos realizando:


4 Inicialización de variables


Instalamos los packages de R que necesitaremos para realizar la práctica:

Generamos campos para guardar las dos temáticas y la ruta básica del directorio en el que tenemos los documentos.

## Cargamos los paquetes necesarios para ejecutar las funciones que se describen a continuación:
# 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 modelización estadística.


# 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
nombreruta <- paste(getwd(),"/txt", sep = "")

5 Limpieza y acondicionado del texto


La función acondicionaCorpus() realizará tareas de acondicionado de texto, como:

  1. Eliminar signos de puntuación.
  2. Eliminar espacios en blanco innecesarios.
  3. Convertir todo el texto a minúsculas.
  4. Eliminar palabras sin significado propio.
  5. Eliminar números.
  6. Substituir las palabras derivadas por su palabra raíz.
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)
  }

6 Generación de la Matriz de Términos


La función generaTDM() recibirá como parámetro la ruta raíz de los documentos de 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, sparc){

# 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 con poca presencia en el conjunto de documentos
s.tdm <- removeSparseTerms(s.tdm,  sparc)
# 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)
}

6.1 Creación de la Matriz de Términos 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, sparc=0.85)
# 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 2 3 4 5 6 7 8 9 10 ...
##   .. ..$ 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" "buy" "common" "compani" ...
##   .. .. ..$ 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] 1 2 3 4 5 6 7 8 9 10 ...
##   .. ..$ 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] "barrel" "bring" "compani" "contract" ...
##   .. .. ..$ 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"

6.2 Visualizaciones sobre la matriz de palabras TDM


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: 110/190
## Sparsity           : 63%
## Maximal term length: 8
## Weighting          : term frequency (tf)
## Sample             :
##          Docs
## Terms     1 10 2 3 4 5 6 7 8 9
##   common  3  0 0 0 0 0 2 0 2 0
##   compani 4  1 1 0 9 1 0 5 1 0
##   dlrs    4  1 2 0 4 0 1 2 1 0
##   inc     2  1 1 3 3 2 0 1 0 0
##   market  1  0 0 0 0 0 0 9 0 0
##   mln     1  1 2 0 3 0 1 3 1 0
##   pct     2  0 1 0 0 0 2 2 2 0
##   reuter  1  1 1 1 1 1 1 1 1 1
##   share   5  0 1 0 6 0 4 0 3 2
##   stock   3  0 1 0 1 0 1 8 2 1
# Frecuencia de los 30 primeros términos en todos los documentos del tema Adquisiciones
inspect(tdm[[1]]$tdm[1:30,])
## <<TermDocumentMatrix (terms: 30, documents: 50)>>
## Non-/sparse entries: 505/995
## Sparsity           : 66%
## Maximal term length: 8
## Weighting          : term frequency (tf)
## Sample             :
##          Docs
## Terms     1 19 22 25 29 34 4 42 47 7
##   common  3  0  0  0  2  0 0  0  2 0
##   compani 4  5  2  3  7  3 9  7  6 5
##   dlrs    4  0  5  3 11  6 4  2 17 2
##   inc     2  0  1  2  3  4 3  0  5 1
##   mln     1  1  3  1  5  0 3  2  7 3
##   pct     2 10  4  2  2  7 0  7  2 2
##   reuter  1  1  2  1  1  1 1  1  1 1
##   share   5  6  1  0  6  5 6  2  7 0
##   stock   3  0  0  2  5  1 1  0  4 8
##   will    0  2  2  1  9  0 1  3  0 0
# Frecuencia de los términos en los documentos del tema Adquisiciones
inspect(tdm[[1]]$tdm)
## <<TermDocumentMatrix (terms: 60, documents: 50)>>
## Non-/sparse entries: 817/2183
## Sparsity           : 73%
## Maximal term length: 10
## Weighting          : term frequency (tf)
## Sample             :
##          Docs
## Terms     19 22 25 29 34 39 4 42 47 7
##   compani  5  2  3  7  3  1 9  7  6 5
##   dlrs     0  5  3 11  6  0 4  2 17 2
##   inc      0  1  2  3  4  0 3  0  5 1
##   mln      1  3  1  5  0  0 3  2  7 3
##   offer    0  1  1  5 10  0 4  0 10 0
##   pct     10  4  2  2  7  2 0  7  2 2
##   reuter   1  2  1  1  1  1 1  1  1 1
##   share    6  1  0  6  5  0 6  2  7 0
##   stock    0  0  2  5  1  0 1  0  4 8
##   will     2  2  1  9  0  1 1  3  0 0
# Inventario de los primeros términos del Tema Adquisiciones
head(tdm[[1]]$tdm$dimnames$Terms)
## [1] "acquir"  "buy"     "common"  "compani" "complet" "dlrs"
# Número de documentos
nDocs(tdm[[1]]$tdm)
## [1] 50
# Número de términos
nTerms(tdm[[1]]$tdm)
## [1] 60
# Visualizamos los términos con más de 2 apariciones en documentos de temática Adquisiciones
findFreqTerms(tdm[[1]]$tdm, lowfreq=10)
##  [1] "acquir"     "buy"        "common"     "compani"    "complet"   
##  [6] "dlrs"       "hold"       "inc"        "make"       "market"    
## [11] "mln"        "oper"       "outstand"   "pct"        "plan"      
## [16] "purchas"    "reuter"     "sale"       "share"      "stock"     
## [21] "total"      "year"       "acquisit"   "capit"      "march"     
## [26] "may"        "profit"     "will"       "agre"       "approv"    
## [31] "corp"       "expect"     "industri"   "subsidiari" "unit"      
## [36] "board"      "cash"       "interest"   "manag"      "offer"     
## [41] "group"      "exchang"    "firm"       "new"        "secur"     
## [46] "stake"      "four"       "sell"       "valu"       "own"       
## [51] "merger"     "receiv"     "sharehold"  "agreement"  "bank"

7 Descripción de la matriz de términos.



7.1 Representación gráfica de las frecuencias


# 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)
d_adq[,3]<-"adq"
# Hacemos lo mismo para Crudo
mat_crude <- as.matrix(tdm[[2]]$tdm)
# Agregamos las frecuencias por términos y las ordenamos de mayor a menor  
v_crude <- sort(rowSums(mat_crude), decreasing=TRUE)
# Creamos un data.frame con términos y frecuencias
d_crude <- data.frame(word=names(v_crude), freq=v_crude)
d_crude[,3]<-"crude"

# Concatenamos las dos matrices
data<-rbind(d_adq,d_crude)
colnames(data)
## [1] "word" "freq" "V3"
# Gráfico de barras con las palabras más frecuentes
library(ggplot2)
ggplot(subset(data, freq>20), aes(word, freq, fill=V3))+ geom_bar(stat="identity")+theme(axis.text.x=element_text(angle=45, hjust=1))


7.2 Construcción de una nube de palabras


Construiremos una nube de palabras para cada TDM (Term Document Matrix) contenida en el objeto tdm, Adquisiciones.

# Cargamos la librería wordcloud
require(wordcloud)
# Construimos la nube de palabras o términos
wordcloud(d_crude$word, d_crude$freq, min.freq=5, random.color=FALSE, colors=rainbow(1))


8 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]
##    remain sever saudi accord arabia temaObjetivo
## 1       0     0     0      0      0        Crudo
## 2       0     0     0      0      0        Crudo
## 3       0     0     0      0      0        Crudo
## 4       0     0     0      0      0        Crudo
## 5       0     0     0      0      0        Crudo
## 6       0     0     0      0      0        Crudo
## 7       1     1     0      0      0        Crudo
## 8       0     1     1      0      0        Crudo
## 9       0     0     0      0      0        Crudo
## 10      0     0     5      5      3        Crudo
## 11      1     0     7      1      3        Crudo
## 12      0     0     1      0      1        Crudo
## 13      0     0     4      2      2        Crudo
## 14      0     0     0      0      0        Crudo
## 15      0     0     0      0      0        Crudo
## 16      1     1     0      0      0        Crudo
## 17      1     1     0      0      0        Crudo
## 18      0     0     0      0      0        Crudo
## 19      0     0     0      4      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 buy common compani complet dlrs hold inc
## 1       1   1      3       4       1    4    1   2
## 2       0   0      0       1       0    2    0   1
## 3       0   0      0       0       1    0    0   3
## 4       1   0      0       9       0    4    0   3
## 5       1   0      0       1       0    0    0   2
## 6       0   0      2       0       0    1    0   0
## 7       0   0      0       5       0    2    0   1
## 8       0   0      2       1       0    1    0   0
## 9       0   0      0       0       0    0    0   0
## 10      0   0      0       1       0    1    0   1

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

8.1 Construcción del Modelo de clasificación


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])

9 Validación del Modelo de clasificación


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 16:

y ha fallado en 5 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
ratio <- sum(diag(conf.mat))/length(test.idx)*100
ratio
## [1] 90.47619

Una medida alternativa para validar el modelo de clasificación es:

ratio2 <- (conf.mat[1,1]/(conf.mat[1,1]+conf.mat[2,1]))*100
ratio2
## [1] 100

10 Ejercicios


10.1 Ejercicio 1:

En la función “acondicionaCorpus”, que se define al inicio del apartado 5, se incorporan los siguientes comandos:

**corpus.tmp <- tm_map(corpus.tmp, stripWhitespace)* **corpus.tmp <- tm_map(corpus.tmp, content_transformer(tolower))*

Describid detalladamente que acciones realizan ambos comandos. Justificad por qué se han incorporado ambos comandos en el proceso de acondicionamiento de los textos.

El primer comando elimina los espacios en blanco y el segundo transforma todas las palabras en minúscula.Forman parte de la función de acondicionado del texto para un uso óptimo a la hora de extraer cada término y poder contabilizarlo según los temas en el caso de esta práctica.

10.2 Ejercicio 2:

En el apartado 6 se define la función denominada generaTDM, que se utiliza para generar la matriz de términos a partir de la cual implementaremos el algoritmo preditivo propuesto posteriormente. Concretamente, en el apartado 6.2 se muestron algunos ejemplos de visualización de distintas secciones de la matriz de términos. Visualizad los 10 primeros términos en la temática “Crudo”.

inspect(tdm[[2]]$tdm[1:10,])
## <<TermDocumentMatrix (terms: 10, documents: 20)>>
## Non-/sparse entries: 67/133
## Sparsity           : 66%
## Maximal term length: 8
## Weighting          : term frequency (tf)
## Sample             :
##           Docs
## Terms      1 10 11 16 17 18 3 4 6 7
##   barrel   2  3  3  3  3  1 1 1 4 0
##   bring    1  0  0  0  0  1 1 1 0 0
##   compani  2  0  0  0  0  2 1 1 2 0
##   contract 2  0  0  0  0  0 1 1 0 1
##   crude    2  0  5  0  0  2 2 3 2 0
##   cut      2  0  1  0  0  1 0 0 0 0
##   day      1  1  1  0  0  0 0 0 0 0
##   dlrs     2  4  2  1  1  5 1 2 2 1
##   effect   1  0  0  0  0  1 1 1 0 0
##   fall     1  1  0  0  0  0 0 0 0 3

10.3 Ejercicio 3:

Entre los resultados obtenidos en el ejercicio 3 se obtiene un valor denominado “Sparsity”, interpretad dicho valor y describid detalladamente como se calcula.

Indica el nivel de dispersión, un nivel alto indica que hay términos que no aparecen en muchos documentos, conforme reduzcamos el nivel de dispersión obtendremos menos términos, ya que, por ejemplo, en un nivel de dispersión del 66% en 20 documentos, tendría cada término que aparecer como minimo 6,7~7 veces en el total de los 20 documentos: 20 multiplicado (1-0,66)=6,8.y con un 40% de dispersión, tendriamos que tener al menos 12 veces cada término en el total de los 20 documentos, 20 multiplicado (1-0.40) = 12 . El nivel de dispersion se calcula sumando la cantidad de ceros que hay en el total de las celdas y dividiendo este importe por el total de celdas. Total celdas = número de términos multiplicado por numero de documentos.

10.4 Ejercicio 4:

Construye un gráficos de barras con las palabras con una frecuencia mayor que 50 de la temática “Adquisiciones”.

ggplot(subset(d_adq, freq>50), aes(word, freq, fill=V3))+ geom_bar(stat="identity")

10.5 Ejercicio 5:

En el apartado 7.1 se construye un gráfico de barras apiladas con las palabras más frecuentes en ambas temáticas. Indicad si es cierta la siguiente afirmación: “El hecho de que haya muchas barras con un único color favorece la capacidad predictiva del algoritmo utilizado para la clasificación de los documentos”.

Si, ya que en el caso de un único color, esos téminos solo están para ese tema. Términos como “market” son muy comunes en el caso de las dos tématicas y no ayudan al algoritmo a diferenciar a cual pertenece.

10.6 Ejercicio 6:

En el apartado 7.2. se construye una nube de palabras para la temática “Crudo” utilizando un único color. Modificar dicha nube permitiendo que el número de colores utilizados sea 5 y las palabras representadas tengan al menos una frecuencia de 10 apariciones en los documentos de dicha temática. Posteriormete, construir la misma nube de palabras para la temática “Adquisiciones”.

Temática “Crudo”:

wordcloud(d_crude$word, d_crude$freq, min.freq=10, random.color=FALSE, colors=rainbow(5))

Temática “Adquisiciones”:

wordcloud(d_adq$word, d_adq$freq, min.freq=10, random.color=FALSE, colors=rainbow(5))

10.7 Ejercicio 7:

En el apartado 9 se presenta la matriz de confusión del modelo generado. De esta matriz se extrae un ratio. Se supone que la categoría de referencia o “verdadera” es Adquisición. Determinad que ratio (ratio2) hemos calculado y obtened otro alternativo. Interpretad los resultados.

Se ha usado el ratio: (precision, PRE) TP/TP+FP . Mide el rendimiento relacionado con las tasas de verdaderos positivos y negativos. Al dar 100, significa que todos los temas considerados como adquisisión positivamente son correctos al 100%. Podríamos usar el ratio: (speci???city, SPE). Es la tasa de instancias correctamente clasi???cadas como negativas respecto a todas las instancias negativas.Se calcula como: TN/TN+FP: 5/5+0= 100%. Todas las instancias negativas han sido correctamente clasificadas.

10.8 Ejercicio 8:

En el apartado 8.1 se utiliza la función “knn” para clasificar los documentos según temática. Esta función por defecto supone que el número de vecinos a evaluar es igual a 1. Valorad los resultados del algoritmo K-NN utilizando distinto número de vecinos, por ejemplo, 2, 4, y 8.

knn.pred <- knn(tdm.pila.nl[entrena.idx, ], tdm.pila.nl[test.idx, ], tdm.tema[entrena.idx], k=2)
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
knn.pred <- knn(tdm.pila.nl[entrena.idx, ], tdm.pila.nl[test.idx, ], tdm.tema[entrena.idx], k=4)
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
knn.pred <- knn(tdm.pila.nl[entrena.idx, ], tdm.pila.nl[test.idx, ], tdm.tema[entrena.idx], k=8)
conf.mat <- table("Predicción" = knn.pred, Real = tdm.tema[test.idx])
conf.mat
##           Real
## Predicción Adq Crudo
##      Adq    14     5
##      Crudo   0     2

Para K=2 y K=4 no hay diferencia, pero para K=8 el falso negativo de la matriz de confusión se incrementa en 3 que pierde del verdadero negativo. Se pierden temas de crudo correctamente adjudicados al ser verdaderos negativos desde el punto de vista del tema adquisición, convirtiendose en falsos negativos, es decir, se tiende a no obtener ningun tema de crudo correctamente conforme aumentamos el número de vecinos.

10.9 Ejercicio 9:

A partir de los resultados obtenidos en el ejercicio 8, analizad si es posible mejorar la capacidad predictiva del algoritmo si se incrementa el número de vecinos. Justificad la respuesta.

No,conforme se incrementa K el algoritmo pierde precisión. Con k=1, K=2 y K=4 la presicion es la misma: 90,47%, con k=8 es de 76,19% y para k=22 bajaría a 71,43%… Al ser el número de K demasiado grande incluye puntos de otras clases lejanas(aumenta la distancia) como propias, esto hace que detecte mas Falsos Negativos, es decir que el algoritmo ,desde el punto de vista de adquisición, llega a no detectar ningún Verdadero negativo pasando a ser estos falsos negativos..

10.10 Ejercicio 10:

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.

Se hace uso de la distancia Euclídea y supone que los vecinos más cercanos nos dan la mejor clasificación ya que hay una mayor concentración aunque con riesgo de ruido.Una Kentre 3 y 5 se considera acpetable para eliminar este ruido, aunque depende del caso puede ser mayor.