import pandas as pd
train=pd.read_csv(".../train_E6oV3lV.csv")
print(train.head(10))
我们可以简单地调用split函数,将句子切分:
train['word_count']=train['tweet'].apply(lambda x:len(str(x).split(" ")))
train[['tweet','word_count']].head()
train['char_count']=train['tweet'].str.len()
train[['tweet','char_count']].head()
注意这里字符串的个数包含了推文中的空格个数,我们根据需要自行去除掉
def avg_word(sentence):
words=sentence.split()
return (sum(len(word) for word in words)/len(words))
train['avg_word']=train['tweet'].apply(lambda x:avg_word(x))
train[['tweet','avg_word']].head()
通常情况下,在解决NLP问题时,首要任务时去除停用词(stopword)。但是有时计算停用词的数量可以提供我们之前失去的额外信息。下面关于停用词的解释:
为节省存储空间和提高搜索效率,搜索引擎在索引页面或处理搜索请求时会自动忽略某些字或词,这些字或词即被称为Stop Words(停用词)。通常意义上,Stop Words大致为如下两类:
在这里,我们导入NLTK库中的stopwors模块
from nltk.corpus import stopwords
stop=stopwords.words('english')
train['stopwords']=train['tweet'].apply(lambda sen:len([x for x in sen.split() if x in stop]))
train[['tweet','stopwords']].head()
一个比较有趣的特征就是我们可以从每个推文中提取“#”和“@”符号的数量。这也有利于我们从文本数据中提取更多信息
这里我们使用startswith
函数来处理
train['hashtags']=train['tweet'].apply(lambda sen:len([x for x in sen.split() if x.startswith("#")]))
train[['tweet','hashtags']].head()
这个特征并不常用,但是在做相似任务时,数字数量是一个比较有用的特征
train['numerics']=train['tweet'].apply(lambda sen:len([x for x in sen.split() if x.isdigit()]))
train[['tweet','numerics']].head()
“Anger”或者 “Rage”通常情况下使用大写来表述,所以有必要去识别出这些词
train['upper']=train['tweet'].apply(lambda sen:len([x for x in sen.split() if x.isupper()]))
train[['tweet','upper']].head()
到目前为止,我们已经学会了如何从文本数据中提取基本特征。深入文本和特征提取之前,我们的第一步应该是清洗数据,以获得更好的特性。
我们将实现这一目标做一些基本的训练数据预处理步骤。
预处理的第一步,我们要做的是把我们的推文变成小写。这避免了拥有相同的多个副本。例如,当我们计算字词汇数量时,“Analytics”和“analytics”将被视为不同的单词。
train['tweet']=train['tweet'].apply(lambda sen:" ".join(x.lower() for x in sen.split()))
train['tweet'].head()
下一步是去除标点符号,因为它在文本数据中不添加任何额外的信息。因此删除的所有符号将帮助我们减少训练数据的大小。
train['tweet'] = train['tweet'].str.replace('[^\w\s]','')
train['tweet'].head()
正如你所看到的在上面的输出中,所有的标点符号,包括”#”和”@”已经从训练数据中去除
正如我们前面所讨论的,停止词(或常见单词)应该从文本数据中删除。为了这个目的,我们可以创建一个列表stopwords作为自己停用词库或我们可以使用预定义的库。
from nltk.corpus import stopwords
stop=stopwords.words('english')
train['tweet']=train['tweet'].apply(lambda sen:" ".join(x for x in sen.split() if x not in stop))
train['tweet'].head()
我们可以把常见的单词从文本数据首先,让我们来检查中最常出现的10个字文本数据然后再调用删除或保留。
freq=pd.Series(' '.join(train['tweet']).split()).value_counts()[:10]
现在我们把这些词去除掉,因为它们对我们文本数据分类没有任何作用
freq=list(freq.index)
train['tweet']=train['tweet'].apply(lambda sen:' '.join(x for x in sen.split() if x not in freq))
train['tweet'].head()
同样,正如我们删除最常见的话说,这一次让我们从文本中删除很少出现的词。因为它们很稀有,它们之间的联系和其他词主要是噪音。可以替换罕见的单词更一般的形式,然后这将有更高的计数。
freq = pd.Series(' '.join(train['tweet']).split()).value_counts()[-10:]
freq = list(freq.index)
train['tweet'] = train['tweet'].apply(lambda x: " ".join(x for x in x.split() if x not in freq))
train['tweet'].head()
所有这些预处理步骤是必不可少的,帮助我们减少我们的词汇噪音,这样最终产生更有效的特征。
我们都见过推文存在大量的拼写错误。我们再短时间内匆忙发送tweet,很难发现这些错误。在这方面,拼写校正是一个有用的预处理步骤,因为这也会帮助我们减少单词的多个副本。例如,“Analytics”和“analytcs”将被视为不同的单词,即使它们在同一意义上使用。
为实现这一目标,我们将使用textblob库。
TextBlob是一个用Python编写的开源的文本处理库。它可以用来执行很多自然语言处理的任务,比如,词性标注,名词性成分提取,情感分析,文本翻译,等等。你可以在官方文档阅读TextBlog的所有特性。
from textblob import TextBlob
train['tweet'][:5].apply(lambda x: str(TextBlob(x).correct()))
注意,它会花费很多时间去做这些修正。因此,为了学习的目的,我只显示这种技术运用在前5行的效果。
另外在使用这个技术之前,需要小心一些,因为如果推文中存在大量缩写,比如“your”缩写为“ur”,那么将修正为“or”
分词是指将文本划分为一系列的单词或词语。在我们的示例中,我们使用了textblob库
TextBlob(train['tweet'][1]).words
WordList(['thanks', 'lyft', 'credit', 'cant', 'use', 'cause', 'dont', 'offer', 'wheelchair', 'vans', 'pdx', 'disapointed', 'getthanked'])
词形还原(lemmatization),是把一个任何形式的语言词汇还原为一般形式(能表达完整语义),而词干提取
(stemming)是抽取词的词干或词根形式(不一定能够表达完整语义)。词形还原和词干提取是词形规范化的两类重要方式,都能够达到有效归并词形的目的,二者既有联系也有区别。具体介绍请参考词干提取(stemming)和词形还原(lemmatization)
词干提取(stemming)是指通过基于规则的方法去除单词的后缀,比如“ing”,“ly”,“s”等等。
from nltk.stem import PorterStemmer
st=PorterStemmer()
train['tweet'][:5].apply(lambda x:" ".join([st.stem(word) for word in x.split()]))
在上面的输出中,“dysfunctional ”已经变为“dysfunct ”
词形还原处理后获得的结果是具有一定意义的、完整的词,一般为词典中的有效词
from textblob import Word
train['tweet']=train['tweet'].apply(lambda x:" ".join([Word(word).lemmatize() for word in x.split()]))
train['tweet'].head()
到目前为止,我们已经做了所有的可以清洗我们数据的预处理基本步骤。现在,我们可以继续使用NLP技术提取特征。
N-grams称为N元语言模型,是多个词语的组合,是一种统计语言模型,用来根据前(n-1)个item来预测第n个item。常见模型有一元语言模型(unigrams)、二元语言模型(bigrams )、三元语言模型(trigrams )。
Unigrams包含的信息通常情况下比bigrams和trigrams少,需要根据具体应用选择语言模型,因为如果n-grams太短,这时不能捕获重要信息。另一方面,如果n-grams太长,那么捕获的信息基本上是一样的,没有差异性
TextBlob(train['tweet'][0]).ngrams(2)
词频(Term frequency)就是一个单词在一个句子出现的次数与这个句子单词个数的比例。
TF = (Number of times term T appears in the particular row) / (number of terms in that row)
tf1 = (train['tweet'][1:2]).apply(lambda x: pd.value_counts(x.split(" "))).sum(axis = 0).reset_index()
tf1.columns = ['words','tf']
tf1
反转文档频率(Inverse Document Frequency),简称为IDF,其原理可以简单理解为如果一个单词在所有文档都会出现,那么可能这个单词对我们没有那么重要。
一个单词的IDF就是所有行数与出现该单词的行的个数的比例,最后对数。
import numpy as np
for i,word in enumerate(tf1['words']):
tf1.loc[i, 'idf'] =np.log(train.shape[0]/(len(train[train['tweet'].str.contains(word)])))
tf1
TF-IDF=TF*IDF
tf1['tfidf']=tf1['tf']*tf1['idf']
tf1
我们可以看到,TF-IDF已经“惩罚了”‘don’t’, ‘can’t’, 和‘use’,因为它们是通用词,tf-idf的值都比较低。
另外可以通过sklearn直接计算tf-idf值
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf = TfidfVectorizer(max_features=1000, lowercase=True,analyzer='word',stop_words= 'english',ngram_range=(1,1))
train_vect = tfidf.fit_transform(train['tweet'])
train_vect
BOW,就是将文本/Query看作是一系列词的集合。由于词很多,所以咱们就用袋子把它们装起来,简称词袋。至于为什么用袋子而不用筐(basket)或者桶(bucket),这咱就不知道了。举个例子:
苏宁易购/是/国内/著名/的/B2C/电商/之一
这是一个短文本。“/”作为词与词之间的分割。从中我们可以看到这个文本包含“苏宁易购”,“B2C”,“电商”等词。换句话说,该文本的的词袋由“苏宁易购”,“电商”等词构成。
详细请参考词袋模型和词向量模型
from sklearn.feature_extraction.text import CountVectorizer
bow = CountVectorizer(max_features=1000, lowercase=True, ngram_range(1,1), analyzer = "word")
train_bow = bow.fit_transform(train['tweet'])
train_bow
我们最终需要解决的任务就是如何对推文进行情感分析,在使用ML/DL模型之前,我们可以使用textblob库去进行评测情感
train['tweet'][:5].apply(lambda x:TextBlob(x).sentiment)
使用TextBlob情感分析的结果,以元组的方式进行返回,形式如(polarity, subjectivity). 其中polarity的分数是一个范围为 [−1.0,1.0] [ − 1.0 , 1.0 ] 浮点数, 正数表示积极,负数表示消极。subjectivity 是一个 范围为 [0.0,1.0] [ 0.0 , 1.0 ] 的浮点数,其中 0.0 0.0 表示 客观, 1.0 1.0 表示主观的。
下面是一个简单实例
from textblob import TextBlob
testimonial = TextBlob("Textblob is amazingly simple to use. What great fun!")
print(testimonial.sentiment)
train['sentiment'] = train['tweet'].apply(lambda x: TextBlob(x).sentiment[0] )
train[['id','tweet','sentiment']].head()
词嵌入就是文本的向量化表示,潜在思想就是相似单词的向量之间的距离比较短。
from gensim.scripts.glove2word2vec import glove2word2vec
glove_input_file = 'glove.6B.100d.txt'
word2vec_output_file = 'glove.6B.100d.txt.word2vec'
glove2word2vec(glove_input_file, word2vec_output_file)