¿Qué es NLP?…procesamiento de lenguaje natural

Las entradas de esta categoría contienen ejemplos de NLP, pero en general la implementación de la librería NLTK de python y su contra parte en tm en R project, pero principalmente los ejemplos son en Python. La idea es compartir de manera breve algunos detalles y ejemplos que en buena parte provienen de la referencia [1] y están disponibles en línea en la referencia [2].

Agrego algunos comentarios o variaciones, la intención con estas entradas es motivar a que se consulten otras fuentes y  comentar más adelante sobre lo avances recientes en Deep Learning en NLP, sobre lo cual se puede consultar la referencia [3].

También comparto unos ejemplos haciendo uso de otros módulo, scikit-learn, para implementar algunas técnicas de Machine Learning.

En caso de estar interesado en investigar sobre una breve introducción teórica y con muchos detalles en explicación,  para mi gusto una buena referencia son los capítulos 22 y 23 de la referencia [4], en el cual se  desarrolla el marco teórico sobre el cual hasta ahora es en parte vigente la investigación en este tipo de temas. Existen nuevas metodologías y nuevas técnicas, sobre material de investigación por la relevancia que se tienen de mejorar los sistemas y técnicas para el procesamiento de lenguaje natural.

Un ejemplo vistoso….qué no me quedó.

Dejo la liga donde puede visualizar un ejemplo de nubes de palabras hechas con el módulo wordcloud de python. Tuve algunos problemas con las fuentes  para poder correrlos en windows y el código para poder hacer que funcione correctamente, así que posteriormente comparto los detalles del ejemplo que tenía pensado compartir. Puede verse el código de ejemplo de Alicia en el mismo repositorio.

alice

Liga del repositorio: https://github.com/amueller/word_cloud

Comentario: Espero posteriormente corregir los problemas del código con las fuentes de windows.

Un ejemplo no tan vistoso

Algunos conceptos que no menciono a detalle son las listas, tuplas y cadenas. Estas son estructuras de datos de python, se cuenta con mucha información sobre cómo se opera con ellas o qué métodos funcionan.

Un ejemplo sencilla para conocer cómo se trabaja con ellas es usar Ipython  es lo siguiente:

#Listas
a=['1','2','3','4']
#type() dice el tipo de datos que es
type(a)
#En Ipython 
a.<taps>
#Despliega una lista de métodos básicos para las listas

#Cadena
b='1234'
type(b)

b.<tabs>
#Despliega una lista de métodos básicos para las cadenas

#Define una tupla
c=("1","2","3","4")
type(c)
c.<tabs> 
#Despliega dos métodos básicos con las tuplas

En las referencias se pueden consultar operaciones con los métodos o con otras funciones de python. Estas estructuras de datos son importantes, debido a que muchos de los textos con cargados como listas o como cadenas  y se opera con ellos mediante sus operaciones naturales.

Debido a que hablar de listas, tuplas y diccionario requiere mucho detalle recomiendo consultar una breve introducción en línea proporcionada por el grupo de Stanford, el cual es breve y preciso respecto a lo que se necesita saber de esos temas:

Liga del tutorial de Stanford: Introducción a Python

Lo siguientes ejemplos pueden ser enriquecidos consultando la referencia [1] y [2], las cuales son sumamente buenas y contienen prácticamente todo lo que uno debe de saber de NLP, sobre todo del manejo de la librería NLTK.

Lo primero es tener el módulo instalado en python, ya que se tenga instalado lo importante es usar los textos o corpus con los que cuenta la librería.

Para instalar el módulo depende de la distribución de python que se instala pero sobre en general se puede usar el módulo pip para instalar cualquier módulo.

Lo básico es lo siguiente:

#NLTK
import nltk
nltk.download()
#Cargar los libros

nltk.book()
*** Introductory Examples for the NLTK Book ***
Loading text1, ..., text9 and sent1, ..., sent9
Type the name of the text or sentence to view it.
Type: 'texts()' or 'sents()' to list the materials.
text1: Moby Dick by Herman Melville 1851
text2: Sense and Sensibility by Jane Austen 1811
text3: The Book of Genesis
text4: Inaugural Address Corpus
text5: Chat Corpus
text6: Monty Python and the Holy Grail
text7: Wall Street Journal
text8: Personals Corpus
text9: The Man Who Was Thursday by G . K . Chesterton 1908

Ya que se tiene esto, se cuenta con 9 textos con los cuales se puede trabajar o aprender a usar las funciones básicas de NLTK.

En la lógica de procesar textos está en medio el concepto central de Corpus, esto en el fondo significa que es un conjunto de textos. La intención en general es estudiar o construir Corpus y analizarlos.

En los libros disponibles en NLTK en algunos casos son un textos y no una colección, pero por ejemplo, el el texto 3, 4 y 8 son Corpus, en otro caso está formado el Corpus por un solo texto. Pero siendo abstracto, un conjunto es no vacío si cuenta por lo menos con un elemento( distinto al vacío, cosa de teoría de conjunto). Pero en resumen el conecto de Corpus en cualquiera de las notaciones del procesamiento de textos significa un conjunto de textos.

Algunas operaciones sencillas son identificar concordancias, cuando una palabra tiene contextos similares o cuando tienen un contexto común. La diferencia entre similar y común, es que en la primera se explora qué otras palabras tienen el mismo contexto y en común se toman dos palabras y se explora si tienen algo en común en el contexto.

Un tema delicado es la definición de “contexto”, esto para un sentido práctica en el procesamiento tienen la idea de identificar al derredor de que palabras aparece la palabra que buscamos.

Entonces como ejemplo, suponiendo que elegimos el Corpus de discursos, aún que están en inglés una palabra que posiblemente aparezca es ‘nation’, así que podemos explorar su concordancia, las palabras similares y eligiendo alguna de las similares podemos explorar si tienen un contexto común.

#Ejemplo
text4.concordance('nation')

#Lo que se vería en la consola es:
#Displaying 25 of 302 matches:
# to the character of an independent nation seems to have been distinguished by
#f Heaven can never be expected on a nation that disregards the eternal rules o
#first , the representatives of this nation , then consisting of little more th.....

#Reviso las palabras similares en el Corpus
text4.similar('nation')

#Lo que se vería es:

#people country government world union time constitu#tion states
#republic law land party earth future other presiden#t strength war
#congress spirit

#Elijo la palabra 'country' para explorar el contexto común con la palabra 'nation'
# Se debe de ingresar en la función como lista , es decir se usa:[]
text4.common_contexts(["nation","country"])

#our_by of_and no_can the_now and_the our_from the_t#he no_has whole_in
#our_that the_in our_i this_to our_today the_for a_w#e our_in the_are
#the_i the_and

Ahora en  breve lo que hacen las funciones anteriores es encontrar el contexto, las palabras que tienen “contexto similar” y compara el contexto común.

Algo que siempre está presente en el análisis de datos es hacer gráficas, entonces una gráfica fácil de hacer es explorar la dispersión de un conjunto de palabras o como se comporta la frecuencia con la cual aparecen ciertas palabras en un Corpus.

El ejemplo lo hago con el Corpus text4 que son discursos.

#Gráfica de dispersión
text4.dispersion_plot(["nation","people","government","law"])

#Gráfica de la frecuencia de las palabras

fdist=FreqDist(text4)
fdist.plot(50,cumulative=True)

Las gráficas que se obtienen son la siguientes:

Dispersión

Frecuencia_de_Palabras

La primera gráfica es similar a la que se construye para hacer una gráfica  “jitter” de un conjunto de datos, de cierto modo muestra las apariciones de las palabras en el texto y la segunda gráfica muestra cuales palabras domina la distribución de la frecuencia de 50 palabras.

Otro aspecto básico cuando se analizan datos, es conocer información numérica, es decir; en este caso sería importante conocer la cantidad de palabras de un texto, cuantas se usaron sin contar repeticiones y quitar las palabras o puntuaciones que puedan ser contadas como caracteres. Esto último está relacionado con el concepto de “tokenización”  del texto.

El código siguiente hace las operaciones anteriores:

#Conteo
len(text4)
145735
#Contando las palabras y símbolos , sin repetición
len(set(text4))
9754
#Porcentaje en el que aparecen las palabras
from __future__ import division

len(text4)/lent(set(text4))

14.941049825712529

#Conteo de apariciones de una palabra en el texto

text4.count('nation')
235

#Porcentaje de apariciones en todo el texto

text4.count('nation')/len(text4)
0.0016125158678423166
text4.count('and')/len(text4)
0.03424709232511065

Esto último no tienen mucha relevancia cuando se piensa en un solo texto, pero pensando que se tienen varios textos que analizar puede resultar interesante como se comporta la densidad de los textos y comparar entre ellos.

Un ejemplo den R.

Para hacer el ejemplo en R uso algunos librerías y un texto; Alicia en el país de las maravillas. Para el ejemplo hago uso de las librerías tm, languageR y ggplot2.

Lo siguiente es más normal a lo que se hace, en el ejemplo en Python ya se contaba con Corpus de ejemplo y se analizaban algunas cosas sobre las palabras, contexto, distribución, etc. En este ejemplo, se construye a partir de un texto un Corpus  y su matriz de términos.También se construye una tabla con información básica de las frecuencias.

No explico a detalle lo que hacen las funciones pero pueden leer un poco respecto a como las uso en las entradas ¿Cuánto se puede saber de los discursos? y Clasificación Binaria.

Las funciones que uso son las siguientes:

library(tm)
library(ggplot2)
library(languageR)

tdm2<-function(doc){
 docCor<-Corpus(VectorSource(doc))
 docs <- tm_map(docCor, stripWhitespace)
 docs <- tm_map(docs, removeWords, stopwords("english"))
 docs <- tm_map(docs, removePunctuation)
 docs <-tm_map(docs,removeNumbers)
 docs <- tm_map(docs,content_transformer(tolower))
 DocsTDM <- TermDocumentMatrix(docs) 
 return(DocsTDM)
}

TablaFreq<-function(TDM){
 docmatrix <- as.matrix(TDM)
 doc.counts <- rowSums(docmatrix)
 doc.df <- data.frame(cbind(names(doc.counts),as.numeric(doc.counts)),stringsAsFactors = FALSE)
 names(doc.df) <- c("Términos", "Frecuencia")
 doc.df$Frecuencia <- as.numeric(doc.df$Frecuencia)
 doc.occurrence <- sapply(1:nrow(docmatrix),
 function(i)
 {
 length(which(docmatrix[i, ] > 0)) / ncol(docmatrix)
 })
 doc.density <- doc.df$Frecuencia / sum(doc.df$Frecuencia)
 
 # Add the term density and occurrence rate
 doc.df <- transform(doc.df,density = doc.density,ocurrencia =doc.occurrence)
 S=head(doc.df[with(doc.df, order(-Frecuencia)),], n=50)
 return(S)
}
 
data(alice)
L=tmd2(alice)
L1=TablaFreq(L)

#Gráfica de frecuencia de palabras.

ggplot(L1,aes(L1$Frecuencia,factor(L1$Términos,levels=L1$Términos)))+geom_point(stat="identity", colour="red")+ggtitle("Tabla de Frecuencias")+xlab("Frecuencia de la palabra")+ylab("Las 50 alabra más frecuentes")

Lo que se hace es construir un Corpus del texto de Alicia en el país de las maravillas, luego se construye una tabla con las frecuencias de las palabras respecto a los que se llama Matriz de términos del texto o corpus, por último se grafica el comportamiento de las 50 palabras más frecuentes.

La gráfica que se obtiene es la siguiente:

Alice_plot

En esta pequeña entrada solo traté de mostrar que existen varias herramientas para el procesamiento del lenguaje natural y de text mining. Si bien no es una entrada muy vistosa, la intención es en las siguientes entradas explicar más detalles y técnicas. En las referencias se encuentra suficiente información para aprender respecto al tema.

Referencias:

1.-http://www.nltk.org/book_1ed/

2.-http://www.nltk.org/book/

3.-http://nlp.stanford.edu/courses/NAACL2013/

4.-http://cs224d.stanford.edu/syllabus.html

5.-http://www.gandhi.com.mx/inteligencia-artificial-un-enfoque-moderno

¿Cuánto se puede saber desde los discursos?

Esto es un juego.

Esta entrada la estuve pensando en la última semana de Septiembre 2015, en especial debido a varios acontecimientos y fechas que se celebran en México. Ejemplo, el 1° de Septiembre se hace entrega del informe anual del gobierno por parte del presidente, el día 15 se celebra el día de la independencia de México, el día 27 de Septiembre del 2014 sucedieron unos hechos lamentables en el estado de Guerrera dónde 43 estudiantes de una Normal Rural fueron “desaparecido”. Pero más aún, con motivo de este último suceso se han realizado manifestaciones y posiblemente se vuelven desde este año actos que se realizarán año con año.

Lo que observé en Facebook, en twitter  y en la prensa, suele ser variado dependiendo del perfil tanto de las personas como de los periódicos. Pero algo que observé fue que las críticas se centraban en los discursos del presidente, sobré algo que decía, sobre lo dicho en la ONU con sede en New York mientras se llevaban acabo manifestaciones en México. Sobre el tipo de palabras, sobre los comentarios, etc.

Algunas frases fueron tomadas como los objetivos centrales de las críticas de dichos discursos. Entiendo que existen un equipo que redacta los discursos del presidente, pero también entiendo que existe un personal que revisa y que verifica el tipo de términos y explicaciones que se dirán. Evito imaginar que se tenga una estrategia para hacer crítica o freno a las aspiraciones políticas de un posible candidato a la presidencia, cuando aún faltan 3 años para la contienda electoral, la entrada la hago sin tener una postura política sobre las conclusiones e interpretaciones que se dieron a los discursos.

Pensé que no sería mala idea jugar con una muestra pequeña de discursos (48) y analizar cosas básicas para tratar de ver cómo ha cambiado la “similaridad” entre los discursos, ver cuáles han sido los tópicos a los largo de los meses y tratar de ver qué se puede saber de manera estadística desde los discursos. Pienso que son otros los capacitados para hacer un análisis de tiempo y espacio donde se dice tal o cual discurso y la relevancia o repercusión de lo dicho.

Esto no desentona en tipo de entradas, ya que no es para nada una manifestación política o una entrada donde trato de mostrar algo negativo del gobierno o donde trato de concluir o deducir algo sobre lo que sucede en el país. Es simplemente jugar con un puñado de datos y ver que se descubre.

Con toda mi ignorancia sobre como interpretar un discurso, me atrevo a suponer de un modo simplista que cada discursos político tienen algo de “localidad” y de “temporalidad”. Con localidad, pienso que depende de donde es emitido, de cuan tan importante es el lugar donde se emite y lo temporal me refiero al momento que rodea a dicho discurso, los acontecimiento cercanos, los sucesos sociales y políticos que acontecen en las fechas en las que se dice dicho discurso. Por otro lado, imagino que otro factor que afecta la “relevancia” de un discurso es gracias a los medios de comunicación, ya que bien un discurso puede ser parte de un evento donde un mandatario comunica algo o puede ser usado como parte de la información que usa y discute un medio de comunicación, el cual termina perneando la opinión publica.

Esto de cierto modo me permite hacer una análisis de textos, clasificar los discursos, detectar tópicos en los discursos, comparar por medio de medidas de similaridad como han cambiado o en qué meses se muestra mayor similaridad, comparar la muestra con respecto al informe y la participación en la ONU, etc. Estos aspectos pueden ser trabajados con una combinación de herramientas y algoritmos, lo cual pensé que sería divertido ver hasta donde se puede saber algo desde los discursos.

Sobre la muestra.

Tomé 48 discursos, 4 por mes desde Septiembre 2014 hasta Septiembre 2015. Los elegí de manera aleatoria, todos fueron guardados en archivos txt. Los  discursos varían, tanto en cantidad de palabras como el tipo de evento en el cual se emitió.

La muestra de discursos fue tratada tanto para hacer un Corpus Global, como uno por cada Mes, y de igual forma se compararon todos los discursos de manera independiente para identificar similaridad.

Lo que decidí hacer.

Escribí unas funciones las cuales me permitían dos cosas, extraer las palabras que aparecen con mayor frecuencia y aplicando LDA tomé 20 palabras del primer tópico detectado. Definí un corpus por año,  por mes y tomé los discursos qué más escuche comentar, el discurso del Informe de Gobierno y el Discursos en la ONU como la pareja inicial a comparar.

Así que use la medida de similaridad de Jaccard, que es una medida muy sencilla para definir entre los discursos cuales eran más cercanos según las palabras más frecuentes y cuales por las 20 palabras detectadas en el primer tópico.

Primero muestro un ejemplo y al final muestro los resultados obtenidos con todos los discursos.

Informe de Gobierno e informe ONU.

El código lo comparto más adelante. Los resultados son los siguientes:

#Comparación entre los discursos de la UNO y el Informe de Gobierno

dir1="C:\\.....ruta.....\\150928_ONU.txt"

dir2="D:\\.....ruta......\\150902_Informe_EPN.txt"

#Extracción del mensaje
Doc1<-msg(dir1)
Doc2<-msg(dir2)

#Contrucción de la matriz de terminos
TMD1<-tdm2(Doc1)
TMD2<-tdm2(Doc2)

#Se contruye su matrix de frecuencias
L1=TablaFreq(TMD1)
L2=TablaFreq(TMD2)

head(L1,5)
#    Términos  Frecuencia density ocurrencia
#356 naciones   15       0.01764706 1
#351 mundo      10       0.01176471 1
#330 méxico      9       0.01058824 1
#391 paz         9       0.01058824 1
#551 unidas      9       0.01058824 1
 
head(L2,5)
#     Términos Frecuencia density ocurrencia
#1597  méxico    92    0.014212884 1
#1603  mil       69    0.010659663 1
#1661  nacional  51    0.007878882 1
#1767  país      51    0.007878882 1
#1596  mexicanos 48    0.007415418 1

Jaccard(head(L1,20),head(L2,20))
#0.1428
#Gráfica
graphFreq(L1)
graphFreq(L2)

#Contruyo el DTM de cada texto
DTM_1=DTM(Doc1)
DTM_2=DTM(Doc2)

#Extraego las priemras 20 palabras asociadas con el primer tópico
top1<-TopicLDA(DTM_1)
top2<-TopicLDA(DTM_2)

top1
[1] "naciones" "derechos" "onu" "organización" "respeto" "agenda" 
[7] "frente" "humanidad" "humanos" "armas" "colectiva" "con" 
[13] "general" "seguridad" "señores" "acción" "clara" "consejo" 
[19] "favor" "futuro" 

top2
[1] "méxico" "con" "mil" "país" "reforma" 
[6] "gobierno" "mayor" "año" "administración" "familias" 
[11] "república" "años" "por" "justicia" "programa" 
[16] "educación" "crecimiento" "condiciones" "partir" "pobreza

Jaccard(top1,top2)
#0.0256

Se observa que hay una similaridad mayor entre los discursos cuando se consideran la palabras con mayor frecuencia o apariciones. La palabra que generó mayor crítica en los medios fue “populismo” y “populistas”. Estas dos palabras hicieron que se criticaran mucho los 2 discursos, lo que muestran los datos es que esas palabras aparecen muy poco, comparado con el efecto que generaron en los medios. De cierto modo son como “palabras singulares” que ejercieron mucho impacto en la apreciación del discurso.

La gráficas de las 50 palabras más citadas son las siguientes:

EPN_ONU

Discurso ONU

EPN_IG

Discurso Informe de Gobierno

Se observa que entre los dos discursos existe una diferencia considerable entre el tipo de palabras que se emplean y la frecuencia, también de la gráfica se puede apreciar que hay cierta “forma” diferente entre las palabras más citadas. Lo cual es notorio que la palabra “méxico” domina el discurso de Informe de Gobierno, por otro lado las palabras “naciones” y “mundo” el discurso de la ONU.

Lo que no muestra gran cambio es el desvanecimiento del color, que representa los cambios de la “densidad” de la frecuencia de las palabras. Concluir algo de estos datos es sutil y posiblemente confuso y atrevido, así que limitándome a lo estadístico se puede decir que hay en estructura diferencias pero muy sutiles, casi no se puede decir nada de esta comparación.

Dándole una interpretación a los tópicos y las mediciones de similaridad, los discursos en cuanto a “estructura”; las palabras más frecuentes, muestran un 14% de similaridad. Pero cuando uno analiza las palabras asociadas al primer tópico detectado, se observa que realmente solo son similares en escaso 2%. Los tópicos me parecen más relevantes, y es notoria la diferencia detectada. Las 20 palabras del primer tópico muestran al discurso de la ONU como algo “global” o “mundial”, y al discurso del informe de gobierno lo muestran como algo “nacional” y de problemas “socio económicos”.

En conclusión; como es de esperar,  se puede decir que los discursos no son tan similares (bajo esta medida de similaridad).

Lo global

Haciendo un corpus con los 48 discursos analizar, puedo comparar con respecto a los otros dos y analizar el comportamiento de la similaridad.

#Procesamiento de los discursos
#Librería para la nube de palabras
library(wordcloud)

dir="D:.....ruta....."
setwd(dir)
#Lista de directorios
filesall<-dir()
#Documentos y corpus
corpusalldoc<-sapply(filesall,function(p)msg(p))
corpusall<-tdm2(corpusalldoc)

#Tabla de frecuencias
Lall=TablaFreq(corpusall)

#Gráfica de frecuencias
graphFreq(Lall)

#Tópicos
DTMall<-DTM(corpusalldoc)
Topic_all<-TopicLDA(DTMall)

#Selección de conjunto de palabras para la nube de palabras
Grupo1<-rowSums(as.matrix(corpusall))
Grupo1<-subset(Grupo1,Grupo1>=30)
m<-as.matrix(Grupo1)
dim(m)
word_freqs = sort(rowSums(m), decreasing=TRUE)
dm = data.frame(word=names(word_freqs), freq=word_freqs)
wordcloud(dm$word, dm$freq, random.order=FALSE, random.color=FALSE,colors=brewer.pal(10,"Dark2"))

La gráfica y la nube de palabras que se obtiene es la siguiente:

Palabras_48 disc

Nube_48disc

Comparando con la métrica de similaridad se tienen lo siguiente:

#Medida de Similaridad
#Palabras más frecuentes
#UNO vs los 48 Discursos
Jaccard(Lall[,1],L1[,1])
#0.123
#Informe de Gobierno vs 48 Discursos
Jaccard(Lall[,1],L2[,1])
#0.282

#Tópicos
#ONU vs los 48 Discursos
Jaccard(Topic_all,top1)
#0.0526
#Informe de Gobiernos vs los 48 Discursos
Jaccard(Topic_all,top2)
#0.1428

Se aprecia que la medida de similaridad es mayor entre el discurso de informe que el de la ONU con respecto al corpus de los discursos. Era de esperar que fuera así, por la naturaleza del discurso y la fecha a la cual corresponde.

Es claro que los dos discursos tienen mayor similaridad con la muestra de discursos en el año, que entre ellos. Las dos preguntas que me hago al observar esto es, ¿cómo se comporta esta medida de similaridad por mes? y ¿cuál discurso muestra mayor similaridad con el de gobierno y el de la ONU?

Por Mes y por Discurso.

Haciendo la comparación por meses, se tienen que a comparar los dos discursos, el de la ONU y el del Informe, se tienen que por corpus construido por mes, se tiene gráficas como las siguientes:

Frec_Meses

El comportamiento por tópicos muestra otro comportamiento, el cual genera la siguiente gráfica:

Topic_Meses

Las gráficas de la métrica por mes muestra lo que uno puede esperar, el discurso de la ONU llega a no tener nada de similaridad en los meses de Marzo y Abril con la frecuencia de palabras, pero peor aún muestra poca similaridad con el primer grupo de tópicos en los meses de Octubre, Diciembre, Enero, Abril, Mayo y Septiembre. Es decir, el discurso dado en la ONU considerando que su primer grupo de tópicos se refiere aspecto mundiales o globales, ese no fue tema en esos meses con respecto a la muestra.

Por otro lado, el discurso del informe uno espera que sea similar al dado cada año o que las palabras que se usan en el mes de Septiembre suelen ser usuales. Eso muestra la primera gráfica, pero además vuelve a ser similar al inicio del año. Por otro lado al considerar los tópicos no muestra el mismo comportamiento, resulta que el mes de Junio es por alguna razón el mes con el cual muestra mayor similaridad. Eso me resulta raro, pero así resultó la medida de similaridad.

Ahora considerando cada uno de los discursos elegidos para analizar, el comportamiento que muestran con la frecuencia de palabras es el siguiente:

Freq_por_discurso

Esta muestra algo un poco más interesante, primero el mes de Septiembre muestra mayor similaridad y me resulta extraño que solo con el mes de Septiembre en el 2014, pero también uno puede observar que el mes de Enero con respecto al informe muestra un comportamiento de alta similaridad. Uno puede pensar que con motivo de inicio del año los discursos suelen ser “alentadores” , “nacionalistas”, “de mejoras” , etc. Esto pienso que puede ser interesante revisar una muestra de varios años y comparar como se comporta conforme pasan los años y quizás muestra estacionalidad la medida de similaridad.

Respecto al informe de la ONU muestra que no es usual que en los discursos se haga uso del mismo tipo de palabras, lo cual uno puede esperarlo ya que no suele decirse mucho del contexto “global”, como en el discursos de la ONU.

La gráfica de los tópicos, muestra el siguiente comportamiento:

 Topic_por_discursoLos tópicos muestran una cosa curiosa, el discurso de la ONU muestra entre los meses de Noviembre-Diciembre una alza, ¿efecto de la navidad para hablar del mundo?..no lo se, esto igual permite que si se hace una muestra mayor analizar si hay algún efecto en el primer grupo de tópicos detectados con la técnicas LDA.

Por otro lado el comportamiento del discurso del Informe muestra una alta similaridad en meses como Enero, Mayo y Junio. De nuevo que el mes de Enero aparezca con valores considerablemente mayores me hace suponer que al inicio del año y a medio año suelen tener este comportamiento de realzar o motivar ciertas cosas “nacionalistas” o “de esperanza” de ser un mejor país. No lo se, solo me hace pensar que teniendo una muestra mayor uno puede empezar hacer cosas más interesantes y jugar a poner algunas hipótesis para experimentar.

 ¿Qué cosas hacer para mejorar esto?

Haciéndome auto-críticas, pienso que hacer una muestra mayor y con discursos de varios años puede resultar más interesante. Por otro lado hacer uso de mejores técnicas o de otras técnicas de medidas de similaridad para explorar como se comportan los discursos con varias medidas. Por último no estaría mal hacer una muestra de otros mandatarios para revisar como evolucionan los tópicos y ver como se comportan ante camios o hechos históricos, cosas de ese estilo.

 Código

Comparto las funciones principales, el resto son muchas líneas de código de loops o de procesar un poco los datos para hacer las gráficas. Por lo cual comparto solo lo más importante del código.

library(tm)
library(NLP)
library(ggplot2)

#######################################
#Mensaje
msg<-function(path){
 con<-file(path,open='rt')
 text<-readLines(con, warn = FALSE, n=-1, encoding = "UCS-2LE")
 close(con)
 return(paste(text, collapse = "\n"))
}

#####################################
#TDM

tdm<-function(doc){
 control<-list(removeWords(stopwords("spanish")),
 removePunctuation,
 removeNumbers,
 content_transformer(tolower),
 minDocFreq=2)
 doc.corpus<-Corpus(VectorSource(doc))
 doc.tdm<-TermDocumentMatrix(doc.corpus,control)
 return(doc.tdm)
}

#######################################
#TDM versión 2
tdm2<-function(doc){
 docCor<-Corpus(VectorSource(doc))
 docs <- tm_map(docCor, stripWhitespace)
 docs <- tm_map(docs, removeWords, stopwords("spanish"))
 docs <- tm_map(docs, removePunctuation)
 docs <-tm_map(docs,removeNumbers)
 docs <- tm_map(docs,content_transformer(tolower))
 DocsTDM <- TermDocumentMatrix(docs) 
 return(DocsTDM)
 }

############################################
#Tabla de frecuencias
TablaFreq<-function(TDM){
 docmatrix <- as.matrix(TDM)
 doc.counts <- rowSums(docmatrix)
 doc.df <- data.frame(cbind(names(doc.counts),as.numeric(doc.counts)),stringsAsFactors = FALSE)
 names(doc.df) <- c("Términos", "Frecuencia")
 doc.df$Frecuencia <- as.numeric(doc.df$Frecuencia)
 doc.occurrence <- sapply(1:nrow(docmatrix),
 function(i)
 {
 length(which(docmatrix[i, ] > 0)) / ncol(docmatrix)
 })
 doc.density <- doc.df$Frecuencia / sum(doc.df$Frecuencia)
 doc.df <- transform(doc.df,density = doc.density,ocurrencia =doc.occurrence)
 S=head(doc.df[with(doc.df, order(-Frecuencia)),], n=50)
 return(S)
 }

##############################################
#Gráfica de frecuencias
graphFreq<-function(L){
 library(ggplot2)
 #Se debe de introducir la matriz con frecuencias
 #Gráfica de palabras y frencia
 p<-ggplot(L,aes(x=factor(Términos, levels=unique(as.character(Términos)) ), y=Frecuencia))+geom_bar(stat = "identity",aes(fill=density))+
 coord_flip()+xlab('Apariciones en el texto')+ylab('Las 50 palabras más frecuentes') 
 return(p)
 }

##########################################
#Función para extraer el Document term Matrix
DTM<-function(Texto){
 docCor<-Corpus(VectorSource(Texto))
 docs <- tm_map(docCor, stripWhitespace)
 docs <- tm_map(docs, removeWords, stopwords("spanish"))
 docs <- tm_map(docs, removePunctuation)
 docs<-tm_map(docs,removeNumbers)
 docs <- tm_map(docs,content_transformer(tolower))
 DocsTDM <- DocumentTermMatrix(docs) 
 return(DocsTDM)
}

################################################
#Topic
TopicLDA<-function(DTMdoc){
 #Regresa las 20 palabras relevantes del primer tópico detectado
 library(topicmodels)
 r.lda=LDA(DTMdoc,method="Gibbs",2)
 L=terms(r.lda,20)
 return(L[,1])
 }

##############################################
#Similaridad de Jaccard
Jaccard<-function(A,B){
 a=length(intersect(A,B))
 b=length(union(A,B))
 a/b
}

Clasificación binaria….Naive Bayes

Un poco sobre análisis de textos

Lo principal de esta entrada es dar un ejemplo de como clasificar correos, esto lo hago siguiendo el ejemplo de libro Machine Learning for Hackers [1]. La técnica es conocida como Naive Bayes, pero creo conveniente comentar un poco de la librería de R para text mining.

La librería tm   permite realizar text mining en R project. El text mining se aboca sobre la conversión textos a datos que puedan ser analizados, ya sea por herramientas estadísticas, o por técnicas de procesamiento natural del lenguaje (NLP, en ingles).

La librería es bastante conocida en la red y existe mucha información, en varios blog se muestran ejemplos interesantes. En particular se mostraban análisis de textos de Twitter, un buen ejemplo es el de Yanchang Zhaog, este se puede encontrar en sus notas data mining con R. Lo desafortunado es que la API de twitter fue actualizada y ya no se puede replicar el ejemplo con el código de Yanchang.

Para ejemplificar el funcionamiento de tm tomo los títulos de las primeras planas de un periódico desde el día 1 al 23 de Marzo.

Los pasos son los siguientes:

  1. Extraemos los HTML de cada portada desde la página del periódico.
  2. Tomo el texto de la página web o HTML.
  3. Le quito a los textos la mayoría de palabras que forman parte de los títulos y secciones del periódico.
  4. Proceso con tm los archivos.
  5. Construyo una nubes de palabras.

Podríamos hacer otro tipo de análisis  después del punto 4, pero implica hablar de otros conceptos, como cluster, PCA  o redes. Observación: No pongo todo el código, pero prácticamente solo falta el proceso del paso 2.

#Paso 0
#Cargamos las librerías 
library(tm)
library(wordcloud)
library(stringi)

#Paso 1
#Extraemos los datos
direc<-""http://www.eluniversal.com.mx/hemeroteca/edicion_impresa_201503"
comp_dir<-".html"
dias<-c("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23")
#Paso 2
#Extraemos los datos
edi_impresa<-character(length(dias))

for (i in 1:length(dias)){
     edi_impresa[i]=htmlToText(stri_paste(direc,dias[i],comp_dir))
    }
#Paso 3 y 4
#Construimos el Corpus
Doc<-Corpus(VectorSource(edi_impresa))

#Visualizamos Doc
Doc[[1]]

#Procesamos Doc con la librería tm

Doc1<-tm_map(Doc, function(x) stri_replace_all_regex(as.character(x), "<.+?>", " "))
Doc2 <- tm_map(Doc1, function(x) stri_replace_all_fixed(x, "\t", " "))
Doc3 <- tm_map(Doc2, PlainTextDocument)
Doc4 <- tm_map(Doc3, stripWhitespace)
Doc5 <- tm_map(Doc4, removeWords, stopwords("spanish"))
Doc6 <- tm_map(Doc5, removePunctuation)
Doc7<-tm_map(Doc6,removeNumbers)

#Hacemos una lista con todas las palabras de títulos o secciones del periódico
L<-#LO OMITO
Doc8 <- tm_map(Doc7, removeWords, L)
Doc9 <- tm_map(Doc8,content_transformer(tolower))

#Contruimos nuestra matriz de TDM
DocsTDM <- TermDocumentMatrix(Docs14)

Lo que hice hasta esta parte del código es la construcción de la matriz de términos de los documentos. Con ello puedo revisar las frecuencias de las palabras. Como observación, debido a que no hice bien la lista de palabras a remover aparecerán algunas que se refieren a títulos y secciones del periódico.

#Revisamos las frecuencias de las palabras
Grupo1<-rowSums(as.matrix(DocsTDM))
Grupo1<-subset(Grupo1,Grupo1>=25)

#Graficamos nuestras palabras
library(ggplot2)
qplot(names(Grupo1), Grupo1, geom="bar", xlab="Terms") + coord_flip()
barplot(Grupo1, las=2)

 Palabras_más_frecuentes

Por último hago la nube de palabras.

#Nube de palabras
Grupo1<-subset(Grupo1,Grupo1>=10)
m<-as.matrix(Grupo1)
word_freqs = sort(rowSums(m), decreasing=TRUE)
dm = data.frame(word=names(word_freqs), freq=word_freqs)
#Nube de palabras
wordcloud(dm$word, dm$freq, random.order=FALSE, colors=brewer.pal(8, "Dark2"))

Imagen_datos_nube

Bueno, la idea sería procesar correctamente los datos y presentar la nube de palabras con mayor frecuencia de apariciones en el texto o con algún criterio que de deseé. Otro ejemplo sería revisar cuales palabras persistieron por el paso de los días en el mes de Marzo. También se puede hacer otro tipo de análisis, por ejemplo buscar cluster  o tópicos en los datos, lo cual resulta más interesante.

Cómo observación, la librería tm cuenta con aproximadamente 54 funciones y cabe mencionar que el objeto principal es la construcción y procesamiento de Corpus, que es como un concentrado de todos los textos  que se analizan. Para procesar el contenido del Corpus se usa la función tm_map, la cual funciona análogamente a las funciones apply, lapply, sapplycon la diferencia de que se aplica solo a los Corpus. Para mayor detalle se puede revisar el manual de la librería [].

Si se tiene interés en aprender sobre procesamiento de textos siguiendo las metodologías de NLP, se tienen muy buenos cursos de la plataforma Coursera. Recomiendo el curso impartido por la Universidad de Stanford.

¿Web Scraping con R?

El procesar textos ha sido una área de investigación desde hace tiempo, ahora se tienen librerías y metodologías estándar, pero donde abunda mucho información para analizar y era complicado extraerla es en la web. Ha este tipo de programas se les llama web scraping.

Mario Annau liberó una librería en R llamada tm.plugin.webminig y por otro lado Hadley Wickham liberó la librería rvest. Las dos librerías se toman como modelo librerías de Python, las cuales son más robustas y se han mejorado con el tiempo.

No hago algún ejemplo, pero se puede revisar la documentación y replicar el ejemplo que comparten cada uno sobre el uso de su librería. Lo interesante es que las dos tratan de simplificar pasos que tm no hace por si sola, principalmente por la interacción con textos HTML.

Como recomendación ( y eso en cada entrada de esta categoría) creo importante revisar el repositorio de Hahley Wickham, el cual a contribuido a la comunidad de R project con librerías muy populares y de mucho utilidad, basta mencionar ggplot2 y reshape. Se ha convertido en el RockStar de R para muchos.

Clasificación de mensajes-detectando Spam

 

En la entrad sobre Máquina de Soporte Vectorial (SVM) hice un ejemplo comparativo para clasificar correos. Replico el código en esta entrada solo enfocándome en Naive Bayes.

#Se instala la librería kernlab para usar sus datos 
library(kernlab)
#Se extraen los datos
data(spam)
#Se genera una muestra de 100 índices
index=sample(1:nrow(spam),100)
#Se carga la librería e1071 para usar la función naiveBayes
library(e1071)
model=naiveBayes(type~.,data=spam[-index,])
model=naiveBayes(type~.,data=spam[-index,])
pred=predict(mol,spam[index,])
table(pred,spam[index,58])
 
pred   nonspam   spam
nonspam  38      0
 spam     29     33

Los datos que se usan en el código anterior son datos ya procesados, los cuales ya cuentan con la etiqueta o variable categórica de ser Spam o no. Lo recomendable es  cargar los datos y explorar la información, en este caso solo hago uso de los algoritmos Naive Bayes que se encuentra en la librería e1071.

Hago otro ejemplo con los datos de la librería kernlab, estos corresponden a 8993 personas de las cuales se toman 14 datos y uso el algoritmo de Naive Bayes para evaluar la clasificación de 1000 de estas personas por sexo.

#Se carga las librerías
library(kernlab)
library(e1071)
#Se extraen los datos
data("income")
#Se toman 1000 personas de los datos
index=sample(1:nrow(spam),1000)
#Se genera el modelo Naive Bayes de la librería e1071
modelo=naiveBayes(SEX~.,data=income[-index,])
#Se estima la predicción
pred=predict(modelo,income[index,])
#Se presentan los datos en modo de una tabla
table(pred,income[index,2])
 
pred  M   F
 M   234 162
 F   220 384

En este último ejemplo, al igual que el primero se hace uso de datos previamente procesados. Lo cual facilita la aplicación del algoritmo.

Las tablas de los dos ejemplos muestra dos categoría, los errores del algoritmo se pueden considerar como la cantidad de Hombres de la fila M de Male que son considerados como mujeres en la columna F de Famale, es decir; 384. Equivalente las 220 mujeres que son clasificadas como hombres en la segunda fila.

Estos errores son conocidos como falsos negativos y falsos positivos, el objetivo de las técnicas de clasificación es tener el mínimo de este tipo de errores.

El siguiente ejemplo consiste  también en clasificar correos, donde un conjunto son identificados como Spam y otro conjunto se sabe que no es spam. Estos son extraídos desde SpamAssasin, la dirección es http://spamassassin.apache.org/publiccorpus/ . Pero recomiendo bajar todos los datos y código libro Machine Learning for Hackers  dese el repositorio de Jhon M. White en Github, para replicar de manera completa el ejemplo.

A diferencia de los primero dos ejemplos de clasificación, en este caso los datos o textos se van a procesar para identificar las palabras que aparecen con mayor frecuencia en los Spam y las palabras de los que no son Spam.

Se hará uso de 3 funciones, la primera para extraer el mensaje del correo, la segunda para convertir el mensaje en una matriz de términos del documento, con la cual se revisa la frecuencias de las palabras y se aplica una tercera función para clasificar cualquier correo con respecto a las palabras que se encuentran con mayor frecuencia en los correos identificados como Spam.

Naive Bayes

Antes doy una breve explicación  de la idea a tras de la clasificación. Suponiendo que se tienen dos clase, hombres y mujeres; y una lista de personas, por ejemplo: Guadalupe, Ernesto, Sofía, Andrea, Guadalupe, Sofía, Luis, Miguel, Guadalupe y Jaime

Nombre     Sexo
Guadalupe- Mujer
Ernesto  - Hombre
Sofía    - Mujer
Andrea   - Mujer
Guadalupe- Hombre
Sofía    - Mujer
Luis     - Hombre
Miguel   - Hombre
Guadalupe- Mujer
Jaime    - Hombre

De la lista anterior al considerar dos categorías, hombres y mujeres, se puede tomar una persona, ejemplo Guadalupe,  pero uno puede preguntarse ¿cuál es la probabilidad de que sea mujer dado que la persona se llama Guadalupe?

P(Mujer|Guadalupe)=[P(Guadalupe|Mujer)P(Mujer)]/P(Guadalupe)

La ecuación anterior expresa P( | ) como la probabilidad condicional y la regla de Bayes. Con los datos del ejemplo anterior  se puede calcular el valor de P(Mujer|Guadalupe)=2/3.

Análogamente se puede calcular P(Hombre|Guadalupe)=1/3

Entonces  lo que se tiene es que tomando a una persona que se llama Guadalupe es más probable que sea mujer. Pero se pueden considerar más atributos, como edad, peso, color de ojos, etc. Así  se puede uno volver a preguntar, ¿cuál es la probabilidad de que sea Mujer dado que se llama la persona Guadalupe?, pero claro ahora se pueden considerar los nuevos atributos en el calculo de la probabilidad condicional.

Lo que se modificará en el calculo es la cantidad de factores, ejemplo:

P(Guadalupe|Mujer)=P(Peso|Mujer)*P(Color de ojos|Mujer)*P(Edad|Mujer)

En el ejemplo de los correos se tiene una lista de palabras que se encuentran en aquellos que son Spam, entonces el total de atributos será dado por el total de palabras. La pregunta que se hace es: ¿el correo es Spam dado que tienen estas palabras?

Lo que  no menciono en el calculo anterior es que los atributos  se consideran independientes condicionalmente. Los aspectos teóricos de manera breve se pueden consultar en la parte 4 de las notas de Andrew Ng o en las notas de Andrew Moore, con más detalles se pueden encontrar en la referencias [2,3,4].

Espero que la explicación pese a ser corta ayude a entender lo que se va hacer con los correos que se procesan.

Correos..¿spam o no?

Como antes mencioné, tomo un conjunto de correos identificados como Spam y otro que se tienen identificado que no lo es. Se procesan para formar una matriz de términos para cada conjunto de correos. Con la matriz se asignarán densidades a las palabras  y por último se construye una función para clasificar.

Los primeros 500 correos se consideran como el conjunto de entrenamiento para el proceso de clasificación.

#Cargamos los datos
library(tm)
library(ggplo2)
spam.path <- file.path("data", "spam")
easyham.path <- file.path("data", "easy_ham")

#Funciones que usaremos
######Primera función

get.msg <- function(path)
{
 con <- file(path, open = "rt")
 text <- readLines(con)
 msg <- text[seq(which(text =="")[1]+1, length(text), 1)]
 close(con)
 return(paste(msg, collapse = "\n"))
}

#####Segunda función

get.tdm2 <- function(doc.vec)
{
 library(stringi)
 doc.corpus<-Corpus(VectorSource(doc.vec))
 cor.doc<-tm_map(doc.corpus, function(x) stri_replace_all_regex(as.character(x), "<.+?>", " "))
 cor.doc <- tm_map(cor.doc, PlainTextDocument)
 cor.doc <- tm_map(cor.doc,content_transformer(tolower))
 cor.doc <- tm_map(cor.doc,removeWords,stopwords("english"))
 cor.doc<- tm_map(cor.doc, removePunctuation)
 cor.doc<- tm_map(cor.doc, removeNumbers)
 cor.doc<-tm_map(cor.doc, stripWhitespace)
 doc.dtm<- TermDocumentMatrix(cor.doc)
 return(doc.dtm)
}

######Tercer función
classify.email <- function(path, training.df, prior = 0.5, c = 1e-6)
{
 #Extrae el mensaje
 #Convierte los datos en una matrz de terminos del documento

 msg <- get.msg(path)
 msg.tdm <- get.tdm(msg)
 msg.freq <- rowSums(as.matrix(msg.tdm))
 msg.match <- intersect(names(msg.freq), training.df$términos)
 if(length(msg.match) < 1)
 {
 return(prior * c ^ (length(msg.freq)))
 }
 else
 {
 match.probs <- training.df$ocurrencia[match(msg.match, training.df$términos)]
 return(prior * prod(match.probs) * c ^ (length(msg.freq) - length(msg.match)))
 }
}

######Procesar los los Spam

spam.docs <- dir(spam.path)
spam.docs <- spam.docs[which(spam.docs != "cmds")]
all.spam <- sapply(spam.docs,function(p) get.msg(file.path(spam.path, p)))
spam.tdm <- get.tdm2(all.spam)

######Procesamos 500 correos que no son spam

easyham.docs<-dir(easyham.path)
easyhamo.docs500=easyham.docs[1:500]
easyham500<-sapply(easyham.docs500,function(p)get.msg(file.path(easyham.path,p))
easyham500.tdm<-get.tdm2(easyham500)

En el código anterior se obtiene las matrices de términos y se puede ver los valores de esta en R, solo colocando el nombre:

#Revisamos los valores de los corpus

spam.tdm

<<TermDocumentMatrix (terms: 23109, documents: 500)>>
Non-/sparse entries: 70715/11483785
Sparsity : 99%
Maximal term length: 298
Weighting : term frequency (tf)

#Corpus para correos que no son Spam
easyham500.tdm

<<TermDocumentMatrix (terms: 13118, documents: 500)>>
Non-/sparse entries: 50247/6508753
Sparsity : 99%
Maximal term length: 245
Weighting : term frequency (tf)

Con las matrices de términos se construye una matriz con las densidades de las palabras.

#Matriz de densidad

spam.matrix <- as.matrix(spam.tdm)
spam.counts <- rowSums(spam.matrix)
spam.df <- data.frame(cbind(names(spam.counts),as.numeric(spam.counts)),stringsAsFactors = FALSE)
names(spam.df) <- c("términos", "frecuencia")
spam.df$frecuencia <- as.numeric(spam.df$frecuencia)
spam.occurrence <- sapply(1:nrow(spam.matrix),
 function(i)
 {
 length(which(spam.matrix[i, ] > 0)) / ncol(spam.matrix)
 })
spam.density <- spam.df$frecuencia / sum(spam.df$frecuencia)

#Aplicamos el mismo proceso para easyham500.tdm

#Términos frecuentes en spam.df

head(spam.df[with(spam.df, order(-ocurrencia)),],n=15)
      términos frecuencia density ocurrencia
6019    email    837    0.007295962 0.574
15199   please   459    0.004001011 0.520
3539    click    350    0.003050880 0.454
11722   list     419    0.003652339 0.442
21468   will     843    0.007348262 0.442
7317    free     651    0.005674637 0.420
9990  information 364   0.003172915 0.374
13837   now      329    0.002867827 0.352
2899    can      518    0.004515302 0.344
7687    get      425    0.003704640 0.334
14323   one      376    0.003277517 0.322
13511   new      336    0.002928845 0.300
16452   receive  327    0.002850394 0.298
19437   time     316    0.002754509 0.296
12527   message  245    0.002135616 0.286

#Términos frecuentes en easy.df

 head(easy.df[with(easy.df, order(-ocurrencia)),],n=15)
       términos frecuencia density ocurrencia
5052    group   232      0.003446125 0.388
12391   use     272      0.004040284 0.380
12979   wrote   238      0.003535249 0.380
1577    can     348      0.005169187 0.368
6982    list    249      0.003698642 0.368
8258    one     358      0.005317727 0.338
6527    just    273      0.004055138 0.326
8120    now     231      0.003431271 0.324
4812    get     230      0.003416417 0.282
6924    like    232      0.003446125 0.282
3653    email   188      0.002792549 0.276
11337   subject 162      0.002406346 0.270
11854   time    188      0.002792549 0.258
12834   will    318      0.004723567 0.254
6112   information 162   0.002406346 0.232

 De los datos anteriores se observa que comparten los dos tipos de correos algunas palabras, pero se aprecia que las ocurrencias en los Spam tienen valores más altos.

Lo que sigue es comparar las palabras contra una muestra de correos que no son Spam, pero por el texto que contiene son más complicados para detectarse como no-Spam.

Usamos la función classify.email se prueba contra el conjunto de entrenamiento que son las matrices spam.df y easy.df

#Clasificación
hardham.path <- file.path("data", "hard_ham")
hardham.docs <- dir(hardham.path)
hardham.docs <- hardham.docs[which(hardham.docs != "cmds")]

hardham.spamtest <- sapply(hardham.docs, function(p) classify.email(file.path(hardham.path, p), training.df = spam.df))
 
hardham.hamtest <- sapply(hardham.docs,function(p) classify.email(file.path(hardham.path, p), training.df = easyham.df))

hardham.res <- ifelse(hardham.spamtest > hardham.hamtest,TRUE,FALSE)

summary(hardham.res)
   Mode FALSE TRUE NA's 
logical  242   7   0

Lo que se observa es que se clasifican 7 email como Spam, cuando no lo son. Es posible que se deba algún error en el procesamiento del TDM de los Spam, ya que el resultado obtenido es demasiado bueno.

Para cerrar esta entrada, hago lo que sería un paso de prueba del clasificador. Se inicia con un conjunto de entrenamiento y se prueba como responde para clasificar  2500 correos que no son Spam y que son fáciles de identificar (easyham).

#Función clasificador de Spam

spam.classifier <- function(path)
{
 pr.spam <- classify.email(path, spam.df)
 pr.ham <- classify.email(path, easy.df)
 return(c(pr.spam, pr.ham, ifelse(pr.spam > pr.ham, 1, 0)))
}
#Cargamos los 2500 correos

easyham.docs <- dir(easyham.path)
easyham.docs <- easyham.docs[which(easyham.docs != "cmds")]

#Los clasificamos

easyham.class <- suppressWarnings(lapply(easyham.docs,function(p){spam.classifier(file.path(easyham.path, p))}))

#Contruimos una matriz con los datos
easyham.matrix <- do.call(rbind, easyham.class)
easyham.final <- cbind(easyham.matrix, "EASYHAM")

class.matrix <- rbind(easyham.final)
class.df <- data.frame(class.matrix, stringsAsFactors = FALSE)

names(class.df) <- c("Pr.SPAM" ,"Pr.HAM", "Class", "Type")
class.df$Pr.SPAM <- as.numeric(class.df$Pr.SPAM)
class.df$Pr.HAM <- as.numeric(class.df$Pr.HAM)
class.df$Class <- as.logical(as.numeric(class.df$Class))
class.df$Type <- as.factor(class.df$Type)

#Hacemos la gráfica con la información de los correos clasificados

ggplot(class.df, aes(x = log(Pr.HAM), log(Pr.SPAM))) +
 geom_point(aes(shape = Type, alpha = 0.5)) +
 stat_abline(yintercept = 0, slope = 1) +
 scale_shape_manual(values = c("EASYHAM" =1),name = "Email Type") +
 scale_alpha(guide = "none") +
 xlab("log[Pr(HAM)]") +
 ylab("log[Pr(SPAM)]") +
 theme_bw() +
 theme(axis.text.x = element_blank(), axis.text.y = element_blank())

#Hacemos una tabla con las densidades 

easyham.col <- get.results(subset(class.df, Type == "EASYHAM")$Class)
class.res <- rbind(easyham.col)
colnames(class.res) <- c("NOT SPAM", "SPAM")
print(class.res)

          NOT SPAM SPAM
easyham.col 0.974 0.026

Se observamos que el clasificador identifica aproximadamente el 97.5% de los correos que no son Spam, lo cual es correcto y solo tienen un error (falsos positivos) de 2.5%. Este ejercicio se puede repetir con el resto de conjuntos de correos que se encuentran en los datos del texto Machine Learning for Hackers.

Grafica_Ham_vs_Spam

Espero que los ejemplos den una idea aproximada de lo que se hace con este algoritmo y como se usa para construir un clasificador, en el texto se tienen más detalles, pero en general traté de poner en esta entrada las ideas básicas.

También se debe de observar que la clasificación no se reduce solo a tener dos clases (Spam y no Spam), como mencioné en otras entradas existen varias técnicas de clasificación multi-clase y en muchas situaciones es probable que se tengan más de dos clases para analizar.

Referencias:

1.-http://shop.oreilly.com/product/0636920018483.do

2.-http://shop.oreilly.com/product/0636920018483.do

3.-http://www.amazon.com/Artificial-Intelligence-Modern-Approach-Edition/dp/0136042597

4.-http://statweb.stanford.edu/~tibs/ElemStatLearn/

5.-http://www.autonlab.org/tutorials/naive02.pdf

6.-http://cs229.stanford.edu/

Análisis Confirmatorio vs Exploratorio

Análisis Exploración….¿qué es eso? 

La idea de la exploración de datos es tal cual lo que su nombre indica, es buscar cosas entre los datos.

Inicialmente uno entra a esa selva de datos y paso a paso va siguiendo algún camino o haciendo uno nuevo, cada pequeño avance nos va dando pistas del lugar en el que estamos, y poco a poco nosotros como exploradores vamos entendiendo como es esa selva. La idea creo que es parecida a jugar a descubrir cosas desde los datos. Lo que se intenta es tomar la información y tratar de encontrar algún indicio de algo que puede “servirnos”. Pero, ¿qué es “servirnos”?,¿qué buscamos?,¿para qué queremos entender esa selva?

Puede parecer absurdo, pero un paso clave antes de explorar la información es conocer un poco sobre su origen, el tipo de datos, la cantidad de ellos y cómo se generan. Hacer este tipo de indagación antes de procesarla y explorarla nos puede ayudar a no caer en falsos “descubrimientos”.

Ejemplo; si uno tiene información de las votaciones por casillas y una casilla muestra un comportamiento “anómalo” con respecto al resto, uno puede tratar de explorar la información en busca de una “explicación” solo desde los datos. En ese caso si uno “contextualiza” o revisar el lugar dónde se encontraba la casilla cuando se realizaba la votación, quizás esto puede explicar su anomalía. Ejemplo, si estaba localizada en la sede de uno de los candidatos es probable que explique porque contenía solo votos de él o si se sabe que la localidad es corrupta uno puedo sospechar que todos estaban de acuerdo en votar por un candidato.

Puede parecer que la exploración de datos es algo poco sofisticado, hacer una gráfica no parece ser lo más difícil cuando uno tiene datos de asistencia de los alumnos de una clase de matemáticas en una preparatoria , o el valor de una moneda contra el dolar durante 100 días, o el registro de lo que compran en el supermercado 50 personas el mismo día. Eso no parece un gran problema. Pero cuando uno  tienen “mucha información”, como todas las publicaciones de los Mexicanos en facebook en dos años, o las transacciones de todos los clientes de un banco, o de todas las consultas que se hacen en Google por año (que son actualmente 1.2 trillones de búsquedas); esa cantidad de información ya no resulta tan fácil de pensar en cómo graficarla o cómo empezar a explorarla.

Revisando un poco de historia, Jhon Tukey en el año 1977 publica su libro sobre “Exploración de datos”, mucho años antes de que tuviéramos computadoras personales con la potencia de gráficos que ahora tenemos. En su libro resaltaba que era fundamental el diseñar herramientas simples para realizar una exploración de los datos antes de proceder a construir un modelo o aplicar alguna técnica inferencial. Hace una distinción entre el análisis confirmatorio y el análisis exploratorio, de manera general uno pude pensar que el confirmatorio es un modo deductivo de estudiar los datos y el exploratorio un modo inductivo. Es decir, en el confirmatorio se platea validar una hipótesis, probar una “ley” o algo que explica de manera general el comportamiento de todos los datos analizados. En el exploratorio se busca ir de lo particular a lo general, de unas cuantas variables, de calcular algunas cosas o  hacer ejercicios gráficos  de donde se puede ir concluyendo el comportamiento global de los datos.

En el exploratorio, uno debe tener duda de lo que su mente deduce desde algún gráfico o técnica de visualización, no se debe de concluir inmediatamente solo confiando en los que “vemos”, en lugar de eso uno debe de plantear algunas preguntas, ir mejorando la calidad de la exploración y posteriormente poner a prueba nuestras hipótesis por medio del análisis confirmatorio.

Pero antes de compartir algunos ejemplos de exploraciones, es bueno tener claro a qué me referimos cuando digo “datos”. Ahora que la palabra Big Data o Data Sciences está en boca de todos, se tienen ideas intuitivas de que siempre estamos generando “datos”, ejemplo; cuando publicamos algo en nuestro muro de facebook, cuando subimos algún vídeo, cuando mandamos algún e-mail, cuando enviamos un mensaje a través de una app, cuando hacemos alguna llamada, cuando elegimos alguna película por medio de una plataforma en internet, cuando realizamos alguna búsqueda en google, bing o yahoo. No es falso, efectivamente en cada uno de los casos anteriores  generan datos.

Lo que me parece que imaginamos como “datos” en general es un puñado de números o cifras. Pero no piensa que también lo son los vídeos, mensajes de texto, cadenas alfanuméricas o imágenes.

Lo fundamental es que desde cualquier tipo de dato se pueda obtener información cuantitativa, aunque solo nos parezca que nuestra información es cualitativa, que a primera vista nos parece poco natural como analizar los mensajes de texto, email o fotos.

Con esta explicación trato de decir que siempre se tiene una noción clara de como calcular la media de una colección de números, pero no es claro como asignar algún valor a un texto. Sin embargo, para muchos tipos de datos existen herramientas o métodos de procesamientos por medio de los cuales se pueden explorar y analizar, pero siempre es posible diseñar un nuevo método o indicador.

Ejemplo, en un texto se puede pensar en analizar la frecuencia de las palabras, la longitud de las oraciones, la hora de emisión de texto, compararlo contra otro y poco a poco establecer un modo para clasificar.

Otro problema común es el saber cómo asignar un tipo de distribución a una colección de datos, en ocasiones se asigna alguna conocida debido a que se creé “entender” cual es el proceso que genera esos datos. En algunos casos funciona  bien ese método, ejemplo la distribución de Poisson para modelar las llegadas a la fila del banco o la atención de llamadas telefónicas a un call center. Pero en la mayoría de los casos no es claro qué distribución tiene la información, así que parte importante de la exploración es tener técnicas para encontrar algunas posibles y después ir validando las candidatas con los datos que se  tienen.

Ahora si, Machine Learning y Análisis Exploratorio

Buena parte de las técnicas de Machine Learning (ML) son exploratorias o tienen algo de ello. Entre la clasificaciones de técnicas de ML a las que se refieren como aprendizaje no supervisado, prácticamente puede ser pensado como análisis exploratorio.

Un concepto clave de lo que son los “datos”  para R project, es lo que se llama “matriz” o arreglo bidimensional. En general es como se ordena la información. Entonces se tienen columnas que serán las variables asignadas a cada uno de los elementos de estudio, que están nombrados y ordenados en las filas de la matriz. Ejemplo, en la columna se puede tener las variables: peso, altura, edad, ancho de espalda, ancho de cintura, etc. En las filas, se tienen ordenado como: persona 1, persona 2, persona 3, etc.

Lo anterior, es en la práctica lo deseable. Pero es común que la información no este bien ordenada ni limpia. Por lo cual es importante revisarla, tenerla ordenada antes de iniciar la exploración, eso permite ir conociendo el tipo de datos, el origen y las variables.

Para replicar el primer ejemplo se deben de trasladar por medio de R ( o R Studio) al directorio donde se encuentran los datos o desde la consola de R pasar al directorio correspondiente. Para replicar el ejemplo, supongo que se descargaron los datos del texto Machine Learning for Hackers que se encuentran en GitHub.

#Abrimos los datos
data.file<-file.path("data","01_heights_weights_genders.csv")
heights.weights<-read.csv(data.file)

Esto se puede hacer de otro modo, si solo se cambia uno de directorio. Todo lo hago pensando en que se usa la consola de R project, ya que creo que es base conocerla y después de eso usar R Studio no genera mayor problema.

#Cargamos los datos
heights.weights<-read.csv(01_heights_weights_genders.csv)
head(heights.weghts)

Hago ahora uso de un simple comando para conocer aspectos de los datos, summary, por el cual se obtiene un resumen de la información de las columnas. En el ejemplo se tiene:

  Gender         Height          Weight     
 Female:5000   Min.   :54.26   Min.   : 64.7  
 Male  :5000   1st Qu.:63.51   1st Qu.:135.8  
               Median :66.32   Median :161.2  
               Mean   :66.37   Mean   :161.4  
               3rd Qu.:69.17   3rd Qu.:187.2  
               Max.   :79.00   Max.   :270.0  

En caso de que se tengan bastantes columnas y solo se tiene interés en algunas de ellas, se puede extraer la información con la función with del siguiente modo:

#Elegimos la columna Height
heights<-with(heights.weights),Height)
summary(heights)

En caso de no conocer el nombre de la columna, se puede usar la función head, para ver los nombres antes de extraer la información.

Con summary, se obtendrá el valor de la media, del máximo, del mínimo y la media. Pero se puede calcular directamente el valor del  máximo, el mínimo y le media, con las funciones mean,max,min, aplicadas a la columna de datos “heights”.

Para tener claro que tipo de cosa se busca revisar y con qué funciones, se puede clasificar la exploración en:

  1. Medidas de Localización
  2. Medidas de Dispersión
  3. Medidas de Forma

Medidas de Localización

Entre estas medidas se consideran el calculo de la media, del mínimo, del máximo, la moda, los cuantiles.

Medidas de Dispersión

Entre estas se consideran el rango de intercuantiles, el rango de los datos, la varianza y la desviación estándar.

Medidas de forma

Estas medidas son la asimetría y la kurtosis.

Para cada una de estas medidas se tienen un comando o función en R.

#Medidas de localización para Height
mean(heights)
# Vale la pena revisar los parámetros que se pueden manipular para la siguiente función, así podemos definir los rangos que deseamos ver de los cuantiles
quantile(heights)
#El comando summary presenta un resumen 
summary(heights)

Para las medidas de dispersión se cuenta con:

#Medidas de disperción para Height
range(heights)
IQR(heights)
var(heights)
sd(heights)

Para las medidas de forma uso la librería “e1071“, para lo cual se debe de instalar ya que no está por default en R.

#Medidas de forma para Height
library(e1071)
#También se puede usar la librería moments
skewness(heights)
kurtosis(heights)

Los anteriores conceptos, sobre todo las funciones en R, pueden resultar poco alentadoras para dar idea de como se comportan los datos y si es primera vez con estas cosas, resultan poco claro lo que se hace. La ventaja de R es que cuenta con una gran cantidad de gráficas que permiten entender mejor los conceptos anteriores.

Algunas gráficas en R project para explorar datos

Entre los gráficos más usados para explorar una variable se encuentra el  histograma, la función hist permite obtener el gráfico y cuenta con varios parámetros los cuales pueden ser consultados en el menú de R.

#Construcción del histograma
hist(heights,nclass=20, main="Histograma de las Alturas", xlab="alturas",ylab="Frecuencias", col="red")

Histograma

Se puede conocer un aproximado de la densidad de nuestros datos, en R se puede calcular con la función density.

#Construcción de una gráfica de densidad
plot(density(heights), main="Densidad", xlab="alturas",ylab="Densidad estimada", col="red",type="p")

Densidad
Para mayor conocimiento de las funciones density y plot  recomiendo consultar el menú de R y probar como se pueden modificar detalles como los títulos o agregar otra gráfica. Se puede hacer otro ejemplo con la función plot , para ver la relación entre dos variables de datos hago el ejemplo con pesos(weights) y alturas(heights).

#Construcción de un gráfico con las dos variables
plot(heights,weights, main="Gráfica entre las dos variables", xlab="Alturas",ylab="Pesos", col="green",type="p")

Plot_de_pesos_y_alturas

Un gráfico que me parece importante para explorar las variables y poder estimar distribución de los datos, se construye usando la función ecdf. Esto permite ajustar mediante alguna curva los valores estimados de la distribución y principalmente cuando las colas de las distribuciones presentan cierto comportamiento, les llaman distribuciones de colas pesadas.

#Exploración de la cola de la distribución
f<-ecdf(heights)
range(heights)
[1] 54.26313 78.99874
ttx}) estimada", xlab="Valores de Height", col="6",log="y",type="p")

Colas_de_las_dist

En particular la paquetería de R para gráficos es bastante buena, usando la función layout uno puede construir gráficos más elaborados. Un ejemplo es el siguiente, del cual no explico detalles del código pero pueden consultarse en el manual de R.

#Construcción de gráfico con histogramas marginales
x <- pmin(3, pmax(-3, rnorm(200)))
y <- pmin(3, pmax(-3, rnorm(200)))
xhist <- hist(x, breaks=seq(-3,3,0.5), plot=FALSE)
yhist <- hist(y, breaks=seq(-3,3,0.5),plot=FALSE)
top <- max(c(xhist$counts, yhist$counts))
xrange <- c(-3,3)
yrange <- c(-3,3)
b<-matrix(c(2,0,1,3),2,2,byrow=TRUE)
nf <- layout(b, widths=c(3,1), heights=c(1,3), respect=TRUE)
par(mar=c(4,4,1,1))
plot(x, y, xlim=xrange, ylim=yrange, xlab="x", ylab="y",type="p")
par(mar=c(0,4,1,1))
barplot(xhist$counts,axes=FALSE, ylim=c(0, top), space=0,col="2")
title(main='Scatterplot con histogramas marginales',font=2)
par(mar=c(4,0,1,1))
barplot(yhist$counts, axes=FALSE, xlim=c(0, top),space=0, horiz=TRUE,col="3")

Gráfico_con_layout

Pese a que los gráficos que se tienen por default son buenos, también existen librerías que tratan de mejorar la presentación y manipulación de datos. En breve habla de ggplot, la cual es una librería que no solo está para R project, también existe para Python y es muy recomendable aprender a manipularla, ya que tiene ventajas por que permite ahorrar el proceso de manipular los datos y los gráficos son bastante agradables. En general la librería es bastante madura y cuenta ya con buenas referencias para su aprendizaje.

#Construcción de un histograma con ggplot
ggplot(heights.weights, aes(x = Height)) + geom_histogram(col="2")

ggplot_hist

Al inicio al escribir una gráfica en ggplot parece demasiado enredado comparado con poner solo un comando. Pero con la práctica se empieza hacer notar que además de que la calidad de los gráficos es mejor, se puede diseñar gráficos mucho mas agradables y se tienen mucha libertad para poderlos modificar.

Ejemplo, para revisar las densidades de las alturas con respecto a los géneros resulta mucho más sencillo hacerlo mediante la siguiente indicación.

#Construcción de gráfico de las densidades por género
ggplot(heights.weights, aes(x = Height, fill = Gender)) + geom_density() + facet_grid(Gender ~ .)
ggplot(heights.weights, aes(x = Height, fill = Gender)) + geom_density()

ggplot_densidad_de_h

ggplot_Heights_for_g

Para hacer una gráfica de las alturas vs los pesos, se requiere solo la siguiente línea y además se agrega la tendencia.

#Construcción de gráfica scatterplot
ggplot(heights.weights, aes(x = Height, y = Weight)) + geom_point() + geom_smooth()

ggplot_scatterplot_HW_tendencia

Como muchas técnicas de Machine Learning son para clasificar, un ejemplo sencillo que se pueden hacer es el distinguir las poblaciones por género y buscar la “mejor”  línea que límite esos datos.

#Transformamos los datos para analizar como se comporta y graficamos la recta que divide a entre géneros

#Primero transformamos los datos
heights.weights <- transform(heights.weights,Male = ifelse(Gender == 'Male', 1, 0))
#Ahora claculamos una regresión lineal general
logit.model <- glm(Male ~ Weight + Height,data = heights.weights,family = binomial(link = 'logit'))
#Por último contruimos el gráfico
ggplot(heights.weights, aes(x = Height, y = Weight)) +
  geom_point(aes(color = Gender, alpha = 0.25)) +
  scale_alpha(guide = "none") + 
  scale_color_manual(values = c("Male" = "black", "Female" = "blue")) +
  theme_bw() +
  stat_abline(intercept = -coef(logit.model)[1] / coef(logit.model)[2],
              slope = - coef(logit.model)[3] / coef(logit.model)[2],
              geom = 'abline',
              color = 'red')



ggplot_HW_plano_separador

 Una observación que es crucial en la exploración de datos, es que en ocasiones se requiere que se “transformen”. Con eso quiero decir que si se tiene una gráfica como la anterior, pero donde el rango de los datos en el eje X es muy grande, es posible que sustituyendo los datos originales por su logaritmo se pueden estudiar de modo más “cómodo”. Pero cuando se transforman los datos o se usa alguna transformación, es recomendable considerar que dicha función respete el orden en el cual tienen los datos,  que sea continua y hasta cierto punto “suave”, que tenga sus derivadas. Esto es quizás técnico, pero es importante tenerlo en cuenta.

Espero la entrada sirva para tener una idea general de lo que es la exploración, en buena medida muchas de las técnicas más avanzadas, que van desde generalizaciones de Análisis de Componentes Principales hasta la Homología Persistente  y variedades de aprendizaje son técnicas exploratorias, requieren cierto conocimiento de lo que se hace para que sea fácil la  interpretación de sus resultados y conocer en qué situación es recomendable usar cada técnica.

Referencias:

1.-http://www.amazon.es/Exploratory-Analysis-Edition-Chapman-Computer/dp/1439812209

2.-http://www.amazon.com/ggplot2-Elegant-Graphics-Data-Analysis-ebook/dp/B0041KLFRW

3.-http://www.amazon.com.mx/Introductory-Beginners-Visualisation-Statistical-Programming-ebook/dp/B00BU34QTM

4.-http://www.amazon.es/Graphics-Second-Edition-Chapman-Series/dp/1439831769

5.-http://www.r-bloggers.com/

¿Qué se necesita conocer de R project para aprender algunas técnicas de Machine Learning?

Sobre R

El lenguaje R o R project, en recientes años se ha vuelto muy popular, sobre todo en el ámbito académico y poco a poco empieza a sonar en el sector de la industria de la Tecnologías de la Información (TI).

Tan es así, que Oracle una empresa líder en tecnología y conocida por su gestor de bases de datos con el mismo nombre, ahora ofrece una versión de R dentro de su gestor de bases de datos. No he probado la versión, por lo cual desconozco si tienen todas las funcionalidades iguales o si tienen ciertas restricciones al compararlo con la versión de R que uno descarga libremente.

Si uno busca en la red o en la página principal de R project, se encontrará con la abrumadora noticia de que existen aproximadamente más de 7 000 librerías. Las librerías son un bloque de código o funciones que alguien escribió y compartió libremente para hacer uso de ellas en el software.

Al ser libre R project uno tienen la posibilidad de compartir algunas funciones que considere que son buenas y que a otros les pueden servir, eso hace que una comunidad a nivel mundial esté actualizando librerías o compartiendo nuevos algoritmos.

Entre todas las librerías uno puede encontrar un poco de todo. Se encuentran librerías que permiten la manipulación de datos, realizar conexiones a la red, automatizar pronósticos, manipular y analizar textos, generar reportes, conectarse con gestores de bases de datos, manipular mapas, etc.

Si uno toma alguna técnica  estándar para hacer algo de estadística, es probable que exista más de una librería que nos permite hacer eso. Por lo tanto pretender conocer todas las librerías es una tarea casi imposible y lo que recomiendo es ir consultando en la red, en foros o blog para conocer las librerías que sugieren para realizar tal o cual técnica.  Al final las mejores librerías y las más usadas tenderán a persistir en las recomendaciones que haga la gente.

Existen muchos manuales básicos para aprender a usar R [1,2,3,4,5]. Lamentablemente el manual de introducción en español no ha sido actualizado desde hace años, pero se cuenta con muchas referencias que van desde blog, notas y textos  que cubren prácticamente todo lo que uno debe de saber a un nivel básico o intermedio, y posiblemente hasta un poco avanzado ( hay un libro hermoso de temas avanzados escrito por Hadley Wickham que deben de apurarse a leerlo).

Lo “malo” hasta cierto punto es que las mejores referencias se encuentran en Ingles, es importante aprender por lo menos a leer en dicho idioma pero hace falta en ocasiones manuales en español que clarifiquen los conceptos e ideas.

Al final lo recomendable para aprender cualquier software es practicar, tomar varias fuentes para aprender y replicar ejemplos. Estudiar el código que otros hacen y hacer las modificaciones que uno pueda ir pensando que mejoran dicho código, ya sea para optimizarlo o solo para probar si se puede usar para hacer otra cosa.

Lo que puedo decir es que R project es un software bastante sencillo de aprender,  lo complicado no radica en el software mismo, sino conocer lo que hacen comandos, técnicas, algoritmos y librerías. A gente que lleva toda su vida haciendo software le desconcierta y le parece muy “científico” como para aprenderlo, pero con algo de tiempo y algunas revisiones de como declarar variables, hacer funciones, manipular tipos de datos, etcétera; van perdiendo el miedo a usarlo.

R project y Machine Learning

La instalación en el sistema que sea, Windows, Mac Ox o Linux, son sencillas y el IDE (Entorno de Desarrollo Integrado) es prácticamente igual para todos los sistemas. Lo único que es importante y que cambia es al correr un script desde la consola del sistema. En general la consola de Windows es muy pobre comparada con las de los otros dos sistemas, pero se puede fortalecer usando otros software. Para ese caso en otro momento explico el tipo de cosas que hay que hacer para correr los script.

Un complemente para programar en R es usar el entorno R Studio, que prácticamente es tener un entorno de desarrollo amigable, de mejor vista y con herramientas que simplifican el trabajo al momento de programar en R. Existen  otros entornos, pero es cosa de cuál se adecua mejor a nuestro modo de trabajo y necesidades.

En el caso de los ejemplos de Machine Learning, lo necesario del software se puede clasificar en:

  1. Funciones estándar en R.
  2. Importación, exportación y manipulación de datos.
  3. Funciones particulares de ciertas librerías.

Del primer punto las funciones estándar son: lm, plot, hist, summary, head, apply, sapply, poly, tail, dim, str, names, c, paste, princomp, subset, cov, cor, ts, entre otras. Lo recomendable es revisar por medio de la función help la información de los parámetros y ver algún ejemplo del uso de dicha función, como siempre la recomendación es hacer pruebas para ir mejorando en el manejo de dichas funciones o comandos. Por supuesto que existen más funciones que no menciono, pero son en general las que de momento creo se usan más en las entras.

Sobre la importación y exportación de datos, lo principal en mi opinión es tener claro que el tipo de dato base en R son los vectores o en otras palabras R es un lenguaje “vectorizado” y en general la mayoría de funciones que se usan depende de asignarle data.frame o matrices, que son construidos con vectores.

La importación de los datos se hace por medio de las funciones read.csv, read.txt,scan entre otras. Para este tema se puede consultar un pequeño manual de aproximadamente 30 páginas en la página oficial de R. En general se requiere practicar con la importación de datos, ya que en ocasiones pedir que se importen datos con cierta restricción, como por ejemplo pedir que las cadenas no sean leídas como factores, que se separen las columnas por tabuladores,etc.

Respecto a la manipulación, se cuenta con algunas funciones subset, with, ifelse, cbind, rbind   las cuales son estándar del software. Pero se cuenta con varias librerías que permiten la manipulación de datos, entre ellas, reshape2,  gdata ,data.table, plyr, dplyr.

Del tercer punto, para aprender sobre algunas funciones de ciertas librerías mi recomendación es revisar los manuales de usuarios que son publicados cuando se libera la librería o paquete, es decir; cuando nos permiten usar su código otras personas que usan R project.

La mayoría  de las librerías suelen contar con algunos ejemplos, datos y  en Internet suele haber información casi suficiente para conocer como funcionan. Creo que lo mejor es tratar de replicar los ejemplos, cambiar las condiciones de parámetros y crear un ejemplo, también esto resulta más divertido para comprender lo que hacen las funciones.

Algunas librerías son más populares que otras y en buena medida es por la calidad y utilidad. Ejemplo, la librería ggplot2  es bastante buena para gráficos, se cuenta con libros y notas que explican su uso con buen detalle, además de que en recientes años se ha hecho muy popular y es considerada unas de las herramientas indispensables en análisis de datos.

Otra librería que ha tenido popularidad es tm, que se refiere a Text Mining ( Minería de Textos); se cuenta con bastante información en la red sobre como usar sus funciones con algunas ejemplos, pero además está relacionada con las investigaciones del Procesamiento de Lenguaje Natural (NLP, en ingles).

Por último las librerías que son populares para analizar redes es igraph y sna, con redes me refiero a redes sociales o redes complejas, esta librería cuenta con una gran cantidad de funciones. La gran ventaja es que se cuenta con suficiente explicación y ejemplos debido a la gran popularidad de las redes sociales en nuestro día a día, entonces basta con buscar un poco en la red para encontrar buen material al respecto [6,7].

Espero las explicaciones generen interés en explorar más sobre R project. La última observación que hago es entender que este software permite rápido aprender ciertas técnicas, pero existen otros software que ante proyectos grandes puede ser mejores( Python, C,Ruby, Java, scala), pero sin duda es una buena fuente para aprender practicando,  R lo permite con una curva de aprendizaje muy pequeña.

Comentario: Debido al desarrollo de R y  la creación de nuevas librerías, con el paso del tiempo algunos de los comentarios respecto a las librarías puede dejar de ser actualizado. Ejemplo, ggplot2 ha evolucionado no solo con mejoras y más funciones, sino también con la aparición de otro tipo de librerías que se abocan a la visualización de datos a nivel web. Otro ejemplo es en el procesamiento de datos, debido a la relevancia de plyr y dplyr es posible que aparezcan librerías más optimizadas o con otro tipo de características, ejemplo data.table es mejor para ciertas operaciones con datos. Así que siempre hay que tener en mente que los software evolucionan, pero ello es recomendable conocer de manera solida las estructuras base o herramientas base.

“The best thing about R is  that it was developed by statisticians. The worst thing about R is that….it was developed by statisticians.”  Bo Cowgill, Google, Inc.

Referencias:

1.-The R inferno.

2.-Using R for Numerical Analysis in Science and Engineering.

3.-A handbook of Statistical Analyses using R.

4.-Data Manipulation with R.

5.-R in Action.

6.-Igraph

7.-SNA

8.-R-bloggers

9.-Revolution Analytic