library(dplyr)
library(tm) # usado para la limpieza y exploración de textos, text mining
library(ggplot2)
library(SnowballC) # used for text stemming, reduce la palabra a su raiz. 
library(wordcloud) # word-cloud generator
library(RColorBrewer) # paletas de colores
library(knitr)
library(wordcloud2) # word-cloud animado
library(factoextra) # Visualización de análisis multivariados
library(cluster)
library(tidytext)
mv<-read.csv("C:/Users/Natalia/Desktop/Universidad y Estudios_030222/Rstudio/RPUBS/PUBLICACIONES/Mineria de textos/tmdb_5000_movies.csv",
             header = T,sep = ",",stringsAsFactors = F,encoding = "UTF-8")

La dimensión de la base es de 4803 filas y 20 columnas


Exploración del dataset

Inicialmente por curiosidad, observaremos las peliculas con mejores puntajes promedios junto con la cantidad de votos que recibieron.

kable(mv %>% top_n(n=10,vote_average) %>% select(title,vote_average,vote_count) %>% arrange(-vote_average),align = 'c')
title vote_average vote_count
Stiff Upper Lips 10.0 1
Dancer, Texas Pop. 81 10.0 1
Me You and Five Bucks 10.0 2
Little Big Top 10.0 1
Sardaarji 9.5 2
One Man’s Hero 9.3 2
The Shawshank Redemption 8.5 8205
There Goes My Baby 8.5 2
The Prisoner of Zenda 8.4 11
The Godfather 8.4 5893

No se pueden tener en cuenta las peliculas con un buen puntaje promedio pero con solo un usuario o pocos usuarios como votantes, pues unos pocos no son representativos. Para mitigar el efecto se debe penalizar el ranting por cantidad de personas votantes.

Para efectos de ello, se tendrá en cuenta la siguiente calificación ponderada:

\[ WR= (\frac{v}{v+m}\cdot R) + (\frac{v}{v+m} \cdot C) \]

Donde:

c<-mean(mv$vote_average)
m<-quantile(mv$vote_count,0.9)
wr<-function(x){
  v<-x["vote_count"]
  r<-x["vote_average"]
  m<-quantile(x$vote_count,0.9)
  c<-mean(x$vote_average)
  return((((v)/(v+m))*r)+ (((m)/(v+m))*c))
}
tab_res=mv %>% mutate(rating_penalizado=wr(x=mv)) %>% select(title,rating_penalizado,vote_average, vote_count) %>%
  top_n(n=8,rating_penalizado) %>% arrange(-rating_penalizado)
kable(x = tab_res,col.names = c("Titulo","Rating_penalizado","Rating_inicial", "Cantidad_votos"), align = "l")
Titulo Rating_penalizado Rating_inicial Cantidad_votos
The Shawshank Redemption 8.059258 8.5 8205
Fight Club 7.939256 8.3 9413
The Dark Knight 7.920020 8.2 12002
Pulp Fiction 7.904645 8.3 8428
Inception 7.863239 8.1 13752
The Godfather 7.851236 8.4 5893
Interstellar 7.809479 8.1 10867
Forrest Gump 7.803188 8.2 7927

Se puede observar que las películas con mejor calificación varian ahora que se han penalizado las que poseían pocos votantes.

Pre-procesamiento del data

title<-mv[,"title"] # convertirlo en un array
docs<-Corpus(VectorSource(title)) # formato de texto,coleccion de docs

Usaremos tm_map() para reemplazar caracteres especiales del texto

toSpace <- content_transformer(function (x , pattern ) gsub(pattern, "", x)) # Definir vacio

#Reemplazar ":" y "-" con caracteres vacios
docs<-tm_map(docs,toSpace,":")
docs<-tm_map(docs,toSpace,"-")
docs<-tm_map(docs,tolower) #Llevar a minusculas
docs<-tm_map(docs,removeNumbers) #Quita números
docs<-tm_map(docs,stripWhitespace) #Quita espacios blancos
docs<-tm_map(docs,removePunctuation) #Quita puntuaciones

Posteriormente se eliminará todas aquellas palabras denominadas Stopwords (palabras basura).

docs<-tm_map(docs,removeWords,stopwords("en"))

Exploración y visualización

Se lleva el documento ya “limpio” a un tipo documento matriz, donde cada fila representa una palabra y las columnas el id de la pelicula. Esto permitirá que al sumar por fila arroje la frecuencia de dicha palabra.

dtm <- TermDocumentMatrix(docs)
m <- as.matrix(dtm)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)
kable(head(d, 10),align = 'c')
word freq
man man 66
love love 56
movie movie 43
dead dead 40
last last 40
good good 32
house house 32
story story 30
big big 30
day day 30

Nubes de palabras

wordcloud(words = d$word,freq = d$freq,max.words = 50,colors = brewer.pal(n = 8,"Dark2"),random.color = T,rot.per = 0.35,random.order = F)

#rot.per establece qué porcentaje de palabras se rotan
#random.oder: si es Falso palabras frecuentes al centro
# Nube de palabras de forma interactiva
library(wordcloud2)
wordcloud2(d,shape = "diamond",size = 0.4)

Top 20

top20 <- head(d, 20)
top20$word <- reorder(top20$word, top20$freq)

ggplot(top20, aes(x = word, y = freq, fill = word, label = freq)) +
  geom_bar(stat="identity", show.legend = FALSE) +
  coord_flip() + # De vertical pasa a horizontal
  labs(title = "Top 20 Most Used Words in Movie Title", x = "Word", y = "Word Count") +
  geom_label(aes(fill = word),colour = "white", fontface = "bold", show.legend = FALSE)+
  theme_minimal()+ theme(plot.title = element_text(hjust=0.5,face="bold"))

Análisis de sentimientos

library(tidytext)
library(textdata)
library(tidyr)
library(reshape2)

El paquete tidytext contiene 3 diccionarios distintos:

nrc<-get_sentiments("nrc")
Base_ana<-d %>% inner_join(nrc,"word") # Unimos el data de sentiemientos con nuestras palabras para que las clasifique según concordancia
Base_ana<-arrange(Base_ana,-freq) #ordenamos de forma descendente por frecuencia

nube de palabras por sentimiento

b5<-data.frame(Base_ana %>% group_by(word,sentiment) %>% summarise(n=n())) %>% arrange(-n)

b5 %>% acast(word~sentiment,fill = 0,value.var = "n") %>% #llenar los missing values con cero
  comparison.cloud(max.words = 500, #Maximo de palabras a representar
                   colors = brewer.pal(n = 10,"Paired"), #Cantidad de colores diferentes equivalente a cantidad de sentimientos
                   title.size = 1,title.colors = "black", #Color de titulos de sentimientos 
                   title.bg.colors = "white") #Resaltado de titulos

Top 10 por sentimiento

Base_ana %>% group_by(sentiment) %>% top_n(10, freq) %>% # Las 10 palabras con mayor frecuencia clasificadas por su respectivo sentimiento
  ungroup() %>%
  mutate(word=reorder(word,freq)) %>% 
  ggplot(aes(x=word,y=freq,fill=sentiment))+
  geom_col()+ # grafico de columnas
  facet_wrap(facets = ~sentiment,nrow = 3,scales = "free")+ #scales="free" para cada grafico de barra me ponga su propio eje x, es decir sus propias palabras
  coord_flip() + # De horizontal pasa a vertical
  theme_light() + theme(axis.text.y = element_text(size = 7), 
                          axis.text.x = element_text(size = 5)) + theme_bw()

Sentimientos predominantes

Base_ana %>%  group_by(sentiment) %>% summarise(n=n()) %>% 
  ungroup() %>% mutate(sentiment=reorder(sentiment,-n)) %>% 
  ggplot(aes(x=sentiment,y=n)) + geom_col(aes(fill=sentiment)) + 
  theme_classic() + labs(title="Cantidad de sentimientos encontrados",
                         y="Frecuencia",fill="Sentimiento",x="Sentimiento") + 
  theme(plot.title = element_text(face="bold",hjust = 0.5),
        legend.title = element_text(face="bold"))

Peliculas clasificada según resumen

Se mirará el sentimiento asociado(positivo o negativo) a cada pelicula segun resumen o descripción de la misma.

title<-mv[,c("title","overview")] #overview: resumen
title_2<-data.frame(title)
title_def<-tibble::rowid_to_column(title_2, "ID") #Agregar un ID

unnest_tidy <- title_def %>%
  unnest_tokens(word, overview) %>% #Desagrega el resumen por palabra
  anti_join(stop_words) #Elimina los stop_words

set_bing<-unnest_tidy %>% 
  inner_join(get_sentiments("bing"))
b3=data.frame(set_bing %>% group_by(word,sentiment) %>% summarise(n=n()))
b3 %>% acast(word~sentiment,value.var = "n", fill = 0) %>% 
  comparison.cloud(colors = c("red","green"), 
                   max.words = 100,
                   title.size = 2,
                   title.colors = "black", # color de titulo
                   title.bg.colors = "white") # fondo del titulo

Se mirará el sentimiento asociado a cada pelicula segun resumen de la misma.

unnest_tidy <- title_def %>%
  unnest_tokens(word, overview) %>%  
  anti_join(stop_words,by = "word")

an<-unnest_tidy %>% inner_join(get_sentiments("nrc"))
top<- an[1:1000,]
map<-data.frame(table(top$title,top$sentiment)) # cuantas veces cada sentimiento aparece en el resumen de cada pelicula
ggplot(map,aes(x=Var2,y=Var1)) + 
  geom_tile(aes(fill=Freq),color="white") +
  scale_fill_gradient(low="white",high = "coral3")+
  ylab("sentimiento") + xlab("película")+
  ggtitle("Sentimiento según pelicula")+ theme_bw() +
  theme(plot.title = element_text(hjust = 0.5,face="bold",size=13),text = element_text(size=12), axis.text.x = element_text(angle=45, hjust=1))