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!

Hay 10 comentarios

Añade el tuyo
  1. 1
    Jorge Spuler

    He leído muy interesado su artículo y me parece muy bueno para adentrarme en la clasificación de textos, pero no logro entender bien como es que cargan los datos en R.
    y como queda el corpus.

    ¿En qué formato están los datos en datos.csv y que son los colnames?

    Sospecho que serían algo así, perno no estoy seguro
    datos.cvs = “súper buenos los mejores, no me gusto, , ah?, es en serio”
    colnames.txt=“tuit_1, tuit_2, tuit_3, tuit_4, tuit_5”

    Para asociarle a cada texto un nombre

    Atentamente Jorge Spuler

  2. 2
    Urko Zurutuza

    Muchas gracias por el comentario y por tu interés Jorge. En realidad datos.csv sería un a tabla de excel (por ejemplo), con columnas de tipo fecha del tuit, un identificador, el autor, el propio texto, etc. y en cada fila tendríamos cada uno de los tuits recopilados.

    Como en un principio no tenía un «título» para cada columna, se lo agrego mediante los nombres del fichero colnames.txt:

    datos.csv= «231235»,»2012-11-08 10:09:51″,»twiteroX»,»súper buenos los mejores, no me gusto,ah?, es en serio»
    colnames.txt= id fecha autor tuit (cada nombre en una línea)

    Así, la estructura de datos «txtrepo», queda en forma de una tabla de la siguiente manera:

    id | fecha | autor | descripcion
    —+——————-+——–+—————–
    1 | 2012-11-08 10:09:51 | twiteroX | súper buenos
    —+——————-+——–+—————–
    2 | 2012-11-08 11:08:21 | twiteroY | no tantos

    Y como verás, en la estructura «txtdataset», solo me quedo con la columna descripción, que es la que me interesa para hacer la minería de textos. Y al final del todo, le añado una columna nueva a «txtrepo» generando grupos de tuits con descripciones similares.

    Un saludo,

    Urko

  3. 3
    Jorge Spuler

    Estimado Urko Zurutuza:

    Su código ha sido muy explicativo y ejemplificador, ya tengo la matriz de distancia, pero en esta parte del código, donde entiendo agrupa por similitud los tuits

    result <- cbind(subset(txtrepo, descripcion!="\\N"),
    label = addLabel(euc.df)$label)

    Obtengo el error "no se pudo encontrar la función "addLabel"

    No pude encontrar la función addLabel por internet para ver cómo funcionaba, no sé si falla porque me falta una librera, si hay un error de tipeo, o si la vercion de R que utilizo (R 3.0.1) excluye esta función.

    Por favor me puede indicar porque está fallando

    • 4
      Urko Zurutuza

      Hola Jorge,

      Tal y como expongo en el post, la función AddLabel es una función creada manualmente. Tal y como se ve en el código de la llamada, es una función que a partir de la matriz de distancias añade una columna (etiquetas) al conjunto de datos original para «etiquetar» cada tuit, asignando el mismo valor a aquellos comentarios con una distancia menor al umbral establecido. Por razones de confidencialidad, no puedo proporcionar el código, pero estoy seguro que podrías crearlo tú mismo con poco esfuerzo.

      Un saludo,

  4. 5
    Cars

    Urko
    Sencillamente emocionante!
    No se nada de mineria de textos!
    Estoy metido en un proyecto interesante, que he ido realizando intuitivamente hasta el punto en el cual excel ya me resulta estrecho. Acabo de comprender que la solución es la mineria de textos, a pesar de estar bastante cerca de la verdad, segun he podido leer en tu articulo.
    Tengo algunas cosas que me preocupan, como el hecho de que algunos textos que proceso hablan de 3 o 4 temas diferentes y que debo separar, tambien que deseo aplicar escala de valores a adjetivos para evaluar el estado emocional del cliente y otras cosas mas. Mi cosuelo es que estoy en el inicio.
    Muy agradecido
    cars

    • 6
      Urko Zurutuza

      Cars, me alegra que te haya parecido interesante. El proyecto que comentas también tiene muy buena pinta, y seguro que conseguirás avanzar. Existen muchas aproximaciones y algoritmos para agrupar los textos, por lo que sigue trabajando que seguro que hallarás el modo!

      Un saludo,

  5. 7
    mike

    Muy intuitivo tu artículo, lo que al final no se cómo ya en el agrupamiento sabes a quien pertenece cada mensaje. O sea en los grupos que creas tienes forma de saber quienes son los autores de los mensajes… Porque no veo que conservas el Id o autor para luego conocer a quien pertenece.

    Saludos

    • 8
      Urko Zurutuza

      Muchas gracias Mike,

      Si te fijas, al inicio cargo el fichero en una estructura llamada txtrepo. Tras todo el procesamiento, el resultado (result) es la unión de los atributos iniciales del fichero (txtrepo, que ya contiene el ID), al que se le añade el identificador (label) del cluster al que pertenece.

      Un saludo,

  6. 9
    Catalina

    Saludo cordial me gustaría conocer como fue el proceso de stop words existen bases de datos para esto en español o ustedes la construyeron
    Gracias

Responder a Urko Zurutuza Cancelar la respuesta