Procesamiento de lenguaje natural¶

Análisis de sentimientos¶



Autor: Luis Fernando Apáez Álvarez



  • Introducción
  • Exploración de los datos
  • Algoritmos para el análisis de sentimientos
    • Métricas
  • Nube de palabras



Introducción ¶

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:

  • Opinión/emoción. Las opiniones también pueden ser llamadas polaridades, la cual puede ser positiva, neutra o negativa. Las emociones pueden ser cualitativas, como alegría, sorpresa, enfado, etcétera, o cuantitativas, como calificar una película del 1 al 10.
  • Tema de discusión: Tema del cual se habla, como un libro, una película, un producto.
  • Titula de la opinión o quién expresa la opinión.

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.

Exploración de los datos ¶

Exploremos el conjunto de datos con el cual estaremos trabajando:

In [1]:
import pandas as pd
data = pd.read_csv('IMDB_sample.csv')[['review', 'label']]
data.head()
Out[1]:
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:

In [3]:
data.value_counts('label')
Out[3]:
label
0    3782
1    3719
dtype: int64

O también visualizamos como porcentajes

In [4]:
(data.value_counts('label') / len(data)) * 100
Out[4]:
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

In [2]:
# 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']
Out[2]:
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
In [3]:
# Obtenemos la longitud maxima de la resegnia
data.long.max()
Out[3]:
10321
In [9]:
# veamos explicitamente cual es dicha resegnia
data[data['long'] == data.long.max()]
Out[9]:
review label long
4930 Titanic directed by James Cameron presents a f... 1 10321

Podemos también hallar la reseña de menor longitud

In [12]:
# 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()
Out[12]:
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
In [13]:
# Extraemos el valor minimo
data.review.str.len().min()
Out[13]:
52
In [14]:
# Obtenemos explicitamente la resegnia de menor longitud
data[data['long'] == data.review.str.len().min()]
Out[14]:
review label long
1628 This movie is terrible but it has some good ef... 0 52

Algoritmos para el análisis de sentimientos ¶

Por otro lado, las tareas de análisis de sentimientos pueden llevarse a cabo mediante diferentes niveles de granulidad:

  1. Nivel del documento: Es cuando se mira la reseña completa de un producto, por ejemplo.
  2. Nivel de la oración: Se refiere a determinar si la opinión expresada en cada oración es positiva, negativa o neutral.
  3. Nivel de aspecto: Se refiere a expresar opiniones sobre diferentes característica de un producto. Por ejemplo, si consideramos "La cámara de este teléfono es muy buena, pero la duración de la batería es descepcionante". Donde se está expresenado opiniones positivas y negativas, lo cual es información relevante sobre el producto para ver qué características les gustan a los clientes y cuáles no.

Los algoritmos utilizados para el análisis de sentimientos se pueden dividir en dos categorías principales:

  • Basados en reglas o léxicos: Estos métodos suelen tener una lista predefinida de palabras con una puntuación de valencia. Por ejemplo, muy bueno podría ser +2, bueno +1, terrible -3, etcétera. el algoritmo luego hace coincidir las palabras del léxico con las palabras del texto y suma o promedia las puntuaciones de alguna manera. Por ejemplo, consideremos "Hoy fue un buen día"; cada palabra obtendrá una puntuación y para ver la valencia total sumamos las valencias de las palabras que conforman la oración.
$$ hoy: 0, \ fue: 0,\ un: 0,\ buen: +1, \ dia: 0 \ \ \Rightarrow \ \ total: +1 $$

Por lo cual tenemos, en este caso, una oración positiva.

  • Basados en Machine Learning: La tarea suele modelarse como un problema de clasificación en el cual se utilizan datos históricos con sentimiento conocido, donde necesitaremos predecir el sentimiento de un nuevo fragmento de texto.

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.

Métricas ¶

Podemos calcular la puntuación de valencia de un texto utilizando la librería textblob:

In [16]:
# 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
Out[16]:
Sentiment(polarity=0.7, subjectivity=0.6000000000000001)

donde:

  • Un objeto TextBlob es como una cadena de texto la cual ha sido adicionada con características mediante el procesamiento del lenguaje natural.
  • La polaridad se mide en una escala del -1 al 1, donde -1 es muy negativo, 0 es neutral y 1 es muy positivo. Vemos entonces que la polaridad de nuestro texto indica que éste es muy cercano a un sentimiento muy bueno.
  • La subjetividad, la cual se mide del 0 al 1, donde 0 es muy objetivo y 1 es muy subjetivo, de modo que el texto con el que trabajamos es muy subjetivo.

Ahora, consideremos la reseña más larga referente al conjunto de datos con el cual estamos trabajando

In [17]:
# veamos explicitamente cual es dicha resegnia
data[data['long'] == data.long.max()]
Out[17]:
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

In [6]:
# Convertimos a cadena de texto la resegnia
titanic = str(data.iloc[4930][0])
# Veamos los primero 1000 caracteres
titanic[:1000]
Out[6]:
'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

In [7]:
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 />'>
In [8]:
# Sustituimos todos los <br /><br /> por espacios en blanco
titanic_clean = pattern.sub(' ', titanic)
titanic_clean[:1000]
Out[8]:
'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

In [39]:
# Instanciamos sobre el texto anterior
blob_titanic = TextBlob(titanic_clean)

# Accedemos a la puntuacion de valencia mediante
# el atributo sentiment
blob_titanic.sentiment
Out[39]:
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

In [40]:
# Polaridad
blob_titanic.sentiment[0]
Out[40]:
0.20791734011246213
In [41]:
# Subjetividad
blob_titanic.sentiment[1]
Out[41]:
0.453315898193947

Nube de palabras ¶

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

In [9]:
# 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

In [12]:
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

In [13]:
# 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:

In [15]:
# 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()