在本次練習(xí)中,我們主要實(shí)現(xiàn)build_freqs()函數(shù),并且把數(shù)據(jù)喂進(jìn)去看看可視化的結(jié)果浪感。在整個推特情感分析項(xiàng)目中缎罢,這個函數(shù)的任務(wù)是構(gòu)建一個字典伊群。我們可以在字典里面查找每個詞出現(xiàn)的次數(shù)考杉。字典對于后續(xù)提取數(shù)據(jù)集的特征值是非常有幫助的。
不單單是計(jì)算頻次舰始,而是計(jì)算一個單詞崇棠,描述正向的次數(shù)和負(fù)向的次數(shù)。也就是說丸卷,當(dāng)一個單詞出現(xiàn)在一個句子時枕稀,這個句子更可能是在講正向的話,還是負(fù)向的谜嫉。
導(dǎo)入庫
先來導(dǎo)入我們需要用到的庫
import nltk
from nltk.corpus import twitter_samples
import matplotlib.pyplot as plt
import numpy as np
導(dǎo)入兩個在utils.py中定義好的函數(shù)
- process_tweet() 數(shù)據(jù)清洗萎坷,分詞函數(shù)
- build_freqs() 計(jì)算一個單詞關(guān)聯(lián)正向或負(fù)向的頻次,然后構(gòu)建出 freqs 字典沐兰,其中的數(shù)據(jù)鍵是 (word, label)哆档,數(shù)據(jù)值是頻次(一個數(shù)值)
from nltk.book import *
from utils import process_tweet, build_freqs
*** Introductory Examples for the NLTK Book ***
Loading text1, ..., text9 and sent1, ..., sent9
Type the name of the text or sentence to view it.
Type: 'texts()' or 'sents()' to list the materials.
text1: Moby Dick by Herman Melville 1851
text2: Sense and Sensibility by Jane Austen 1811
text3: The Book of Genesis
text4: Inaugural Address Corpus
text5: Chat Corpus
text6: Monty Python and the Holy Grail
text7: Wall Street Journal
text8: Personals Corpus
text9: The Man Who Was Thursday by G . K . Chesterton 1908
加載 NLTK 樣本數(shù)據(jù)集
像前一篇做數(shù)據(jù)預(yù)處理的步驟一樣,先把數(shù)據(jù)加載到列表中來住闯。在這個練習(xí)當(dāng)中虐呻,加載數(shù)據(jù)非常簡單,只需要一行命令寞秃。但我們在實(shí)際情況中會碰到多種數(shù)據(jù)源斟叼,比如:關(guān)系型數(shù)據(jù)庫,非關(guān)系型數(shù)據(jù)庫春寿,接口如WebSerivices朗涩,文本,excel表格等等绑改。因此許多從業(yè)人員也會抱怨谢床,做數(shù)據(jù)科學(xué)80~90%的時間都花在了處理數(shù)據(jù)本身之上。
all_positve_tweets = twitter_samples.strings('positive_tweets.json')
all_negative_tweets = twitter_samples.strings('negative_tweets.json')
tweets = all_positve_tweets + all_negative_tweets
print("Number of tweets", len(tweets))
Number of tweets 10000
接下來厘线,我們會創(chuàng)建一個標(biāo)簽數(shù)組(array)识腿,后續(xù)用于關(guān)聯(lián)實(shí)際數(shù)據(jù)的情感。數(shù)組的使用跟列表差不多造壮,但會更便于計(jì)算渡讼。
這里labels
數(shù)組共10000個元素,前5000個全是 ‘1‘ 代表正向情感耳璧,后5000個全是 ‘0‘ 代表負(fù)向成箫。
我們用numpy庫來進(jìn)行這樣的處理。
-
np.ones()
創(chuàng)建全 1 數(shù)組 -
np.zeros()
創(chuàng)建全 0 數(shù)組 -
np.append()
連接數(shù)組
# 分別給 np.ones() 和 np.zeros() 輸入兩個list的長度值旨枯,然后再連接到一塊去
labels = np.append(np.ones((len(all_positve_tweets))), np.zeros((len(all_negative_tweets))))
# 顯示出來看看更直觀
labels
array([ 1., 1., 1., ..., 0., 0., 0.])
構(gòu)建字典
在Python蹬昌,字典是可變的,被索引好的集合攀隔。它把每一個成員存儲為 鍵-值對(key-value pairs)皂贩,并使用哈希表來建立查找索引栖榨。這有利于在上千條數(shù)據(jù)中進(jìn)行快速檢索。
定義
Python字典的聲明使用大括號
dictionary = {'key1': 1, 'key2': 2}
這里定義的字典包含兩個條目明刷,在這個例子里治泥,我們使用了字符串類型,根據(jù)實(shí)際情況還可以是浮點(diǎn)數(shù)遮精,整數(shù)居夹,元組(tuple)等
增加/修改入口
用中括號可以對字典進(jìn)行操作,如果字典鍵已存在本冲,值會被覆蓋
# 增加新入口
dictionary['key3'] = -5
# 覆蓋值
dictionary['key1'] = 0
print(dictionary)
{'key1': 0, 'key2': 2, 'key3': -5}
訪問值和查找鍵
對字典進(jìn)行檢索和提取是常用操作准脂,建議反復(fù)練習(xí),這里有兩個方法:
- 使用中括號:鍵已存在時用檬洞,若鍵不存在則報(bào)錯
- 使用get()函數(shù):如果值不存在狸膏,則可以存在一個默認(rèn)值
# 中括號查找
print(dictionary['key2'])
2
我們來試試報(bào)錯的情況,查找一個不存在的鍵
print(dictionary['key666'])
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-8-3e8f34e21792> in <module>()
----> 1 print(dictionary['key666'])
KeyError: 'key666'
在使用中括號進(jìn)行檢索時添怔,一個常用的辦法是通過條件語句來處理鍵是否能被找到的問題湾戳。或者你也可以使用get()函數(shù)來為找不到的鍵生成一個默認(rèn)值广料。
if 'key1' in dictionary:
print("item found: ", dictionary['key1']) # 輸出 key1 對應(yīng)的值為:0
else:
print('key1 is not defined')
print("item found: ", dictionary.get('key1', -1)) # 同上砾脑,若 key1 沒有對應(yīng)值才會輸出 -1
item found: 0
item found: 0
if 'key7' in dictionary:
print(dictionary['key7'])
else:
print('key does not exists') # 字典中沒有 key7 因此會輸出這一行
print(dictionary.get('key7', -1)) # 字典中沒有 key7 所以會直接給 key7 一個默認(rèn)值為 -1
print(dictionary['key7']) # 最后的報(bào)錯意味著上一步get()的執(zhí)行并沒有給字典賦值
key does not exists
-1
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-10-ff9ae431bf94> in <module>()
5
6 print(dictionary.get('key7', -1)) # 字典中沒有 key7 所以會直接給 key7 一個默認(rèn)值為 -1
----> 7 print(dictionary['key7']) # 最后的報(bào)錯意味著上一步get()的執(zhí)行并沒有給字典賦值
KeyError: 'key7'
詞頻統(tǒng)計(jì)
下面我們一起來看看 build_freqs()
函數(shù)是如何實(shí)現(xiàn)的
def build_freqs(tweets, ys):
"""Build frequencies.
Input:
tweets: a list of tweets
ys: an m x 1 array with the sentiment label of each tweet
(either 0 or 1)
Output:
freqs: a dictionary mapping each (word, sentiment) pair to its
frequency
"""
# Convert np array to list since zip needs an iterable.
# The squeeze is necessary or the list ends up with one element.
# Also note that this is just a NOP if ys is already a list.
yslist = np.squeeze(ys).tolist()
# Start with an empty dictionary and populate it by looping over all tweets
# and over all processed words in each tweet.
freqs = {}
for y, tweet in zip(yslist, tweets):
for word in process_tweet(tweet):
pair = (word, y)
if pair in freqs:
freqs[pair] += 1
else:
freqs[pair] = 1
return freqs
如前所述,你也可以使用get()函數(shù)處理鍵值匹配問題艾杏,如:
for y, tweet in zip(yslist, tweets):
for word in process_tweet(tweet):
pair = (word, y)
freqs[pair] = freqs.get(pair, 0) + 1
如上所示韧衣,(word, y) 是一對鍵-值。word是被處理后的推特?cái)?shù)據(jù)购桑,而y則是對應(yīng)正向/負(fù)向的標(biāo)記畅铭,即1或0。例如:
# "folowfriday" 在正向推特中出現(xiàn)了 25 次
('followfriday', 1.0): 25
# "shame" 在負(fù)向推特中出現(xiàn)了 19 次
('shame', 0.0): 19
到這里勃蜘,或者你就能夠理解自然語言為何如此難硕噩,因?yàn)槿祟惐磉_(dá)中獨(dú)有的諸如先抑后揚(yáng),用負(fù)面詞匯表達(dá)正向情感等缭贡。
例如炉擅,一款好玩的游戲,我們常常會說“有毒”匀归,但“有毒”這一詞在通常情況下又是負(fù)面的坑资。
人類語言(特別是中文)這樣的特性讓機(jī)器“理解”語言變得尤為困難。
下面穆端,我們就來看看build_freqs()函數(shù)返回的字典長什么樣子
freqs = build_freqs(tweets, labels)
print(f'type(freqs) = {type(freqs)}') # print的一種用法,把大括號執(zhí)行語句的輸出仿便,跟前面的字符串合并起來
print(f'len(freqs) = {len(freqs)}')
type(freqs) = <class 'dict'>
len(freqs) = 13076
再把所有詞的詞頻print出來看看
print(freqs)
{('followfriday', 1.0): 25, ......('misser', 0.0): 1}
然而体啰,這樣的結(jié)果對于理解數(shù)據(jù)并沒有帶來太多幫助攒巍。更好的辦法是對數(shù)據(jù)進(jìn)行可視化。
詞頻表格
在可視化之后荒勇,把我們感興趣的那部分?jǐn)?shù)據(jù)放進(jìn)一張臨時表(table)是更好的辦法柒莉。
keys = ['happi', 'merri', 'nice', 'good', 'bad', 'sad', 'mad', 'best', 'pretti',
'?', ':)', ':(', '??', '??', '??', '??', '?',
'song', 'idea', 'power', 'play', 'magnific']
data = []
for word in keys:
pos = 0
neg = 0
if (word, 1) in freqs:
pos = freqs[(word, 1)]
if (word, 0) in freqs:
neg = freqs[(word, 0)]
data.append([word, pos, neg])
data
[['happi', 211, 25],
['merri', 1, 0],
['nice', 98, 19],
['good', 238, 101],
['bad', 18, 73],
['sad', 5, 123],
['mad', 4, 11],
['best', 65, 22],
['pretti', 20, 15],
['?', 29, 21],
[':)', 3568, 2],
[':(', 1, 4571],
['??', 1, 3],
['??', 0, 2],
['??', 5, 1],
['??', 2, 1],
['?', 0, 210],
['song', 22, 27],
['idea', 26, 10],
['power', 7, 6],
['play', 46, 48],
['magnific', 2, 0]]
這張表格看起來就清晰多了,更進(jìn)一步沽翔,我們可以使用散點(diǎn)圖來表示這些數(shù)據(jù)兢孝。
我們不用原始數(shù)值來繪制,因?yàn)槟菚?dǎo)致圖樣范圍太大仅偎,取而代之我們在對數(shù)尺度上繪制跨蟹,這樣所有的計(jì)數(shù)更能夠在一個維度上看清。
例如橘沥,“:)” 有3568個計(jì)數(shù)為正窗轩,只有2個為負(fù),對數(shù)取值后變?yōu)?.17和0.69座咆。(想通過計(jì)算器驗(yàn)證的同學(xué)要用 ln 以自然對數(shù)e為底痢艺,而不是一般的以10為底的 log)
我們還要在圖上添加一條紅線,標(biāo)志正區(qū)域和負(fù)區(qū)域的邊界介陶〉淌妫靠近紅線的詞可被歸類為中性詞。
fig, ax = plt.subplots(figsize = (8,8))
x = np.log([x[1] + 1 for x in data])
y = np.log([x[2] + 1 for x in data])
ax.scatter(x, y)
plt.xlabel("Log Positive count")
plt.ylabel("Log Negative count")
for i in range(0, len(data)):
ax.annotate(data[i][0], (x[i], y[i]), fontsize = 12)
ax.plot([0,9], [0,9], color = 'red')
plt.show()
這張圖很容易理解哺呜,特別是表情符號:(和:)在情感分析中似乎很重要植酥。因此,在進(jìn)行數(shù)據(jù)預(yù)處理時不能去掉這些符號弦牡。
此外我們還看到友驮,皇冠所表示的意義似乎還蠻消極的。
感興趣的同學(xué)建議可以試試把所有數(shù)據(jù)都放到圖上來回發(fā)生什么驾锰。
ChangeLog
- 2021/2/2 09:59:22 完成1-2步驟
- 2021/2/2 17:11:14 完成剩余步驟及理解