Procesamiento de lenguaje natural¶

Análisis de sentimientos¶



Autor: Luis Fernando Apáez Álvarez



  • Bolsa de Palabras
  • n-gramas
  • Construcción de características adicionales
  • Detección del lenguaje



En esta clase comenzaremos el camino de transformar nuestros datos de texto a una forma numérica, lo cual es necesario dado que un modelo de ML no puede funcionar con datos de texto directamente, sino, más bien con características numéricas las cuales creamos a partir de los datos. Comenzaremos con un método básico y algo robusto.

Bolsa de Palabras ¶

En este método se escribe la ocurrencia o frecuencia de las palabras dentro de un documento, o una colección de documentos (denominado corpus). Básicamente se trata de construir un vocabulario de todos las palabra que aparecen en el documento y realizar un seguimiento de las frecuencias. antes de continuar, veamos el conjunto de datos con el cual trabajaremos

In [8]:
import pandas as pd

data = pd.read_csv('amazon_reviews_sample.csv')[['score', 'review']]
data.head()
Out[8]:
score review
0 1 Stuning even for the non-gamer: This sound tr...
1 1 The best soundtrack ever to anything.: I'm re...
2 1 Amazing!: This soundtrack is my favorite musi...
3 1 Excellent Soundtrack: I truly like this sound...
4 1 Remember, Pull Your Jaw Off The Floor After H...

los cuales son reseñas de productos de Amazon, donde la primer columna tendrá el valor de 1 si la reseña es positiva, o 0 caso contrario. La segunda columna contiene la reseña real del producto. Crearemos entonces una bolsa de palabras, donde la estructura básica de la bolsa es:

palabra: frecuencia de aparición de dicha palabra en el documento.

Cabe resaltar que con dicho enfoque se pierde el orden de las palabras y las reglas gramaticales, por lo cual es que este método se denomina "bolsa".

Procedemos entonces a crear nuestra bolsa de palabras en Python, para lo cual utilizaremos countVectorizer de la librería Sklearn

In [7]:
# Importacion necesaria
from sklearn.feature_extraction.text import CountVectorizer

# Instanciamos un objeto CountVectorizer.
# Ademas, max_features hara que solo se consideren
# las caracteristicas con mayor frecuencia de terminos, esto es
# se seleccionaran las palabras mas frecuentes en el corpus de resegnias
vect = CountVectorizer(max_features = 1000)

# Ajustamos a nuestro texto, el cual es en particular las
# resegnias del dataframe
vect.fit(data.review)

# Para crear la representacion de bolsa de palabras llamamos al metodo
# transform()
X = vect.transform(data.review)

donde el resultado es un matriz dispersa, la cual almacena entidades que no son cero, donde las filas corresponden al número de filas en el conjunto de datos y las columnas al vocabulario generado por la bolsa de palabras.

In [8]:
X
Out[8]:
<10000x1000 sparse matrix of type '<class 'numpy.int64'>'
	with 406668 stored elements in Compressed Sparse Row format>

Para poder observar dicha matriz, debemos convertirla a una array denso de NumPy utilizando el método toarray()

In [10]:
array_matrix = X.toarray()
array_matrix
Out[10]:
array([[0, 0, 0, ..., 0, 1, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=int64)

De manera más visual, podemos construir un dataframe donde los nombres de las columnas son obtenidos del archivo get_feature_names() (éste devuelve una lista donde cada entrada corresponde a una característica) del método vectorizador:

In [14]:
X_df = pd.DataFrame(array_matrix, columns = vect.get_feature_names())
X_df
C:\ProgramData\Miniconda3\lib\site-packages\sklearn\utils\deprecation.py:87: FutureWarning: Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.
  warnings.warn(msg, category=FutureWarning)
Out[14]:
10 100 12 15 1984 20 30 40 451 50 ... wrong wrote year years yes yet you young your yourself
0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 1 0
1 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 1 0 0 1 0 0 0
2 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 1 0 0 2 0 0 0
3 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 ... 0 1 0 0 0 0 3 0 1 0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
9995 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
9996 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
9997 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 1 0 0 0
9998 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
9999 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0

10000 rows × 1000 columns

n-gramas ¶

Recordemos que en la bolsa de palabras el orden de las palabras se pierde. Consideremos por ejemplo las siguiente dos oraciones:

  • Estoy feliz, no triste.
  • Estoy triste, no feliz.

Las cuales tendrán las mismas representaciones referentes a la bolsa de palabras, aunque los significados esten invertidos. Existe una forma de capturar el contexto, considerando, por ejemplo, pares o triples de tokens que aparecen uno al lado del otro.

Los tokens individuales con los cuales hemos estado trabajando se denomina unigramas; los bigramas son pares de tokens; los trigramas son triples de tokens y, finalmente, una secuencia de $n-$tokens se denomina $n-$gramas.

Por ejemplo, consideremos la siguiente frase: "El clima hoy es maravilloso" y dividámoslo como sigue.

  • Unigramas: El, clima, hoy, es, maravilloso
  • Bigramas: El clima, clima hoy, hoy es, es maravilloso
  • Trigranas: El clima hoy, clima hoy es, hoy es maravilloso

Podemos implementar $n-$gramas utilizando countVectorizer específicando el parámetro n_gram_range=(nmin, nmax):

vect = CountVectorizer(ngram_range=(nmin, nmax))

donde nmin es la longitud mínima y el segundo la longitud máxima de tokens. Por ejemplo, ngram_range(1,1) indicará que solo utilizaremos unigramas, ngram_range(1,2) indaca que usaremos unigramas y bigramas, y así sucesivamente. No es difícil determinar cuál es la secuencia óptimaque debe de utilizarse para el problema en cuestión. Si utilizamos una secuencia de tokens más larga, tendremos entonces como resultado más características o funciones. Por ejemplo, el número de bigramas podría ser, en principio, el número de unigramas al cuadrado; El número de trigramas podría llegar a ser el número de unigramas elevado al cubo, y así sucesivamente. En general, tener secuencias más largas resulta en modelos de ML más precisos, pero esto también aumenta el riesgo de sobre ajuste.

Un enfoque para hallar la longitud de secuencia óptima sería probar diferentes longitudes y ver qué resultados nos arrojan el mejor modelo.

Por otro lado, veamos la misma construcción del dataframe X_df pero ahora considerando también bigramas

In [17]:
# Considereremos unigramas y bigramas
vect = CountVectorizer(max_features = 1000, ngram_range=(1, 2))

# Ajustamos a nuestro texto, el cual es en particular las
# resegnias del dataframe
vect.fit(data.review)

# Para crear la representacion de bolsa de palabras llamamos al metodo
# transform()
X = vect.transform(data.review)

# Creamos un dataframe
X_df = pd.DataFrame(X.toarray(), columns = vect.get_feature_names())
X_df
C:\ProgramData\Miniconda3\lib\site-packages\sklearn\utils\deprecation.py:87: FutureWarning: Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.
  warnings.warn(msg, category=FutureWarning)
Out[17]:
10 1984 20 30 able able to about about it about the about this ... you have you like you ll you re you want you will young your your money yourself
0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 1 0 0
1 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 1 0 0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
9995 0 0 0 0 0 0 1 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
9996 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
9997 0 0 0 0 0 0 2 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
9998 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
9999 0 0 0 0 0 0 1 1 0 0 ... 0 0 0 0 0 0 0 0 0 0

10000 rows × 1000 columns

donde podemos notar tanto unigramas como bigramas.

Considerando ahora CountVectorizer() podemos pasarle, por ejemplo, otros dos parámetros:

  • max_df: le dice a CountVectorizer que ignore los términos con una frecuencia mayor a la dada. Donde podemos especificarlo con un número entero (recuento absoluto) o con un número flotante (proporción). El número 1 significa que ningún término será ignorado. Otro ejemplo, si escribimos max_df=200 significa que limitaremos el vocabulario a aquellos términos que aparezcan en no más de 200 documentos.
  • min_df: se utiliza para eliminar términos que aparecen con poca frecuencia y los especificamos de la misma manera que en max_df. El 1 significa que serán ignorados los términos que aparecen en menos de un documento.

Construcción de características adicionales ¶

Cuando trabajamos en un modelo de ML para el análisis de sentimientos, tener características adicionales generalmente resulta en un mejor modelo.

Lo que haremos será proveer de característica adicionales relacionadas con el texto que captura el sentimiento. Antes de ello tokenizaremos cada reseña que tenemos en el dataframe original.

In [19]:
# Veamos de nuevo el dataframe
data
Out[19]:
score review
0 1 Stuning even for the non-gamer: This sound tr...
1 1 The best soundtrack ever to anything.: I'm re...
2 1 Amazing!: This soundtrack is my favorite musi...
3 1 Excellent Soundtrack: I truly like this sound...
4 1 Remember, Pull Your Jaw Off The Floor After H...
... ... ...
9995 1 A revelation of life in small town America in...
9996 1 Great biography of a very interesting journal...
9997 0 Interesting Subject; Poor Presentation: You'd...
9998 0 Don't buy: The box looked used and it is obvi...
9999 1 Beautiful Pen and Fast Delivery.: The pen was...

10000 rows × 2 columns

In [20]:
# Obversemos una resegnia en particular
data.review[0]
Out[20]:
' Stuning even for the non-gamer: This sound track was beautiful! It paints the senery in your mind so well I would recomend it even to people who hate vid. game music! I have played the game Chrono Cross but out of all of the games I have ever played it has the best music! It backs away from crude keyboarding and takes a fresher step with grate guitars and soulful orchestras. It would impress anyone who cares to listen! ^_^\r\n'
In [34]:
# Realizaremos una pequenia limpieza considerando solo los caracteres
# alfanumericos
import re

pattern = re.compile(r'[^\w+]')

# Todos los caracteres que no son lafanumericos los sustituimos por 
# espacios en blanco
match = pattern.sub(' ', data.review[0])

match
Out[34]:
' Stuning even for the non gamer  This sound track was beautiful  It paints the senery in your mind so well I would recomend it even to people who hate vid  game music  I have played the game Chrono Cross but out of all of the games I have ever played it has the best music  It backs away from crude keyboarding and takes a fresher step with grate guitars and soulful orchestras  It would impress anyone who cares to listen   _   '

Después, tokenizamos mediante word_tokenize de la librería nltk

In [37]:
from nltk import word_tokenize

match_tokens = word_tokenize(match) 
match_tokens 
Out[37]:
['Stuning',
 'even',
 'for',
 'the',
 'non',
 'gamer',
 'This',
 'sound',
 'track',
 'was',
 'beautiful',
 'It',
 'paints',
 'the',
 'senery',
 'in',
 'your',
 'mind',
 'so',
 'well',
 'I',
 'would',
 'recomend',
 'it',
 'even',
 'to',
 'people',
 'who',
 'hate',
 'vid',
 'game',
 'music',
 'I',
 'have',
 'played',
 'the',
 'game',
 'Chrono',
 'Cross',
 'but',
 'out',
 'of',
 'all',
 'of',
 'the',
 'games',
 'I',
 'have',
 'ever',
 'played',
 'it',
 'has',
 'the',
 'best',
 'music',
 'It',
 'backs',
 'away',
 'from',
 'crude',
 'keyboarding',
 'and',
 'takes',
 'a',
 'fresher',
 'step',
 'with',
 'grate',
 'guitars',
 'and',
 'soulful',
 'orchestras',
 'It',
 'would',
 'impress',
 'anyone',
 'who',
 'cares',
 'to',
 'listen',
 '_']

Aplicamos todo lo anterior pero para cada reseña, agregando para ello una nueva columna al dataframe

In [46]:
pattern = re.compile(r'[^\w+]')

# Mediante una funcion lambda y apply, limpiamos cada resegnia
data['tokens'] = data.review.apply(lambda x: pattern.sub(' ', x))

# Mediante una funcion lambda y apply, tokenizamos cada resegnia limpia
data['tokens'] = data.tokens.apply(lambda x: word_tokenize(x))

data
Out[46]:
score review tokens
0 1 Stuning even for the non-gamer: This sound tr... [Stuning, even, for, the, non, gamer, This, so...
1 1 The best soundtrack ever to anything.: I'm re... [The, best, soundtrack, ever, to, anything, I,...
2 1 Amazing!: This soundtrack is my favorite musi... [Amazing, This, soundtrack, is, my, favorite, ...
3 1 Excellent Soundtrack: I truly like this sound... [Excellent, Soundtrack, I, truly, like, this, ...
4 1 Remember, Pull Your Jaw Off The Floor After H... [Remember, Pull, Your, Jaw, Off, The, Floor, A...
... ... ... ...
9995 1 A revelation of life in small town America in... [A, revelation, of, life, in, small, town, Ame...
9996 1 Great biography of a very interesting journal... [Great, biography, of, a, very, interesting, j...
9997 0 Interesting Subject; Poor Presentation: You'd... [Interesting, Subject, Poor, Presentation, You...
9998 0 Don't buy: The box looked used and it is obvi... [Don, t, buy, The, box, looked, used, and, it,...
9999 1 Beautiful Pen and Fast Delivery.: The pen was... [Beautiful, Pen, and, Fast, Delivery, The, pen...

10000 rows × 3 columns

In [47]:
data.tokens[0]
Out[47]:
['Stuning',
 'even',
 'for',
 'the',
 'non',
 'gamer',
 'This',
 'sound',
 'track',
 'was',
 'beautiful',
 'It',
 'paints',
 'the',
 'senery',
 'in',
 'your',
 'mind',
 'so',
 'well',
 'I',
 'would',
 'recomend',
 'it',
 'even',
 'to',
 'people',
 'who',
 'hate',
 'vid',
 'game',
 'music',
 'I',
 'have',
 'played',
 'the',
 'game',
 'Chrono',
 'Cross',
 'but',
 'out',
 'of',
 'all',
 'of',
 'the',
 'games',
 'I',
 'have',
 'ever',
 'played',
 'it',
 'has',
 'the',
 'best',
 'music',
 'It',
 'backs',
 'away',
 'from',
 'crude',
 'keyboarding',
 'and',
 'takes',
 'a',
 'fresher',
 'step',
 'with',
 'grate',
 'guitars',
 'and',
 'soulful',
 'orchestras',
 'It',
 'would',
 'impress',
 'anyone',
 'who',
 'cares',
 'to',
 'listen',
 '_']

Luego, contaremos cuántos tokens hay en cada lista tokenizada

In [48]:
data['long'] = data.tokens.apply(lambda x: len(x))
data
Out[48]:
score review tokens long
0 1 Stuning even for the non-gamer: This sound tr... [Stuning, even, for, the, non, gamer, This, so... 81
1 1 The best soundtrack ever to anything.: I'm re... [The, best, soundtrack, ever, to, anything, I,... 102
2 1 Amazing!: This soundtrack is my favorite musi... [Amazing, This, soundtrack, is, my, favorite, ... 136
3 1 Excellent Soundtrack: I truly like this sound... [Excellent, Soundtrack, I, truly, like, this, ... 122
4 1 Remember, Pull Your Jaw Off The Floor After H... [Remember, Pull, Your, Jaw, Off, The, Floor, A... 90
... ... ... ... ...
9995 1 A revelation of life in small town America in... [A, revelation, of, life, in, small, town, Ame... 157
9996 1 Great biography of a very interesting journal... [Great, biography, of, a, very, interesting, j... 143
9997 0 Interesting Subject; Poor Presentation: You'd... [Interesting, Subject, Poor, Presentation, You... 112
9998 0 Don't buy: The box looked used and it is obvi... [Don, t, buy, The, box, looked, used, and, it,... 29
9999 1 Beautiful Pen and Fast Delivery.: The pen was... [Beautiful, Pen, and, Fast, Delivery, The, pen... 105

10000 rows × 4 columns

Tenemos entonces una característica adicional que podría ser útil para determinado contexto. Otras características adicionales que podríamos incluir son:

  • Crear una función para contar los signos de puntuación, lo que podría indicar que más signos de puntuación traen opiniones más cargadas de emociones.
  • Crear una función para contar el número de oraciones, en vez de contar solo cada palabra como hicimos antes.

Entre otras más, dependiendo de los objetivos y los enfoque de nuestro problema.

Detección del lenguaje ¶

Es posible que deseemos detectar qué idioma se está usando o crear características específicas relacionadas con un idioma ya dado.

Dentro de Python existen diversas librerías que nos permiten detectar el idioma de una cadena de texto. En particular utilizaremos langdetect dado que es uno de los mejores respecto al rendimiento. Por ejemplo

In [11]:
# Importacion necesaria.
# La funcion detect_langs nos ayudara a detectar el idioma
# de una cadena de texto
from langdetect import detect_langs

# Cadena en idioma espagnol
cad = 'Hola mundo, estoy escribiendo en español.'

# Aplicamos la funcion detect_langs a la cadena anterior
detect_langs(cad)
Out[11]:
[es:0.999996656110993]

que nos devuelve una lista, donde se específica primero el idioma detectado de la cadena de texto, seguido de una probabilidad, esto es, la cadena cad almacena una cadena de texto cuya probabilidad de estar escrita en el idioma español es de 0.999995589.

Otro ejemplo

In [3]:
# Cadena en idioma ingles
cad = 'Hello world, I am writing in Spanish.'

# Aplicamos la funcion detect_langs a la cadena anterior
detect_langs(cad)
Out[3]:
[en:0.9999958712167164]

donde la probabilidad de que la cadena de texto este escrito en inglés (english) es de 0.9999957. en aplicaciones reales es común trabajar con varias cadenas de texto, en vez de una, como hemos trabajado antes sobre las reseñas de productos de Amazon. Un problema común es detectar el idioma de cada de las cadenas y capturar el idioma más probable en una nueva columna. Lo anterior será sencillo de resolver, aunque será un poco tardado de ejecutarse (<2min)

In [10]:
# Creamos una nueva columna para colocar el probable idioma
# detectado en cada una de las resegnias
data['lang'] = data.review.apply(lambda x: detect_langs(x))
data
Out[10]:
score review lang
0 1 Stuning even for the non-gamer: This sound tr... [en:0.9999970045393871]
1 1 The best soundtrack ever to anything.: I'm re... [en:0.9999969331701792]
2 1 Amazing!: This soundtrack is my favorite musi... [en:0.999996523966582]
3 1 Excellent Soundtrack: I truly like this sound... [en:0.9999977616627801]
4 1 Remember, Pull Your Jaw Off The Floor After H... [en:0.9999966030055435]
... ... ... ...
9995 1 A revelation of life in small town America in... [en:0.9999975756246933]
9996 1 Great biography of a very interesting journal... [en:0.9999950445580532]
9997 0 Interesting Subject; Poor Presentation: You'd... [en:0.999998331084728]
9998 0 Don't buy: The box looked used and it is obvi... [en:0.999998492368484]
9999 1 Beautiful Pen and Fast Delivery.: The pen was... [en:0.9999975843869945]

10000 rows × 3 columns

Podemos crear una columna más específica colocando sólo el idioma y omitiendo la probabilidad:

In [23]:
# Accedemos a cada elemento de la columna lang, luego accedemos al 
# primer elemento de la lista resultante de deteccion del idioma 
# (el primer elemento es el idioma mas probable detectado).
# Despues convertivos a cadena de texto cada elemento de dicha columna
# y accedemos a los dos primeros caracteres
data['lang_exp'] = data.lang.apply(lambda x: str(x[0])[:2])
data
Out[23]:
score review lang lang_exp
0 1 Stuning even for the non-gamer: This sound tr... [en:0.9999970045393871] en
1 1 The best soundtrack ever to anything.: I'm re... [en:0.9999969331701792] en
2 1 Amazing!: This soundtrack is my favorite musi... [en:0.999996523966582] en
3 1 Excellent Soundtrack: I truly like this sound... [en:0.9999977616627801] en
4 1 Remember, Pull Your Jaw Off The Floor After H... [en:0.9999966030055435] en
... ... ... ... ...
9995 1 A revelation of life in small town America in... [en:0.9999975756246933] en
9996 1 Great biography of a very interesting journal... [en:0.9999950445580532] en
9997 0 Interesting Subject; Poor Presentation: You'd... [en:0.999998331084728] en
9998 0 Don't buy: The box looked used and it is obvi... [en:0.999998492368484] en
9999 1 Beautiful Pen and Fast Delivery.: The pen was... [en:0.9999975843869945] en

10000 rows × 4 columns

Veamos, por ejemplo, si tenemos reseñas en italiano

In [24]:
# Tendremos una resegnia en italiano
data[data.lang_exp == 'it']
Out[24]:
score review lang lang_exp
1249 1 Il grande ritorno!: E' dai tempi del tour di ... [it:0.9999962595090164] it
In [25]:
# Tendremos 8 resegnias en frances
data[data.lang_exp == 'fr']
Out[25]:
score review lang lang_exp
2316 1 Nightwish is unique and rocks for eva: Moi to... [fr:0.9999970676897462] fr
2760 0 Completement nul: Fait sur commande et ennuya... [fr:0.9999972821811058] fr
4914 0 De la poudre aux yeux: J'ai acheté un Sansa V... [fr:0.9999964131374507] fr
5720 1 C'est magnifique! il y a du vrai dans ce qui'... [fr:0.9999973622432383] fr
5875 1 Erreur: "Les Triplettes de Belleville" n'a pa... [fr:0.9999954875413937] fr
6631 0 certains bugs viennent tout gacher: le jeu es... [fr:0.9999986981092219] fr
7983 1 1F4T: Cet album est chanté vraiment bien. Jea... [fr:0.9999971781286289] fr
8340 1 Jean de Florette et Manon des sources: bien a... [fr:0.9999981019283888] fr
In [26]:
# Tendremos 14 resegnias en espagnol
data[data.lang_exp == 'es']
Out[26]:
score review lang lang_exp
1259 1 La reencarnación vista por un científico: El ... [es:0.9999953923802931] es
1261 1 Magnifico libro: Brian Weiss ha dejado una ma... [es:0.9999942254581136] es
1745 1 Excelente!: Una excelente guía para todos aqu... [es:0.9999977246887979] es
2486 1 Palabras de aliento para tu caminar con Dios:... [es:0.9999948990649543] es
2903 1 fabuloso: mil gracias por el producto fabulos... [es:0.9999968848809118] es
3318 1 Excelentes botas.. excelentes boots: Excelent... [es:0.9999957667748436] es
3694 0 Why not Spanish ???: Alguien me puede decir p... [es:0.9999956602589468] es
4820 1 La mejor película de Moore: A mi juicio, esta... [es:0.9999950238615187] es
5901 1 Buen cargador: Product very good, I am of Ven... [es:0.8571387835958204, en:0.1428601546048478] es
6234 1 5+ stars. LO MEJOR DE LO QUE HE LEIDO EN MI V... [es:0.9999942069437358] es
7078 1 Variedad: Bueno tener este album debido a su ... [es:0.9999959479067406] es
8018 1 Exelente eleccion: Los mejores zapatos de fut... [es:0.9999961554846727] es
9265 1 Excelente: Manu es una de los mejores cantant... [es:0.9999956344575924] es
9624 0 baaaaaadddddddd bookkkkkkk: por favor no gast... [es:0.9999957024195795] es

vemos, por ejemplo, que en la fila 5901 la lista de detección de idioma ha detectado dos posibles idiomas: el español y el inglés. Donde el idioma más probable es el español. Al parecer la reseña en la fila 6234 se mira interesante jeje:

In [27]:
data.review.iloc[6234]
Out[27]:
' 5+ stars. LO MEJOR DE LO QUE HE LEIDO EN MI VIDA.: Un resúmen:El clan del oso cavernario: EXCELENTE, INSPIRADOR.Un libro para leer y releer, es como un libro de superación personal, pero sin los aburridos consejos sabiondos de los autores, ni las falsas promesas de los nuevos autores llamados "new age". DEBE LEERLO, Y POR FAVOR, NO LO PRESTE.....!El valle de los caballos: BUENA SECUELA, RECOMENDABLE.Los cazadores del mamut: SOLO PARA FANS.El libro de los viajes ("Plains"): REPETITIVO, LENTO Y ABURRIDO EN DOS TERCERAS PARTES (LA\'ÚLTIMA PARTE, COMO EN "cazadores", PARA FANS).....LA ESPERANZA...."The Shelter of stone"...donde los que admiramos a Ayla, esperamos reencontrarnos con ella...\r\n'

Realicemos un rápido análisis de sentimiento mediante la puntuación de valencia

In [28]:
# Importacion necesaria
from textblob import TextBlob

# Instancias sobre el texto de la resegnia
valence = TextBlob(data.review.iloc[6234])

# Accedemos a la puntuacion de valencia mediante
# el atributo sentiment
valence.sentiment
Out[28]:
Sentiment(polarity=0.06818181818181818, subjectivity=0.37727272727272726)

Tenemos que dicha reseña tiene un sentimiento neural, además ésta es objetiva.