Код находится на GitHub, а набор данных доступен на Kaggle.

Введение

Игра престолов от HBO, пожалуй, один из самых популярных и лучших сериалов всех времен. У него появилось огромное количество поклонников по всему миру из-за драмы и многочисленных сюжетных линий, которые разыгрывались в течение 8 сезонов. Тем не менее, общее отношение к сериалу, казалось, со временем менялось: люди были взволнованы и полны энтузиазма в течение первых двух сезонов, но к концу его показа люди разочаровались и расстроились из-за развития персонажей и резких выводов к основным сюжетным линиям. В рамках этого проекта я просмотрел твиты во время финального сезона Игры престолов и провел анализ настроений, чтобы увидеть, как общественное мнение относилось к сериалу и как оно менялось с течением времени.

Данные и методология

Данные взяты из набора данных, созданного Франсиско де Абреу эЛима, который был общедоступен на Kaggle. На странице набора данных он подробно описывает шаги, которые он предпринял для сбора данных. Чтобы выполнить анализ настроений в наборе данных, я использую библиотеку обработки естественного языка NLTK в Python, которая содержит модель классификации Naive Bayes и набор данных примеров твитов, которые я использовал для обучения модели. После обучения модели я прогоняю через нее твиты Игры престолов, чтобы получить настроение для каждого твита. Последний шаг — просмотр сюжетов о том, как менялись настроения в течение последнего сезона шоу.

Предварительная обработка и обучение модели

Первым шагом очистки и предварительной обработки данных является загрузка данных и создание фрейма данных только с необходимыми столбцами.

# Import the file with tweets
got = pd.read_csv('/content/drive/MyDrive/gotTwitter.csv')
gotsa = got[['created_at', 'text']]
gotsa.head()

Теперь кадр данных заполняется только временем создания твита и фактическим текстом каждого твита. Отсюда я сосредоточусь на обучении модели с помощью набора данных образцов твиттера в библиотеке NLTK.

positive_tokens = twitter_samples.tokenized('positive_tweets.json')
negative_tokens = twitter_samples.tokenized('negative_tweets.json')

Следующий шаг включает в себя очистку как положительных, так и отрицательных токенов путем удаления гиперссылок и дескрипторов Twitter, их лемматизации (возвращение слов в их словарную форму) и удаления знаков препинания и стоп-слов.

positive_cleaned_tokens = []
for i in range(len(positive_tokens)):
  row_token = []
  for token, tag in pos_tag(positive_tokens[i]):
    token = re.sub('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+#]|[!*\(\),]|'\
                    '(?:%[0-9a-fA-F][0-9a-fA-F]))+','', token) # remove any hyperlinks
    token = re.sub('(@[A-Za-z0-9_]+)','', token) # remove any twitter handles
    if tag.startswith('NN'): # assigning nouns
      pos = 'n'
    elif tag.startswith('VB'): # assigning verbs
      pos = 'v'
    else: # assigning adjectives
      pos = 'a'
    lemmatizer = WordNetLemmatizer()
    token = lemmatizer.lemmatize(token, pos) # lemmatize the token
    if len(token) > 0 and token not in string.punctuation and token.lower() not in stop_words:
      row_token.append(token.lower()) # save the token to the row (tweet)
  positive_cleaned_tokens.append(row_token) # save the row (tweet) to the list of cleaned tweets

После этого я создаю словарь с твитами, чтобы он правильно проходил через модель. Затем я добавляю классификатор, чтобы обозначить, является ли настроение положительным или отрицательным, чтобы я мог объединить наборы данных в один словарь. Затем с помощью одного словаря я разделяю набор данных для обучения и набор данных для тестирования.

# Define the function to put the tweet tokens into a dictionary
def get_tweets_for_model(cleaned_tokens_list):
  for tweet_tokens in cleaned_tokens_list:
    yield dict([token, True] for token in tweet_tokens)
# Run the function on both datasets
positive_tokens_for_model = get_tweets_for_model(positive_cleaned_tokens)
negative_tokens_for_model = get_tweets_for_model(negative_cleaned_tokens)
# Add classifier to denote if tweet is positive or negative
positive_dataset = [(tweet_dict, "Positive")
                     for tweet_dict in positive_tokens_for_model]

negative_dataset = [(tweet_dict, "Negative")
                     for tweet_dict in negative_tokens_for_model]

dataset = positive_dataset + negative_dataset

train_data, test_data = train_test_split(dataset, test_size=0.3, random_state=10)

Наконец, когда наборы для обучения и тестирования готовы, я обучаю модель, а затем проверяю точность.

classifier = NaiveBayesClassifier.train(train_data)

print("Accuracy is:", classify.accuracy(classifier, test_data))

print(classifier.show_most_informative_features(10))

Модель возвращает точность 99,67%, что очень хорошо, но, как я укажу позже, это может вводить в заблуждение. Однако после обучения модели я перехожу к ее использованию для анализа настроений в наборе данных Game of Thrones.

Полученные результаты

Теперь, когда модель обучена и очень точна, я прогоняю через нее твиты Game of Thrones после очистки и предварительной обработки твитов так же, как это делается для набора данных образца.

# Loop to put the tweet tokens in a dictionary and then run it through the model to return the sentiment
got_sent = []
for tokens in cleaned_tokens:
  got_dict = dict([token, True] for token in tokens)
  got_sent.append(classifier.classify(got_dict))
# Add a new column to the dataframe that will show the sentiment
gotsa['sentiment'] = got_sent
gotsa.head()

В приведенной выше таблице показан кадр данных с настроением для каждого твита, добавленного в качестве нового столбца. Использование нормализованного .value_counts() в столбце «настроение» показывает, что 60% твитов имеют негативное настроение. Хотя большинство твитов негативны, я хотел посмотреть, изменилось ли настроение со временем.

Чтобы исследовать, как тональность твитов менялась с течением времени, я рассмотрел 2 разных периода времени. Первый — это просмотр твитов по дням и то, как они развивались в течение всего последнего сезона. Во втором временном интервале рассматриваются 15-минутные интервалы времени, чтобы увидеть, как настроение менялось в течение каждого эпизода. Я надеюсь увидеть изменение настроений с течением времени в обоих временных рамках, надеюсь, соответствующее событиям в сериале.

На этом графике показано количество твитов в день во время показа последнего сезона Игры престолов. Наиболее примечательной особенностью является то, что количество твитов резко возрастает в день выхода серий. Это связано с тем, что люди были в восторге от эпизода, писали в Твиттере во время эпизода и комментировали свои мысли после того, как эпизод закончился. Еще одна ключевая особенность заключается в том, что каждый день негативных твитов больше, чем позитивных, за исключением случаев, когда 23 и 24 мая их может быть одинаковое количество. Это не то, чего я ожидал. Я ожидал увидеть сдвиг туда и обратно с течением времени. Я также думал, что твиты, предшествующие первому эпизоду, будут в основном положительными, поскольку люди были в восторге от финального сезона. Возможно, в большинстве твитов было негативное отношение к сериалу, но также могло быть и то, что набор данных, который мы использовали для обучения модели, не очень подходил для твитов об Игре престолов. набор данных. Последней интересной особенностью является то, как количество твитов меняется в дни до/после эпизода. Для эпизодов 2–6 количество твитов, как негативных, так и позитивных, обычно уменьшается с каждым днем ​​до дня выхода следующего эпизода. Однако для эпизода 1 количество твитов увеличивается с каждым днем ​​​​до его выхода в эфир.

Наиболее заметной особенностью этих графиков, как и при просмотре ежедневного набора данных, является то, что каждый временной блок здесь содержит больше негативных твитов, чем позитивных. Я думаю, что должны быть хотя бы несколько моментов до, во время или после эпизода, когда настроение скорее положительное, чем отрицательное. Итак, опять же, возможно, набор данных, используемый для обучения этой модели, не идеален. Мы также можем видеть, что первый, третий и шестой эпизоды были наиболее популярными в Твиттере. Это имеет смысл, потому что люди были в восторге от нового сезона в первом эпизоде, главные герои сражались с белыми ходоками, а шестой эпизод стал финальным эпизодом всего сериала. Также интересно отметить, как количество твитов менялось в каждом эпизоде. Каждый эпизод начинался в 21:00 по восточному времени, и во время первого эпизода было довольно много твитов. Однако во время третьего эпизода, как только он начался, количество твитов стало уменьшаться сразу после этого. Эта разница может указывать на разные уровни взаимодействия с разными эпизодами.

В будущем, я думаю, было бы полезно найти другой набор данных для обучения модели. В идеале я хотел бы иметь больший набор данных с более широким разнообразием твитов, которые могли бы различать тона и сарказм. Кроме того, было бы лучше включить нейтральные настроения, потому что большинство твитов могут попасть в эту категорию. Рассмотрение нейтральных настроений также покажет более четкую картину положительных и отрицательных настроений, поскольку у них будут более строгие критерии для попадания в одну из этих категорий.