martes, 25 de octubre de 2011

Análisis de Satisfacción de Usuarios de Telefonía Celular en Twitter con R - Minería de Datos en Redes Sociales :

#El elemental ejemplo de minería de datos en redes sociales que desarrollaremos está basado, con algunas variantes, en este estudio..
# http://dl.dropbox.com/u/4839225/twitter%20text%20mining%20with%20R.pdf

#La idea es analizar la satisfacción y simpatía que los clientes tienen hacia las empresas telefónicas de Argentina y cómo la expresan a través de las redes sociales, en este caso en Twitter..
#Para esto utilizaremos el entorno de análisis estadístico R y el paquete con la API para Twitter denominado muy creativamente “TwitteR”..
#Los usuarios de Twitter de empresas de telefonía celular en Argentina que seleccionamos son:
#•    @PersonalAr
#•    @MovistarArg
#•    @ClaroArgentina

#Abrimos el R y en R Console comenzamos..

#Primeramente vamos a instalar y abrir todos los paquetes que necesitaremos..
#Si nos da la opción elegimos el CRAN del país que queramos..
install.packages('plyr')
require(plyr)
install.packages('stringr')
require(stringr)
install.packages('twitteR')
require(twitteR)
install.packages('ggplot2')
require(ggplot2)
install.packages('doBy')
require(doBy)

#Extraemos los últimos 1500 twitts que nombren a cada empresa..
personalar.tweets = searchTwitter('@PersonalAr', n=1500)
length(personalar.tweets)
movistararg.tweets = searchTwitter('@MovistarArg', n=1500)
length(movistararg.tweets)
claroargentina.tweets = searchTwitter('@ClaroArgentina', n=1500)
length(claroargentina.tweets)

#Para cada twitt extraemos sólo su texto..
personalar.text = laply(personalar.tweets, function(t) t$getText() )
movistararg.text = laply(movistararg.tweets, function(t) t$getText() )
claroargentina.text = laply(claroargentina.tweets, function(t) t$getText() )

#Creamos la lista de palabras positivas y la lista de las negativas, mejor si están relacionadas con el rubro del ejemplo..
#Eso lo haremos en 2 archivos de texto plano separado:

#palabras-positivas.txt
#Con las siguientes palabras positivas: (Separadas sólo por espacio)
#amor amo mejor maravilloso grande grandes bueno buenas buena increíble increíbles fácil fáciles gracias solucionado solucionamos solucionar conveniente convenientes bonificar  bonifican bonificación copada beneficio aprovechar buenos estimada sin peor buena excelente pude velocidad disponible anda solución lindo también alguien alguno siempre nuevo genial fantástico amigo compañero responsable audaz granade bello interesante inteligente honesto honestos seguro responsables atento honrado trabajador prudente espontáneo delicado civilizado educado prolijo decidido fuerte comprensible entretenido alegre simpático familiar cuidadoso pulcro

#palabras-negativas.txt
#Con las siguientes palabras negativas: (Separadas sólo por espacio)
#odio peor asco pesadilla malo horrible feo despreciable maligno espero esperando spam corta no no nada azotar suspender baja problema cambiar mal cae asesinó destruyó pésimo urgente quema fallando falla fallas inútiles lenta lentas lento lentos deuda pagar sin grosos fuera histérica histérico lamentable peor tampoco nadie nunca ninguno ninguna ni jamás borraron desapareció desaparecieron bruto feo fastidioso egoísta arrogante mentiroso mentira mentir cruel bago bago tacaño paranoico resentido envidioso impaciente desordenado desprolijo orgulloso

#Lo alojamos en la carpeta R dentro del disco C:'C:/R'
pos.words = scan('C:/R/palabras-positivas.txt', what='character', comment.char=';')
neg.words = scan('C:/R/palabras-negativas.txt', what='character', comment.char=';')
#Vemos las palabras cargadas..
pos.words[1:70]
neg.words[1:70]


#Cargamos la función "score.sentiment", esta le asigna un valor positivo o negativo a cada palabra encontrada que coincida con la de alguna de las 2 listas y luego suma esos valores por cada twitt..
score.sentiment = function(sentences, pos.words, neg.words, .progress='none')
{
require(plyr)
require(stringr)
# we got a vector of sentences. plyr will handle a list or a vector as an "l" for us
# we want a simple array of scores back, so we use "l" + "a" + "ply" = laply:
scores = laply(sentences, function(sentence, pos.words, neg.words) {
# clean up sentences with R's regex-driven global substitute, gsub():
sentence = gsub('[[:punct:]]', '', sentence)
sentence = gsub('[[:cntrl:]]', '', sentence)
sentence = gsub('\\d+', '', sentence)
# and convert to lower case:
sentence = tolower(sentence)
# split into words. str_split is in the stringr package
word.list = str_split(sentence, '\\s+')
# sometimes a list() is one level of hierarchy too much
words = unlist(word.list)
# compare our words to the dictionaries of positive & negative terms
pos.matches = match(words, pos.words)
neg.matches = match(words, neg.words)
# match() returns the position of the matched term or NA
# we just want a TRUE/FALSE:
pos.matches = !is.na(pos.matches)
neg.matches = !is.na(neg.matches)
# and conveniently enough, TRUE/FALSE will be treated as 1/0 by sum():
score = sum(pos.matches) - sum(neg.matches)
return(score)
}, pos.words, neg.words, .progress=.progress )
scores.df = data.frame(score=scores, text=sentences)
return(scores.df)
}

#Probamos un ejemplo de la función “score.sentimientos" y del algoritmo..
ejemplo = c("odio a personal, se borraron dan asco", "claro el amor me envuelve a utilizar tus servicios","movistar es la pesadilla más horrible que me sucedio")
result = score.sentiment(ejemplo, pos.words, neg.words)
result$score

#Calculamos los totales de puntajes para cada empresa..
personalar.scores = score.sentiment(personalar.text, pos.words,neg.words, .progress='text')
personalar.scores$empresa = 'PersonalAr'
personalar.scores$code = 'PE'
hist(personalar.scores$score)

movistararg.scores = score.sentiment(movistararg.text, pos.words,neg.words, .progress='text')
movistararg.scores$empresa = 'MovistarArg'
movistararg.scores$code = 'MO'
hist(movistararg.scores$score)

claroargentina.scores = score.sentiment(claroargentina.text, pos.words,neg.words, .progress='text')
claroargentina.scores$empresa = 'ClaroArgentina'
claroargentina.scores$code = 'CL'
hist(claroargentina.scores$score)

#Unificamos los resultados..
all.scores = rbind(personalar.scores, movistararg.scores, claroargentina.scores )

#Graficamos..
ggplot(data=all.scores) + geom_bar(mapping=aes(x=score, fill=empresa), binwidth=1) + facet_grid(empresa~.) + theme_bw() + scale_fill_brewer()

#Ignoramos los valores medios..
all.scores$very.pos = as.numeric( all.scores$score >= 2 )
all.scores$very.neg = as.numeric( all.scores$score <= -2 )

#Comenzamos a armar la matriz de resultados con Nombre de la Empresa, Código suma por positivos y negativos..
twitter.df = ddply(all.scores, c('empresa', 'code'), summarise, pos.count = sum(very.pos), neg.count = sum(very.neg))

#Agregamos una fila de totales a la matriz..
twitter.df$all.count = twitter.df$pos.count + twitter.df$neg.count

#Calculamos el Score como: (100 * Cantidad-Positivos ) / Cantidad-Total
twitter.df$score =  round( 100 * twitter.df$pos.count /twitter.df$all.count )


orderBy(~-score, twitter.df)

#         empresa code pos.count neg.count all.count score
#1    MovistarArg   MO        38       146       184      21
#2 ClaroArgentina   CL        15        98       113      13
#3     PersonalAr    PE        15       120       135      11

#Rankeando cada empresa según el score podemos concluír al día de la fecha 25/10/2011 que en general los clientes de Movistar están más conforme con el Producto/Servicio brindado con un 21% de satisfacción, seguidos por los usuarios de Claro con un 13% y los de Personal con un 11%..

1 comentario:

  1. Buenas noches pablo. Quisiera preguntarte por qué utilizas un corpus tan pequeño (tan solo 70 palabras). Por casualidad sabes de la existencia de un corpus para el idioma español?.
    Gracias. George

    ResponderEliminar