Autor: Luis Fernando Apáez Álvarez
El análisis de sentimientos, o también llamado minería de opiniones, es el proceso de comprensión de la opinión de un autor sobre un tema, es decir, es el proceso mediante el cual podemos responder a la pregunta, ¿Cuál es la emoción u opinión del autor del texto sobre un tema?
Dentro de un sistema de análisis de sentimientos, y bajo determinado contexto, se tienen 3 puntos claves:
El análisis de sentimientos tiene gran variedad de aplicaciones prácticas tales como el monitoreo de redes sociales, opiniones en foros, blogs y noticias. En general, muchas marcar analizan las fuentes mencionadas antes para enriquecer su comprensión de cómo los clientes interactúan con su marca, con lo cual éstos pueden estar contentos o descontentos. De tal manera, el análisis de sentimientos es muy importante en campos como el análisis de clientes y productos, investigación y análisis de mercado.
Exploremos el conjunto de datos con el cual estaremos trabajando:
import pandas as pd
data = pd.read_csv('IMDB_sample.csv')[['review', 'label']]
data.head()
review | label | |
---|---|---|
0 | This short spoof can be found on Elite's Mille... | 0 |
1 | A singularly unfunny musical comedy that artif... | 0 |
2 | An excellent series, masterfully acted and dir... | 1 |
3 | The master of movie spectacle Cecil B. De Mill... | 1 |
4 | I was gifted with this movie as it had such a ... | 0 |
donde tenemos dos columnas, una para las reseñas y la segunda label
que expresa el sentimiento en general: 1 significa positivo y 0 negativo.
Veamos cuántas criticas positivas y negativas tenemos en los datos:
data.value_counts('label')
label 0 3782 1 3719 dtype: int64
O también visualizamos como porcentajes
(data.value_counts('label') / len(data)) * 100
label 0 50.419944 1 49.580056 dtype: float64
Por lo cual vemos que la muestra está equilibrada. Veamos cuál es la reseña más larga
# Creamos una nueva columna en la cual almacenamos la longitud
# de la cadena de cada resegnia
data['long'] = data['review'].apply(lambda x: len(x))
data['long']
0 667 1 2982 2 669 3 691 4 1087 ... 7496 633 7497 650 7498 654 7499 918 7500 336 Name: long, Length: 7501, dtype: int64
# Obtenemos la longitud maxima de la resegnia
data.long.max()
10321
# veamos explicitamente cual es dicha resegnia
data[data['long'] == data.long.max()]
review | label | long | |
---|---|---|---|
4930 | Titanic directed by James Cameron presents a f... | 1 | 10321 |
Podemos también hallar la reseña de menor longitud
# De manera alternativa al procedimiento anterior, de
# la clase str utilizaremos el metodo len() para calcular
# la longitud de cada resegnia
data.review.str.len()
0 667 1 2982 2 669 3 691 4 1087 ... 7496 633 7497 650 7498 654 7499 918 7500 336 Name: review, Length: 7501, dtype: int64
# Extraemos el valor minimo
data.review.str.len().min()
52
# Obtenemos explicitamente la resegnia de menor longitud
data[data['long'] == data.review.str.len().min()]
review | label | long | |
---|---|---|---|
1628 | This movie is terrible but it has some good ef... | 0 | 52 |
Por otro lado, las tareas de análisis de sentimientos pueden llevarse a cabo mediante diferentes niveles de granulidad:
Los algoritmos utilizados para el análisis de sentimientos se pueden dividir en dos categorías principales:
Por lo cual tenemos, en este caso, una oración positiva.
Los modelos de Machine learning se basan en la existencia de datos históricos, mientras que los modelos basados en reglas o léxicos parten de la creación manual de reglas o diccionarios.
Podemos calcular la puntuación de valencia de un texto utilizando la librería textblob:
# Importacion necesaria
from textblob import TextBlob
# Texto
text = "Today was a good day."
# Instanciamos sobre el texto anterior
valence = TextBlob(text)
# Accedemos a la puntuacion de valencia mediante
# el atributo sentiment
valence.sentiment
Sentiment(polarity=0.7, subjectivity=0.6000000000000001)
donde:
Ahora, consideremos la reseña más larga referente al conjunto de datos con el cual estamos trabajando
# veamos explicitamente cual es dicha resegnia
data[data['long'] == data.long.max()]
review | label | long | |
---|---|---|---|
4930 | Titanic directed by James Cameron presents a f... | 1 | 10321 |
Veamos cómo es categorizada dicha reseña, ya sea que se le asigne un sentimiento positivo, neutro o negativo, veamos además el nivel de subjetividad. Para ello
# Convertimos a cadena de texto la resegnia
titanic = str(data.iloc[4930][0])
# Veamos los primero 1000 caracteres
titanic[:1000]
'Titanic directed by James Cameron presents a fictional love story on the historical setting of the Titanic. The plot is simple, noncomplicated, or not for those who love plots that twist and turn and keep you in suspense. The end of the movie can be figured out within minutes of the start of the film, but the love story is an interesting one, however. Kate Winslett is wonderful as Rose, an aristocratic young lady betrothed by Cal (Billy Zane). Early on the voyage Rose meets Jack (Leonardo DiCaprio), a lower class artist on his way to America after winning his ticket aboard Titanic in a poker game. If he wants something, he goes and gets it unlike the upper class who are so concerned with their social worries. The two fall in love and the audience sees the sinking of the Titanic primarily through their eyes.<br /><br />The movie begins in modern times with the exploration of the wreck by a group searching for treasures, that sunk with the Titanic, which has recently occurred. One of the'
notamos la presencia de <br /><br />
. Limpiamos la reseña quitando dichos caracteres
import re
# Patron de busqueda
pattern = re.compile(r'<br /><br />')
match = pattern.search(titanic)
# Veamos la coincidencia hallada
print(match)
<re.Match object; span=(818, 830), match='<br /><br />'>
# Sustituimos todos los <br /><br /> por espacios en blanco
titanic_clean = pattern.sub(' ', titanic)
titanic_clean[:1000]
'Titanic directed by James Cameron presents a fictional love story on the historical setting of the Titanic. The plot is simple, noncomplicated, or not for those who love plots that twist and turn and keep you in suspense. The end of the movie can be figured out within minutes of the start of the film, but the love story is an interesting one, however. Kate Winslett is wonderful as Rose, an aristocratic young lady betrothed by Cal (Billy Zane). Early on the voyage Rose meets Jack (Leonardo DiCaprio), a lower class artist on his way to America after winning his ticket aboard Titanic in a poker game. If he wants something, he goes and gets it unlike the upper class who are so concerned with their social worries. The two fall in love and the audience sees the sinking of the Titanic primarily through their eyes. The movie begins in modern times with the exploration of the wreck by a group searching for treasures, that sunk with the Titanic, which has recently occurred. One of the survivors '
Una vez que el texto está limpio, continuamos
# Instanciamos sobre el texto anterior
blob_titanic = TextBlob(titanic_clean)
# Accedemos a la puntuacion de valencia mediante
# el atributo sentiment
blob_titanic.sentiment
Sentiment(polarity=0.20791734011246213, subjectivity=0.453315898193947)
donde dicha reseña es un poco positiva, un tanto más cerca a la neutralidad y donde la opinión es algo personal pero con cierta perspectiva objetiva.
Podemos acceder a la polaridad y subjetividad por separado
# Polaridad
blob_titanic.sentiment[0]
0.20791734011246213
# Subjetividad
blob_titanic.sentiment[1]
0.453315898193947
Una nube de palabras es una imagen compuesta por palabras de diferentes tamaños y colores
la cual puede ser especialmente de utilidad para el análisis de sentimientos. Pero, ¿cómo son creadas dichas nubes de palabras? A continuación veremos la manera de crearlas utilizando Python.
Las nubes de palabras son utilizadas en diferentes contextos, en particular utilizaremos aquellas en las cuales el tamaño de las palabras corresponde a la frecuencia de aparición de la misma, esto es, mientras más frecuente sea una palabra en determinado texto, más grande será en la correspondiente nube de palabras. Por otro lado, respecto a las nubes de palabras tenemos:
pros | contras |
---|---|
Pueden relevar lo esencial | A veces tiende a no funcionar bien |
Tratarán todas las palabras en un texto | Todas las palabras trazadas en la nube de palabras pueden parecer no relacionadas |
Un escaneo rápido de la imagen puede proporcionar un sentido general del texto | Podría ser difícil sacar una conclusión basada en una nube de palabras abarrotada |
Son fáciles de entender y visualmente estéticos | Si el texto es largo, la nube de palabras puede requerir bastante preprocesamiento previo |
Ahora, crearemos una nube de palabras para la reseña más larga en nuestro dataframe
para ello
# Importacion necesaria
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# Instanciamos un objeto WordCloud
word_cloud = WordCloud()
# Generamos la nube de palabras mediante el metodo generate()
# aplicado a la resegnia mas larga
cloud_titanic = word_cloud.generate(titanic_clean)
Para poder graficar la nube de palabras utilizaremos la librería matplotlib, para lo cual utilizaremos imshow()
como sigue
plt.imshow(cloud_titanic, interpolation='bilinear')
# Configuramos que los ejes no sean visibles
plt.axis('off')
plt.show()
donde en dicha reseña la palabra con más apariciones es, claramente, "titanic". Podemos modificar la estética de la nube, por ejemplo, cambiar el color de fondo
# Instanciamos un objeto WordCloud cambiando el color de fondo
word_cloud = WordCloud(background_color='white')
cloud_titanic = word_cloud.generate(titanic_clean)
# Graficamos
plt.imshow(cloud_titanic, interpolation='bilinear')
# Configuramos que los ejes no sean visibles
plt.axis('off')
plt.show()
Asimismo, podemos realizar un preprocesamiento de los datos para la nube de palabras, por ejemplo, queremos omitir las palabras vacías:
# Creamos un set de las palabras vacias del ingles
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
# Instanciamos un objeto WordCloud con fondo blanco y especificando
# que queremos quitar las palabras vacias a la hora de crear la
# nube de palabras
word_cloud = WordCloud(background_color='white', stopwords=stop_words)
cloud_titanic = word_cloud.generate(titanic_clean)
# Graficamos
plt.imshow(cloud_titanic, interpolation='bilinear')
# Configuramos que los ejes no sean visibles
plt.axis('off')
plt.show()