pythonai

[译文]另一个使用Python的Twitter情感分析 - 第1

2018-06-06  本文已影响214人  CalPort

离上一篇文章已经有一段时间了。在我缺席的这段时间,我的生活发生了许多改变。 我终于鼓起勇气辞掉工作,并加入伦敦大会的 Data Science Immersive 课程。
这是我一生中的一个重大决定,但我并不后悔。事实上,我认为这是迄今为止我做过的最好的决定。这门课很激烈,但我喜欢它的每一部分。我学到了许多关于数据科学更严谨的方法,最重要的是我从导师和同学那里得到的反馈,使我进步飞速。这并不容易,但绝对值得!
我目前正在进行第八周的课程,并为我的毕业项目做准备。正如标题所示,这是关于Twitter的情感分析。
起初,我不确定我的毕业设计应该做什么,但毕竟,我感兴趣的领域是自然语言处理(NLP),而 Twitter 似乎是我 NLP 旅程的一个很好的起点。你可以从这里找到我最初的项目想法意见。
简要地介绍一下这个项目,它是关于建立一个可以检测情感的模型,因此我就可以把这个模型应用到不同城市的 tweets(推文)上,并比较/分析来自不同城市的推文的快乐程度。这起源于我自己的好奇心。对我来说,快乐是生活中非常重要的因素,我认为这很大程度上取决于你的环境。所以,我想看看不同城市的市民有多幸福。

训练数据

情感分析可以有很多种不同的方式,一些库提供了可以直接使用的情感分析函数,你可以将其直接运用在文本上,但是那样没有乐趣(是吗?)。所以我决定训练我自己的模型,将其应用到我收集的推文中。为了使模型具有良好的性能,我需要一个相对较好的数据集来训练。训练的数据集,我选择了来自斯坦福大学的“Sentiment140”。有关数据集的更多信息可以从链接中找到。 http://help.sentiment140.com/for-students/
数据集可以从下面的链接下载。
http://cs.stanford.edu/people/alecmgo/trainingandtestdata.zip
通过查看链接中对数据集的描述,可以找到每个字段的信息。
0 - tweet的极性(0=消极,2=中性,4=积极)
1 - tweet的ID(2087)
2 - tweet日期(Sat May 16 23:58:44 UTC 2009)
3 - 查询(lyx)。 如果没有查询,那么这个值是NO_QUERY。
4 - tweet用户(robotickilldozr)
5 - tweet文本(Lyx is cool)
好的,我们先来看看数据

import pandas as pd  
import numpy as np
import matplotlib.pyplot as plt
cols = ['sentiment','id','date','query_string','user','text']
df = pd.read_csv("./trainingandtestdata/training.1600000.processed.noemoticon.csv",header=None, names=cols,encoding="ISO-8859–1")
# above line will be different depending on where you saved your data, and your file name
df.head()
df.sentiment.value_counts()

数据集有160万条记录,没有空记录,对于“sentiment”列来说很重要,尽管数据集描述中提到了中性类,但是训练集中没有中性类。
50%的数据带有消极标签,另外50%带有积极标签。 从中我们可以看出类别划分没有偏向性。

df.drop(['id','date','query_string','user'],axis=1,inplace=True)

为了进行情感分析,我首先删除了不需要的列。
“id”列是每个tweet的唯一ID
“date”列作为tweet的日期信息
“query_string”列是指是否使用了特定查询关键字收集了tweet,但对于该列,100%的记录都是“NO_QUERY”。
“user”列是推特用户被Twitter处理后的名称。
我决定删除以上四列。

df[df.sentiment == 0].head(10)
df[df.sentiment == 4].head(10) 

通过查看每个类别的一些记录,似乎所有消极类别的索引都是从0到799999,并且积极类别记录的索引是从800000开始到数据集的末尾。

数据准备

作为完整性检查的一种方式,我们来看看每个记录中文本列中字符串的长度。

df['pre_clean_len'] = [len(t) for t in df.text]

数据字典 - 初稿

下面是数据集的数据字典的第一稿,但是当我进行准备时,需要进行更新。

from pprint import pprint
data_dict = {
    'sentiment':{
        'type':df.sentiment.dtype,
        'description':'sentiment class - 0:negative, 1:positive'
    },
    'text':{
        'type':df.text.dtype,
        'description':'tweet text'
    },
    'pre_clean_len':{
        'type':df.pre_clean_len.dtype,
        'description':'Length of the tweet before cleaning'
    },
    'dataset_shape':df.shape
}
pprint(data_dict)

我将会用箱形图绘制pre_clean_len,这样我就可以看到每条记录中字符串长度的总体分布。

fig, ax = plt.subplots(figsize=(5, 5))
plt.boxplot(df.pre_clean_len)
plt.show()

这看起来有点奇怪,因为twitter的字符限制是140。但是从上面的箱形图来看,有些推特的长度超过140个字符。

df[df.pre_clean_len > 140].head(10)

看起来是时候进行一些清理了!

数据准备1:HTML解码

它看起来像HTML编码,没有被转换成文本,并且在文本字段中以“&”、“>”等字符结尾。将HTML解码为一般文本将是我数据准备的第一步。 我将为此使用BeautifulSoup。

df.text[279]
from bs4 import BeautifulSoup
example1 = BeautifulSoup(df.text[279], 'lxml')
print(example1.get_text())

数据准备2:'@'提及

准备工作的第二部分是处理@提及(@mention)
尽管@mention带有一定的信息(推文中提到的另一个用户),但这些信息并没有为构建情感分析模型提供价值。

df.text[343]
import re
re.sub(r'@[A-Za-z0-9]+','',df.text[343])

数据准备3:URL链接

清理的第三部分是处理URL链接,与@mention一样,即使它带有一些信息,但为了情感分析的目的,这可以忽略。

df.text[0]
re.sub('https?://[A-Za-z0-9./]+','',df.text[0])

数据准备4:UTF-8 BOM(字节顺序标记)

df.text[226]

通过查看上面的记录,我看到奇怪的字符模式 “\xef\xbf\xbd”。 经过一番研究,我发现这些字符是 UTF-8 BOM。
“UTF-8 BOM 是一个字节序列(EF BB BF),允许读者将文件识别为以 UTF-8 格式进行编码。”
通过使用'utf-8-sig'来解码文本,这个BOM将被替换为unicode无法识别的特殊字符,然后我可以将它处理为“?”

testing = df.text[226].decode("utf-8-sig")
testing
testing.replace(u"\ufffd", "?")

我按照作者的思路进行操作,运行代码时,出现一些错误:在python2中str有decode方法,python3里面都是用的Unicode编码,没有该方法了,下面是我自己在python3环境下的代码和结果:

df.text[226]
bytes(df.text[226], encoding='ISO-8859–1')
testing = ascii(bytes(df.text[226], encoding='ISO-8859–1').decode('utf-8'))
testing
testing.replace('\\ufffd',"?").strip("'")

数据准备5:井号 /数字

有时,与井号一起使用的文本可以提供有关推文的有用信息。 将所有与井号一起出现的文本都删除可能有点冒险。
所以我决定留下完整的文本,仅仅删除'#'。对于包括数字在内的所有非字母字符,我都将按照这个思路进行处理。

df.text[175]
re.sub("[^a-zA-Z]", " ", df.text[175])

定义数据清理函数

首先定义数据清洗函数,将上述五个数据清洗任务包含进去,然后将其应用到整个数据集上。分词,词干化/词形化、停用词将在后期用 count vectorizer 和 Tfidf vectorizer 创建矩阵时使用。

from nltk.tokenize import WordPunctTokenizer
tok = WordPunctTokenizer()
pat1 = r'@[A-Za-z0-9]+'
pat2 = r'https?://[A-Za-z0-9./]+'
combined_pat = r'|'.join((pat1, pat2))
def tweet_cleaner(text):
    soup = BeautifulSoup(text, 'lxml')
    souped = soup.get_text()
    stripped = re.sub(combined_pat, '', souped)
    try:
        clean = stripped.decode("utf-8-sig").replace(u"\ufffd", "?")
    except:
        clean = stripped
    letters_only = re.sub("[^a-zA-Z]", " ", clean)
    lower_case = letters_only.lower()
    # During the letters_only process two lines above, it has created unnecessay white spaces,
    # I will tokenize and join together to remove unneccessary white spaces
    words = tok.tokenize(lower_case)
    return (" ".join(words)).strip()
testing = df.text[:100]
test_result = []
for t in testing:
    test_result.append(tweet_cleaner(t))
test_result
nums = [0,400000,800000,1200000,1600000]
print("Cleaning and parsing the tweets...\n")
clean_tweet_texts = []
for i in range(nums[0],nums[1]):
    if( (i+1)%10000 == 0 ):
        print("Tweets %d of %d has been processed" % ( i+1, nums[1]))                                                                    
    clean_tweet_texts.append(tweet_cleaner(df['text'][i]))

剩下的,你知道了,我把整个数据集分成四批,然后清理它们。

将清理后的数据保存为csv

clean_df = pd.DataFrame(clean_tweet_texts,columns=['text'])
clean_df['target'] = df.sentiment
clean_df.head()
clean_df.to_csv('clean_tweet.csv',encoding='utf-8')
csv = 'clean_tweet.csv'
my_df = pd.read_csv(csv,index_col=0)
my_df.head()

由于文章越来越长,我会在这里停下来,并试着在下一篇文章中继续。 如果您有任何问题,意见或建议,请不要犹豫,留下评论! 谢谢。

您可以从下面的Github链接中找到Jupyter Notebook文件。
https://github.com/tthustla/twitter_sentiment_analysis_part1/blob/master/Capstone_part2.ipynb

原文地址:Another Twitter sentiment analysis with Python — Part 1

上一篇下一篇

猜你喜欢

热点阅读