22 KiB
Sentimentanalyse med hotellanmeldelser
Nå som du har utforsket datasettet i detalj, er det på tide å filtrere kolonnene og deretter bruke NLP-teknikker på datasettet for å få nye innsikter om hotellene.
Pre-lecture quiz
Filtrering og sentimentanalyse-operasjoner
Som du sikkert har lagt merke til, har datasettet noen problemer. Noen kolonner er fylt med unyttig informasjon, andre virker feil. Selv om de er korrekte, er det uklart hvordan de ble beregnet, og svarene kan ikke uavhengig verifiseres med dine egne beregninger.
Oppgave: litt mer databehandling
Rens dataene litt mer. Legg til kolonner som vil være nyttige senere, endre verdiene i andre kolonner, og fjern visse kolonner helt.
-
Innledende kolonnebehandling
-
Fjern
lat
oglng
-
Erstatt verdiene i
Hotel_Address
med følgende verdier (hvis adressen inneholder navnet på byen og landet, endre det til bare byen og landet).Dette er de eneste byene og landene i datasettet:
Amsterdam, Nederland
Barcelona, Spania
London, Storbritannia
Milano, Italia
Paris, Frankrike
Wien, Østerrike
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())
Nå kan du hente data på landsnivå:
display(df.groupby("Hotel_Address").agg({"Hotel_Name": "nunique"}))
Hotel_Address Hotel_Name Amsterdam, Nederland 105 Barcelona, Spania 211 London, Storbritannia 400 Milano, Italia 162 Paris, Frankrike 458 Wien, Østerrike 158
-
-
Behandle hotell-meta-anmeldelseskolonner
-
Fjern
Additional_Number_of_Scoring
-
Erstatt
Total_Number_of_Reviews
med det totale antallet anmeldelser for det hotellet som faktisk er i datasettet -
Erstatt
Average_Score
med vår egen beregnede score
-
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. Behandle anmeldelseskolonner
1. Fjern `Review_Total_Negative_Word_Counts`, `Review_Total_Positive_Word_Counts`, `Review_Date` og `days_since_review`
2. Behold `Reviewer_Score`, `Negative_Review` og `Positive_Review` som de er
3. Behold `Tags` for nå
- Vi skal gjøre noen ekstra filtreringsoperasjoner på taggene i neste seksjon, og deretter vil taggene bli fjernet
4. Behandle anmelderkolonner
1. Fjern `Total_Number_of_Reviews_Reviewer_Has_Given`
2. Behold `Reviewer_Nationality`
### Tag-kolonner
`Tag`-kolonnen er problematisk siden den er en liste (i tekstform) lagret i kolonnen. Dessverre er rekkefølgen og antallet underseksjoner i denne kolonnen ikke alltid det samme. Det er vanskelig for et menneske å identifisere de riktige frasene som er interessante, fordi det er 515 000 rader og 1427 hoteller, og hver har litt forskjellige alternativer en anmelder kunne velge. Her kommer NLP til nytte. Du kan skanne teksten og finne de vanligste frasene og telle dem.
Dessverre er vi ikke interessert i enkeltord, men flervordsfraser (f.eks. *Forretningsreise*). Å kjøre en flervordsfrekvensfordelingsalgoritme på så mye data (6762646 ord) kan ta ekstremt lang tid, men uten å se på dataene, virker det som om det er en nødvendig utgift. Her kommer utforskende dataanalyse til nytte, fordi du har sett et utvalg av taggene som `[' Forretningsreise ', ' Alenereisende ', ' Enkeltrom ', ' Bodde 5 netter ', ' Sendt fra en mobil enhet ']`, kan du begynne å spørre om det er mulig å redusere prosesseringen betydelig. Heldigvis er det det - men først må du følge noen få trinn for å fastslå hvilke tagger som er interessante.
### Filtrering av tagger
Husk at målet med datasettet er å legge til sentiment og kolonner som vil hjelpe deg med å velge det beste hotellet (for deg selv eller kanskje en klient som ber deg lage en hotellanbefalingsbot). Du må spørre deg selv om taggene er nyttige eller ikke i det endelige datasettet. Her er en tolkning (hvis du trengte datasettet av andre grunner, kan forskjellige tagger bli inkludert/utelatt):
1. Typen reise er relevant, og det bør beholdes
2. Typen gjestegruppe er viktig, og det bør beholdes
3. Typen rom, suite eller studio som gjesten bodde i er irrelevant (alle hoteller har stort sett de samme rommene)
4. Enheten anmeldelsen ble sendt fra er irrelevant
5. Antall netter anmelderen bodde *kan* være relevant hvis du tilskriver lengre opphold til at de likte hotellet mer, men det er en svak sammenheng og sannsynligvis irrelevant
Oppsummert, **behold 2 typer tagger og fjern de andre**.
Først vil du ikke telle taggene før de er i et bedre format, så det betyr å fjerne firkantede parenteser og anførselstegn. Du kan gjøre dette på flere måter, men du vil ha den raskeste siden det kan ta lang tid å behandle mye data. Heldigvis har pandas en enkel måte å gjøre hvert av disse trinnene på.
```Python
# Remove opening and closing brackets
df.Tags = df.Tags.str.strip("[']")
# remove all quotes too
df.Tags = df.Tags.str.replace(" ', '", ",", regex = False)
Hver tag blir noe som: Forretningsreise, Alenereisende, Enkeltrom, Bodde 5 netter, Sendt fra en mobil enhet
.
Neste finner vi et problem. Noen anmeldelser, eller rader, har 5 kolonner, noen 3, noen 6. Dette er et resultat av hvordan datasettet ble opprettet, og vanskelig å fikse. Du vil få en frekvenstelling av hver frase, men de er i forskjellig rekkefølge i hver anmeldelse, så tellingen kan være feil, og et hotell kan ikke få en tag tilordnet som det fortjente.
I stedet vil du bruke den forskjellige rekkefølgen til vår fordel, fordi hver tag er flervords, men også separert med komma! Den enkleste måten å gjøre dette på er å opprette 6 midlertidige kolonner med hver tag satt inn i kolonnen som tilsvarer dens rekkefølge i taggen. Du kan deretter slå sammen de 6 kolonnene til én stor kolonne og kjøre value_counts()
-metoden på den resulterende kolonnen. Ved å skrive ut dette, vil du se at det var 2428 unike tagger. Her er et lite utvalg:
Tag | Count |
---|---|
Fritidsreise | 417778 |
Sendt fra en mobil enhet | 307640 |
Par | 252294 |
Bodde 1 natt | 193645 |
Bodde 2 netter | 133937 |
Alenereisende | 108545 |
Bodde 3 netter | 95821 |
Forretningsreise | 82939 |
Gruppe | 65392 |
Familie med små barn | 61015 |
Bodde 4 netter | 47817 |
Dobbeltrom | 35207 |
Standard dobbeltrom | 32248 |
Superior dobbeltrom | 31393 |
Familie med eldre barn | 26349 |
Deluxe dobbeltrom | 24823 |
Dobbelt- eller tomannsrom | 22393 |
Bodde 5 netter | 20845 |
Standard dobbelt- eller tomannsrom | 17483 |
Klassisk dobbeltrom | 16989 |
Superior dobbelt- eller tomannsrom | 13570 |
2 rom | 12393 |
Noen av de vanlige taggene som Sendt fra en mobil enhet
er ikke nyttige for oss, så det kan være smart å fjerne dem før du teller fraseforekomst, men det er en så rask operasjon at du kan la dem være og ignorere dem.
Fjerne tagger for lengden på oppholdet
Å fjerne disse taggene er trinn 1, det reduserer det totale antallet tagger som skal vurderes litt. Merk at du ikke fjerner dem fra datasettet, bare velger å fjerne dem fra vurdering som verdier å telle/beholde i anmeldelsesdatasettet.
Lengde på opphold | Count |
---|---|
Bodde 1 natt | 193645 |
Bodde 2 netter | 133937 |
Bodde 3 netter | 95821 |
Bodde 4 netter | 47817 |
Bodde 5 netter | 20845 |
Bodde 6 netter | 9776 |
Bodde 7 netter | 7399 |
Bodde 8 netter | 2502 |
Bodde 9 netter | 1293 |
... | ... |
Det finnes et stort utvalg av rom, suiter, studioer, leiligheter og så videre. De betyr stort sett det samme og er ikke relevante for deg, så fjern dem fra vurdering.
Type rom | Count |
---|---|
Dobbeltrom | 35207 |
Standard dobbeltrom | 32248 |
Superior dobbeltrom | 31393 |
Deluxe dobbeltrom | 24823 |
Dobbelt- eller tomannsrom | 22393 |
Standard dobbelt- eller tomannsrom | 17483 |
Klassisk dobbeltrom | 16989 |
Superior dobbelt- eller tomannsrom | 13570 |
Til slutt, og dette er gledelig (fordi det ikke tok mye prosessering i det hele tatt), vil du bli igjen med følgende nyttige tagger:
Tag | Count |
---|---|
Fritidsreise | 417778 |
Par | 252294 |
Alenereisende | 108545 |
Forretningsreise | 82939 |
Gruppe (kombinert med Reisende med venner) | 67535 |
Familie med små barn | 61015 |
Familie med eldre barn | 26349 |
Med et kjæledyr | 1405 |
Du kan argumentere for at Reisende med venner
er det samme som Gruppe
mer eller mindre, og det ville være rimelig å kombinere de to som ovenfor. Koden for å identifisere de riktige taggene finnes i Tags-notatboken.
Det siste trinnet er å opprette nye kolonner for hver av disse taggene. Deretter, for hver anmeldelsesrad, hvis Tag
-kolonnen samsvarer med en av de nye kolonnene, legg til en 1, hvis ikke, legg til en 0. Sluttresultatet vil være en telling av hvor mange anmeldere som valgte dette hotellet (i aggregat) for, for eksempel, forretning vs fritid, eller for å ta med et kjæledyr, og dette er nyttig informasjon når du skal anbefale et hotell.
# 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)
Lagre filen
Til slutt, lagre datasettet slik det er nå med et nytt navn.
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)
Sentimentanalyse-operasjoner
I denne siste seksjonen skal du bruke sentimentanalyse på anmeldelseskolonnene og lagre resultatene i et datasett.
Oppgave: last inn og lagre de filtrerte dataene
Merk at du nå laster inn det filtrerte datasettet som ble lagret i forrige seksjon, ikke det originale datasettet.
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)
Fjerne stoppord
Hvis du skulle kjøre sentimentanalyse på de negative og positive anmeldelseskolonnene, kan det ta lang tid. Testet på en kraftig test-laptop med rask CPU, tok det 12–14 minutter avhengig av hvilken sentimentbibliotek som ble brukt. Det er en (relativt) lang tid, så det er verdt å undersøke om det kan gjøres raskere.
Å fjerne stoppord, eller vanlige engelske ord som ikke endrer sentimentet i en setning, er det første trinnet. Ved å fjerne dem, bør sentimentanalysen kjøre raskere, men ikke være mindre nøyaktig (siden stoppordene ikke påvirker sentimentet, men de bremser analysen).
Den lengste negative anmeldelsen var 395 ord, men etter å ha fjernet stoppordene, er den 195 ord.
Å fjerne stoppordene er også en rask operasjon, å fjerne stoppordene fra 2 anmeldelseskolonner over 515 000 rader tok 3,3 sekunder på test-enheten. Det kan ta litt mer eller mindre tid for deg avhengig av enhetens CPU-hastighet, RAM, om du har SSD eller ikke, og noen andre faktorer. Den relativt korte tiden for operasjonen betyr at hvis det forbedrer sentimentanalysen, er det verdt å gjøre.
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)
Utføre sentimentanalyse
Nå bør du beregne sentimentanalysen for både negative og positive anmeldelseskolonner, og lagre resultatet i 2 nye kolonner. Testen av sentimentet vil være å sammenligne det med anmelderens score for samme anmeldelse. For eksempel, hvis sentimentet mener den negative anmeldelsen hadde et sentiment på 1 (ekstremt positivt sentiment) og et positivt anmeldelsessentiment på 1, men anmelderen ga hotellet den laveste scoren mulig, så stemmer enten ikke anmeldelsesteksten med scoren, eller sentimentanalysatoren kunne ikke gjenkjenne sentimentet korrekt. Du bør forvente at noen sentimentresultater er helt feil, og ofte vil det være forklarbart, f.eks. anmeldelsen kan være ekstremt sarkastisk "Selvfølgelig ELSKET jeg å sove i et rom uten oppvarming" og sentimentanalysatoren tror det er positivt sentiment, selv om et menneske som leser det ville vite at det var sarkasme. NLTK tilbyr forskjellige sentimentanalysatorer som du kan lære med, og du kan bytte dem ut og se om sentimentet blir mer eller mindre nøyaktig. VADER sentimentanalyse brukes her.
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, juni 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"]
Senere i programmet, når du er klar til å beregne sentiment, kan du bruke det på hver anmeldelse som følger:
# 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")
Dette tar omtrent 120 sekunder på min datamaskin, men det vil variere fra datamaskin til datamaskin. Hvis du vil skrive ut resultatene og se om sentimentet samsvarer med anmeldelsen:
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"]])
Det aller siste du må gjøre med filen før du bruker den i utfordringen, er å lagre den! Du bør også vurdere å omorganisere alle de nye kolonnene dine slik at de er enkle å jobbe med (for et menneske er dette en kosmetisk endring).
# 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)
Du bør kjøre hele koden for analyse-notatboken (etter at du har kjørt filtrerings-notatboken for å generere Hotel_Reviews_Filtered.csv-filen).
For å oppsummere, trinnene er:
- Den originale datasettfilen Hotel_Reviews.csv utforskes i forrige leksjon med utforsker-notatboken
- Hotel_Reviews.csv filtreres av filtrerings-notatboken og resulterer i Hotel_Reviews_Filtered.csv
- Hotel_Reviews_Filtered.csv behandles av sentimentanalyse-notatboken og resulterer i Hotel_Reviews_NLP.csv
- Bruk Hotel_Reviews_NLP.csv i NLP-utfordringen nedenfor
Konklusjon
Da du startet, hadde du et datasett med kolonner og data, men ikke alt kunne verifiseres eller brukes. Du har utforsket dataene, filtrert ut det du ikke trenger, konvertert tagger til noe nyttig, beregnet dine egne gjennomsnitt, lagt til noen sentimentkolonner og forhåpentligvis lært noen interessante ting om behandling av naturlig tekst.
Quiz etter forelesning
Utfordring
Nå som du har analysert datasettet for sentiment, se om du kan bruke strategier du har lært i dette kurset (klustering, kanskje?) for å finne mønstre rundt sentiment.
Gjennomgang & Selvstudium
Ta denne Learn-modulen for å lære mer og bruke forskjellige verktøy for å utforske sentiment i tekst.
Oppgave
Ansvarsfraskrivelse:
Dette dokumentet er oversatt ved hjelp av AI-oversettelsestjenesten Co-op Translator. Selv om vi tilstreber nøyaktighet, vennligst vær oppmerksom på at automatiske oversettelser kan inneholde feil eller unøyaktigheter. Det originale dokumentet på sitt opprinnelige språk bør anses som den autoritative kilden. For kritisk informasjon anbefales profesjonell menneskelig oversettelse. Vi er ikke ansvarlige for eventuelle misforståelser eller feiltolkninger som oppstår ved bruk av denne oversettelsen.