You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ML-For-Beginners/translations/es/6-NLP/5-Hotel-Reviews-2
leestott e4050807fb
🌐 Update translations via Co-op Translator
3 weeks ago
..
solution 🌐 Update translations via Co-op Translator 3 weeks ago
README.md 🌐 Update translations via Co-op Translator 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 3 weeks ago
notebook.ipynb 🌐 Update translations via Co-op Translator 3 weeks ago

README.md

Análisis de sentimientos con reseñas de hoteles

Ahora que has explorado el conjunto de datos en detalle, es momento de filtrar las columnas y luego usar técnicas de procesamiento de lenguaje natural (NLP) en el conjunto de datos para obtener nuevas perspectivas sobre los hoteles.

Cuestionario previo a la clase

Operaciones de filtrado y análisis de sentimientos

Como probablemente hayas notado, el conjunto de datos tiene algunos problemas. Algunas columnas están llenas de información inútil, otras parecen incorrectas. Si son correctas, no está claro cómo se calcularon, y las respuestas no pueden ser verificadas de manera independiente con tus propios cálculos.

Ejercicio: un poco más de procesamiento de datos

Limpia los datos un poco más. Agrega columnas que serán útiles más adelante, cambia los valores en otras columnas y elimina ciertas columnas por completo.

  1. Procesamiento inicial de columnas

    1. Elimina lat y lng.

    2. Reemplaza los valores de Hotel_Address con los siguientes valores (si la dirección contiene el nombre de la ciudad y el país, cámbialo por solo la ciudad y el país).

      Estas son las únicas ciudades y países en el conjunto de datos:

      Ámsterdam, Países Bajos

      Barcelona, España

      Londres, Reino Unido

      Milán, Italia

      París, Francia

      Viena, Austria

      def replace_address(row):
          if "Netherlands" in row["Hotel_Address"]:
              return "Amsterdam, Netherlands"
          elif "Barcelona" in row["Hotel_Address"]:
              return "Barcelona, Spain"
          elif "United Kingdom" in row["Hotel_Address"]:
              return "London, United Kingdom"
          elif "Milan" in row["Hotel_Address"]:        
              return "Milan, Italy"
          elif "France" in row["Hotel_Address"]:
              return "Paris, France"
          elif "Vienna" in row["Hotel_Address"]:
              return "Vienna, Austria" 
      
      # Replace all the addresses with a shortened, more useful form
      df["Hotel_Address"] = df.apply(replace_address, axis = 1)
      # The sum of the value_counts() should add up to the total number of reviews
      print(df["Hotel_Address"].value_counts())
      

      Ahora puedes consultar datos a nivel de país:

      display(df.groupby("Hotel_Address").agg({"Hotel_Name": "nunique"}))
      
      Hotel_Address Hotel_Name
      Ámsterdam, Países Bajos 105
      Barcelona, España 211
      Londres, Reino Unido 400
      Milán, Italia 162
      París, Francia 458
      Viena, Austria 158
  2. Procesar columnas de meta-reseñas de hoteles

    1. Elimina Additional_Number_of_Scoring.

    2. Reemplaza Total_Number_of_Reviews con el número total de reseñas para ese hotel que realmente están en el conjunto de datos.

    3. Reemplaza Average_Score con nuestro propio puntaje calculado.

Drop Additional_Number_of_Scoring

df.drop(["Additional_Number_of_Scoring"], axis = 1, inplace=True)

Replace Total_Number_of_Reviews and Average_Score with our own calculated values

df.Total_Number_of_Reviews = df.groupby('Hotel_Name').transform('count') df.Average_Score = round(df.groupby('Hotel_Name').Reviewer_Score.transform('mean'), 1)


3. Procesar columnas de reseñas

 1. Elimina `Review_Total_Negative_Word_Counts`, `Review_Total_Positive_Word_Counts`, `Review_Date` y `days_since_review`.

 2. Conserva `Reviewer_Score`, `Negative_Review` y `Positive_Review` tal como están.

 3. Conserva `Tags` por ahora.

    - Realizaremos algunas operaciones de filtrado adicionales en las etiquetas en la siguiente sección y luego las eliminaremos.

4. Procesar columnas de los revisores

 1. Elimina `Total_Number_of_Reviews_Reviewer_Has_Given`.

 2. Conserva `Reviewer_Nationality`.

### Columnas de etiquetas

La columna `Tag` es problemática ya que es una lista (en forma de texto) almacenada en la columna. Desafortunadamente, el orden y el número de subsecciones en esta columna no siempre son los mismos. Es difícil para un humano identificar las frases correctas de interés, porque hay 515,000 filas y 1427 hoteles, y cada uno tiene opciones ligeramente diferentes que un revisor podría elegir. Aquí es donde el NLP brilla. Puedes escanear el texto y encontrar las frases más comunes, y contarlas.

Desafortunadamente, no estamos interesados en palabras individuales, sino en frases de varias palabras (por ejemplo, *Viaje de negocios*). Ejecutar un algoritmo de distribución de frecuencia de frases en tantos datos (6762646 palabras) podría tomar un tiempo extraordinario, pero sin mirar los datos, parecería que es un gasto necesario. Aquí es donde el análisis exploratorio de datos resulta útil, porque al haber visto una muestra de las etiquetas como `[' Viaje de negocios  ', ' Viajero solo ', ' Habitación individual ', ' Estancia de 5 noches ', ' Enviado desde un dispositivo móvil ']`, puedes comenzar a preguntarte si es posible reducir significativamente el procesamiento que tienes que hacer. Afortunadamente, lo es, pero primero necesitas seguir algunos pasos para determinar las etiquetas de interés.

### Filtrar etiquetas

Recuerda que el objetivo del conjunto de datos es agregar sentimientos y columnas que te ayuden a elegir el mejor hotel (para ti o tal vez para un cliente que te encargue crear un bot de recomendación de hoteles). Debes preguntarte si las etiquetas son útiles o no en el conjunto de datos final. Aquí hay una interpretación (si necesitaras el conjunto de datos por otras razones, diferentes etiquetas podrían permanecer o salir de la selección):

1. El tipo de viaje es relevante y debería permanecer.
2. El tipo de grupo de huéspedes es importante y debería permanecer.
3. El tipo de habitación, suite o estudio en el que se hospedó el huésped es irrelevante (todos los hoteles tienen básicamente las mismas habitaciones).
4. El dispositivo desde el cual se envió la reseña es irrelevante.
5. El número de noches que el revisor se hospedó *podría* ser relevante si atribuyes estancias más largas con que les gustó más el hotel, pero es poco probable y probablemente irrelevante.

En resumen, **conserva 2 tipos de etiquetas y elimina las demás**.

Primero, no quieres contar las etiquetas hasta que estén en un formato mejor, lo que significa eliminar los corchetes y las comillas. Puedes hacer esto de varias maneras, pero quieres la más rápida ya que podría tomar mucho tiempo procesar muchos datos. Afortunadamente, pandas tiene una forma fácil de realizar cada uno de estos pasos.

```Python
# Remove opening and closing brackets
df.Tags = df.Tags.str.strip("[']")
# remove all quotes too
df.Tags = df.Tags.str.replace(" ', '", ",", regex = False)

Cada etiqueta se convierte en algo como: Viaje de negocios, Viajero solo, Habitación individual, Estancia de 5 noches, Enviado desde un dispositivo móvil.

A continuación, encontramos un problema. Algunas reseñas, o filas, tienen 5 columnas, otras 3, otras 6. Esto es resultado de cómo se creó el conjunto de datos y es difícil de corregir. Quieres obtener un conteo de frecuencia de cada frase, pero están en diferentes órdenes en cada reseña, por lo que el conteo podría estar incorrecto y un hotel podría no recibir una etiqueta que merecía.

En cambio, usarás el orden diferente a tu favor, porque cada etiqueta es de varias palabras pero también está separada por una coma. La forma más sencilla de hacer esto es crear 6 columnas temporales con cada etiqueta insertada en la columna correspondiente a su orden en la etiqueta. Luego puedes fusionar las 6 columnas en una gran columna y ejecutar el método value_counts() en la columna resultante. Al imprimir eso, verás que había 2428 etiquetas únicas. Aquí hay una pequeña muestra:

Tag Count
Viaje de ocio 417778
Enviado desde un dispositivo móvil 307640
Pareja 252294
Estancia de 1 noche 193645
Estancia de 2 noches 133937
Viajero solo 108545
Estancia de 3 noches 95821
Viaje de negocios 82939
Grupo 65392
Familia con niños pequeños 61015
Estancia de 4 noches 47817
Habitación doble 35207
Habitación doble estándar 32248
Habitación doble superior 31393
Familia con niños mayores 26349
Habitación doble deluxe 24823
Habitación doble o twin 22393
Estancia de 5 noches 20845
Habitación doble estándar o twin 17483
Habitación doble clásica 16989
Habitación doble superior o twin 13570
2 habitaciones 12393

Algunas de las etiquetas comunes como Enviado desde un dispositivo móvil no nos son útiles, por lo que podría ser inteligente eliminarlas antes de contar la ocurrencia de frases, pero es una operación tan rápida que puedes dejarlas y simplemente ignorarlas.

Eliminar las etiquetas de duración de la estancia

Eliminar estas etiquetas es el paso 1, reduce ligeramente el número total de etiquetas a considerar. Nota que no las eliminas del conjunto de datos, solo eliges eliminarlas de la consideración como valores para contar/conservar en el conjunto de reseñas.

Duración de la estancia Count
Estancia de 1 noche 193645
Estancia de 2 noches 133937
Estancia de 3 noches 95821
Estancia de 4 noches 47817
Estancia de 5 noches 20845
Estancia de 6 noches 9776
Estancia de 7 noches 7399
Estancia de 8 noches 2502
Estancia de 9 noches 1293
... ...

Hay una gran variedad de habitaciones, suites, estudios, apartamentos y demás. Todos significan aproximadamente lo mismo y no son relevantes para ti, así que elimínalos de la consideración.

Tipo de habitación Count
Habitación doble 35207
Habitación doble estándar 32248
Habitación doble superior 31393
Habitación doble deluxe 24823
Habitación doble o twin 22393
Habitación doble estándar o twin 17483
Habitación doble clásica 16989
Habitación doble superior o twin 13570

Finalmente, y esto es encantador (porque no tomó mucho procesamiento en absoluto), te quedarás con las siguientes etiquetas útiles:

Tag Count
Viaje de ocio 417778
Pareja 252294
Viajero solo 108545
Viaje de negocios 82939
Grupo (combinado con Viajeros con amigos) 67535
Familia con niños pequeños 61015
Familia con niños mayores 26349
Con una mascota 1405

Podrías argumentar que Viajeros con amigos es lo mismo que Grupo más o menos, y sería justo combinarlos como se muestra arriba. El código para identificar las etiquetas correctas está en el cuaderno de etiquetas.

El paso final es crear nuevas columnas para cada una de estas etiquetas. Luego, para cada fila de reseña, si la columna Tag coincide con una de las nuevas columnas, agrega un 1, si no, agrega un 0. El resultado final será un conteo de cuántos revisores eligieron este hotel (en conjunto) para, por ejemplo, negocios vs ocio, o para llevar una mascota, y esta es información útil al recomendar un hotel.

# Process the Tags into new columns
# The file Hotel_Reviews_Tags.py, identifies the most important tags
# Leisure trip, Couple, Solo traveler, Business trip, Group combined with Travelers with friends, 
# Family with young children, Family with older children, With a pet
df["Leisure_trip"] = df.Tags.apply(lambda tag: 1 if "Leisure trip" in tag else 0)
df["Couple"] = df.Tags.apply(lambda tag: 1 if "Couple" in tag else 0)
df["Solo_traveler"] = df.Tags.apply(lambda tag: 1 if "Solo traveler" in tag else 0)
df["Business_trip"] = df.Tags.apply(lambda tag: 1 if "Business trip" in tag else 0)
df["Group"] = df.Tags.apply(lambda tag: 1 if "Group" in tag or "Travelers with friends" in tag else 0)
df["Family_with_young_children"] = df.Tags.apply(lambda tag: 1 if "Family with young children" in tag else 0)
df["Family_with_older_children"] = df.Tags.apply(lambda tag: 1 if "Family with older children" in tag else 0)
df["With_a_pet"] = df.Tags.apply(lambda tag: 1 if "With a pet" in tag else 0)

Guarda tu archivo

Finalmente, guarda el conjunto de datos tal como está ahora con un nuevo nombre.

df.drop(["Review_Total_Negative_Word_Counts", "Review_Total_Positive_Word_Counts", "days_since_review", "Total_Number_of_Reviews_Reviewer_Has_Given"], axis = 1, inplace=True)

# Saving new data file with calculated columns
print("Saving results to Hotel_Reviews_Filtered.csv")
df.to_csv(r'../data/Hotel_Reviews_Filtered.csv', index = False)

Operaciones de análisis de sentimientos

En esta sección final, aplicarás análisis de sentimientos a las columnas de reseñas y guardarás los resultados en un conjunto de datos.

Ejercicio: cargar y guardar los datos filtrados

Nota que ahora estás cargando el conjunto de datos filtrado que se guardó en la sección anterior, no el conjunto de datos original.

import time
import pandas as pd
import nltk as nltk
from nltk.corpus import stopwords
from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download('vader_lexicon')

# Load the filtered hotel reviews from CSV
df = pd.read_csv('../../data/Hotel_Reviews_Filtered.csv')

# You code will be added here


# Finally remember to save the hotel reviews with new NLP data added
print("Saving results to Hotel_Reviews_NLP.csv")
df.to_csv(r'../data/Hotel_Reviews_NLP.csv', index = False)

Eliminar palabras vacías

Si ejecutaras análisis de sentimientos en las columnas de reseñas negativas y positivas, podría tomar mucho tiempo. Probado en una laptop de prueba potente con CPU rápida, tomó entre 12 y 14 minutos dependiendo de la biblioteca de análisis de sentimientos utilizada. Ese es un tiempo (relativamente) largo, por lo que vale la pena investigar si se puede acelerar.

Eliminar palabras vacías, o palabras comunes en inglés que no cambian el sentimiento de una oración, es el primer paso. Al eliminarlas, el análisis de sentimientos debería ejecutarse más rápido, pero no ser menos preciso (ya que las palabras vacías no afectan el sentimiento, pero sí ralentizan el análisis).

La reseña negativa más larga tenía 395 palabras, pero después de eliminar las palabras vacías, tiene 195 palabras.

Eliminar las palabras vacías también es una operación rápida, eliminarlas de 2 columnas de reseñas en más de 515,000 filas tomó 3.3 segundos en el dispositivo de prueba. Podría tomar un poco más o menos tiempo para ti dependiendo de la velocidad de tu CPU, RAM, si tienes un SSD o no, y algunos otros factores. La relativa brevedad de la operación significa que si mejora el tiempo de análisis de sentimientos, entonces vale la pena hacerlo.

from nltk.corpus import stopwords

# Load the hotel reviews from CSV
df = pd.read_csv("../../data/Hotel_Reviews_Filtered.csv")

# Remove stop words - can be slow for a lot of text!
# Ryan Han (ryanxjhan on Kaggle) has a great post measuring performance of different stop words removal approaches
# https://www.kaggle.com/ryanxjhan/fast-stop-words-removal # using the approach that Ryan recommends
start = time.time()
cache = set(stopwords.words("english"))
def remove_stopwords(review):
    text = " ".join([word for word in review.split() if word not in cache])
    return text

# Remove the stop words from both columns
df.Negative_Review = df.Negative_Review.apply(remove_stopwords)   
df.Positive_Review = df.Positive_Review.apply(remove_stopwords)

Realizar análisis de sentimientos

Ahora deberías calcular el análisis de sentimientos para las columnas de reseñas negativas y positivas, y almacenar el resultado en 2 nuevas columnas. La prueba del sentimiento será compararlo con la puntuación del revisor para la misma reseña. Por ejemplo, si el análisis de sentimientos piensa que la reseña negativa tenía un sentimiento de 1 (sentimiento extremadamente positivo) y un sentimiento de reseña positiva de 1, pero el revisor dio al hotel la puntuación más baja posible, entonces o el texto de la reseña no coincide con la puntuación, o el analizador de sentimientos no pudo reconocer correctamente el sentimiento. Deberías esperar que algunas puntuaciones de sentimiento sean completamente incorrectas, y a menudo eso será explicable, por ejemplo, la reseña podría ser extremadamente sarcástica: "Por supuesto que AMÉ dormir en una habitación sin calefacción" y el analizador de sentimientos piensa que eso es un sentimiento positivo, aunque un humano que lo lea sabría que es sarcasmo. NLTK ofrece diferentes analizadores de sentimientos para aprender, y puedes sustituirlos y ver si el análisis de sentimientos es más o menos preciso. Aquí se utiliza el análisis de sentimientos VADER.

Hutto, C.J. & Gilbert, E.E. (2014). VADER: Un modelo parsimonioso basado en reglas para el análisis de sentimientos de textos en redes sociales. Octava Conferencia Internacional sobre Blogs y Redes Sociales (ICWSM-14). Ann Arbor, MI, junio de 2014.

from nltk.sentiment.vader import SentimentIntensityAnalyzer

# Create the vader sentiment analyser (there are others in NLTK you can try too)
vader_sentiment = SentimentIntensityAnalyzer()
# Hutto, C.J. & Gilbert, E.E. (2014). VADER: A Parsimonious Rule-based Model for Sentiment Analysis of Social Media Text. Eighth International Conference on Weblogs and Social Media (ICWSM-14). Ann Arbor, MI, June 2014.

# There are 3 possibilities of input for a review:
# It could be "No Negative", in which case, return 0
# It could be "No Positive", in which case, return 0
# It could be a review, in which case calculate the sentiment
def calc_sentiment(review):    
    if review == "No Negative" or review == "No Positive":
        return 0
    return vader_sentiment.polarity_scores(review)["compound"]    

Más adelante en tu programa, cuando estés listo para calcular el sentimiento, puedes aplicarlo a cada reseña de la siguiente manera:

# Add a negative sentiment and positive sentiment column
print("Calculating sentiment columns for both positive and negative reviews")
start = time.time()
df["Negative_Sentiment"] = df.Negative_Review.apply(calc_sentiment)
df["Positive_Sentiment"] = df.Positive_Review.apply(calc_sentiment)
end = time.time()
print("Calculating sentiment took " + str(round(end - start, 2)) + " seconds")

Esto toma aproximadamente 120 segundos en mi computadora, pero puede variar en cada equipo. Si deseas imprimir los resultados y verificar si el sentimiento coincide con la reseña:

df = df.sort_values(by=["Negative_Sentiment"], ascending=True)
print(df[["Negative_Review", "Negative_Sentiment"]])
df = df.sort_values(by=["Positive_Sentiment"], ascending=True)
print(df[["Positive_Review", "Positive_Sentiment"]])

Lo último que debes hacer con el archivo antes de usarlo en el desafío es ¡guardarlo! También deberías considerar reorganizar todas tus nuevas columnas para que sean fáciles de trabajar (para una persona, es un cambio estético).

# Reorder the columns (This is cosmetic, but to make it easier to explore the data later)
df = df.reindex(["Hotel_Name", "Hotel_Address", "Total_Number_of_Reviews", "Average_Score", "Reviewer_Score", "Negative_Sentiment", "Positive_Sentiment", "Reviewer_Nationality", "Leisure_trip", "Couple", "Solo_traveler", "Business_trip", "Group", "Family_with_young_children", "Family_with_older_children", "With_a_pet", "Negative_Review", "Positive_Review"], axis=1)

print("Saving results to Hotel_Reviews_NLP.csv")
df.to_csv(r"../data/Hotel_Reviews_NLP.csv", index = False)

Debes ejecutar todo el código del notebook de análisis (después de haber ejecutado el notebook de filtrado para generar el archivo Hotel_Reviews_Filtered.csv).

Para repasar, los pasos son:

  1. El archivo del conjunto de datos original Hotel_Reviews.csv se explora en la lección anterior con el notebook explorador
  2. Hotel_Reviews.csv se filtra con el notebook de filtrado, resultando en Hotel_Reviews_Filtered.csv
  3. Hotel_Reviews_Filtered.csv se procesa con el notebook de análisis de sentimientos, resultando en Hotel_Reviews_NLP.csv
  4. Usa Hotel_Reviews_NLP.csv en el desafío de NLP a continuación

Conclusión

Cuando comenzaste, tenías un conjunto de datos con columnas y datos, pero no todo podía ser verificado o utilizado. Has explorado los datos, filtrado lo que no necesitas, convertido etiquetas en algo útil, calculado tus propios promedios, añadido algunas columnas de sentimientos y, con suerte, aprendido cosas interesantes sobre el procesamiento de texto natural.

Cuestionario posterior a la lección

Desafío

Ahora que tienes tu conjunto de datos analizado para sentimientos, intenta usar las estrategias que has aprendido en este curso (¿quizás agrupamiento?) para determinar patrones relacionados con los sentimientos.

Revisión y autoestudio

Toma este módulo de aprendizaje para aprender más y usar diferentes herramientas para explorar sentimientos en texto.

Tarea

Prueba con un conjunto de datos diferente


Descargo de responsabilidad:
Este documento ha sido traducido utilizando el servicio de traducción automática Co-op Translator. Aunque nos esforzamos por garantizar la precisión, tenga en cuenta que las traducciones automatizadas pueden contener errores o imprecisiones. El documento original en su idioma nativo debe considerarse como la fuente autorizada. Para información crítica, se recomienda una traducción profesional realizada por humanos. No nos hacemos responsables de malentendidos o interpretaciones erróneas que puedan surgir del uso de esta traducción.