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
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.
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"))
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"))
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"))
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))