Investigación en TICs

Minería de Textos para la Clasificacion de Documentos en Español con R

El equipo de Telemática entregó recientemente un trabajo a una empresa que sin duda mejorará su producto. Se trata de un sistema de clasificación de textos, o dicho de otro modo, un método para agrupar documentos o conjuntos de textos parecidos.

La aproximación se ha realizado utilizando el paquete estadístico R, y aunque en un principio se pensaba que quizás haría falta aplicar algoritmos de data mining y clustering sobre los textos, se ha comprobado que lo sencillo es muchas veces lo más óptimo…

El proceso sería el siguiente:

1. Librerías necesarias:

library(tm) # para Text Mining
library(Snowball) # para Stemming
library(slam) # estructuras y algoritmos para arrays y matrices
library(Matrix) # para trabajar con matrices

 

2. Carga de datos en R

Partiendo de un fichero .csv (comma separated values), donde en una de las columnas se encuentran los textos (como por ejemplo el contenido de un tuit), añadimos los nombres de las columnas y creamos lo que para la librería tm (text mining) sería un Corpus:

txtrepo = read.csv("datos.csv")
cols <- readLines("colnames.txt",encoding="UTF-8")
colnames(txtrepo) <- cols

Como un preproceso inicial, eliminamos las columnas vacías (como por ejemplo tuits sin texto en la columna descripción):

txtdataset <- subset(txtrepo, descripcion!="\\N")

Creamos el corpus a partir del vector (o columna) descripción:

micorpus <- Corpus(VectorSource(txtdataset$descripcion),
                   readerControl = list(reader = readPlain,
                   language = "es",
                   load = TRUE))

 

3. Preproceso de datos

Tenemos un vector con el texto en crudo a clasificar, pero antes de nada es necesario preprocesarlo para eliminar e igualar los términos. Así, quitamos los números, los signos de puntuación, palabras de parada (artículos, pronombres,…), lo ponemos todo en minúscula, quitamos espacios en blanco sobrante, y aplicamos stemming (reducir las palabras a su raíz, como de «clasificar» a «clasific»).

micorpus <- tm_map(micorpus, removeNumbers)
micorpus <- tm_map(micorpus, removePunctuation)
micorpus <- tm_map(micorpus, tolower)
micorpus <- tm_map(micorpus, removeWords, stopwords("spanish"))
micorpus <- tm_map(micorpus, stemDocument, language="spanish")
micorpus <- tm_map(micorpus, stripWhitespace)

Después creamos lo que se conoce como una matriz de términos del documento (Document Term Matrix). Se trata de una matriz de dimensión mxn, donde m sería el número de descripciones (o documentos, o tuits,…) a procesar, y n sería el número de términos existentes en esos documentos. Los valores de la matríz sería el número de veces que cada documento (fila) contiene el término (columna) dado.

dtm <- DocumentTermMatrix(micorpus) 
dtm.data <- as.matrix(dtm) # Lo mismo pero en formato de datos

 

4. Análisis estadístico

Finalmente, como he comentado al inicio, si el objetivo es únicamente agrupar los tuits más parecidos, un simple análisis estadístico comparando las distancias entre documentos resultaría suficiente. Por ejemplo, se trataría de crear una nueva matriz de distancias, siendo el resultado una matriz cuadrada de distancias (Distance Matrix) nxn, donde computamos la distancia de cada documento (filas) con el resto de documentos (columnas). Así, empezaría calculando la distancia (Euclídea, Manhattan, Binaria,…) entre el primer documento y é mismo (sindo su distancia 0 ya que son el mismo documento), poniendo su valor en la primera casilla de la matriz:

distancias <- as.matrix(dist(dtm.data, 
                             method = "binary", 
                             diag = TRUE, 
                             upper = FALSE, 
                             p = 2))

 

5. Post-procesado

Una vez computado, lo único que haría falta es establecer un umbral (cuanto más cercano a «0», más parecidos son), y a partir de este momento escribir una nueva columna agrupando los tuits parecidos. Esto se puede hacer sencillamente con una función en R (que llamo addLabel).

# Añadimos el numero de cluster en cada tuit del fichero:
 euc.df <- as.data.frame(distancias)
 result <- cbind(subset(txtrepo, descripcion!="\\N"), 
                 label = addLabel(euc.df)$label)
 write.csv2(result, file="results.csv", fileEncoding="utf8")

Gero arte!

Salir de la versión móvil