天天看點

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

阿曼多·凡丹戈(Armando Fandango)的《python資料分析(第2版)》,2018年6月出版,本系列為讀書筆記。主要是為了系統整理,加深記憶。第6章“資料可視化”介紹如何利用Matplotlib和Pandas的繪圖函數來實作資料的可視化。

python資料分析個人學習讀書筆記-目錄索引

第9章分析文本資料和社交媒體

   在前幾章中,我們着重讨論了結構化資料的分析,主要涉及清單格式的資料。實際上,跟結構化資料一樣,純文字也是最常見的格式之一。文本分析需要用到詞頻分布分析、模式識别、标注、連結和關聯分析(link and association analysis)、情感分析和可視化等。這裡将借助Python自然語言工具包(The Python Natural Language Toolkit,NLTK)來分析文本。NLTK自身帶有一個文本樣本集,這個樣本集名為corpora。同時,scikitlearn程式庫也提供了許多文本分析工具,本章也會對這些工具進行簡要的介紹。

  此外,在本章中還會舉例說明網絡分析。本章涉及的主題如下。

  • 安裝NLTK
  • NLTK簡介
  • 濾除停用字、姓名和數字
  • 詞袋模型
  • 詞頻分析
  • 樸素貝葉斯分類
  • 情感分析
  • 建立詞雲
  • 社交網絡分析

9.1NLTK

  安裝NLTK:

1 pip3 install nltk scikit-learn      

9.2NLTK簡介

   NLTK是一個用來分析自然語言文本(如英文句子)的Python應用程式接口。NLTK起源于2001年,最初設計是用來進行教學的。

  雖然我們在9.1節安裝了NLTK,但還不夠:還需要下載下傳NLTK語料庫。需要注意的是,這裡的下載下傳量相對較大(約1.8 GB),但是隻需要下載下傳一次。除非我們确切地知道自己需要哪種語料庫,否則最好下載下傳所有可用的語料庫。從Python shell下載下傳語料庫的指令如下。

1 $ python3
2 >>> import nltk
3 >>> nltk.download()      

  這時,會出現一個GUI應用程式,你可以指定要下載下傳的檔案以及放到何處,如圖9-1所示。

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

  如果是NLTK新手,最友善的方法就是選擇預設選項并下載下傳所有内容。在本章中,我們将需要用到停用詞、電影評論、名字和古騰堡語料庫。是以,我們鼓勵讀者根據ch-09.ipynb檔案中的代碼來學習下列部分。

    Tips:如果安裝NLTK方面有疑惑,這篇文章或許對你有幫助。《資料分析實戰-托馬茲.卓巴斯》讀書筆記第9章--自然語言處理NLTK(分析文本、詞性标注、主題抽取、文本資料分類)

9.3濾除停用字、姓名和數字

   進行文本分析時,我們經常需要對停用字(Stopwords)進行剔除。這裡所謂的停用字,就是那些經常見、但是沒有多大資訊含量的詞。NLTK為很多語種都提供了停用字語料庫。下面加載英語停用字語料并輸出部分單詞。

1 sw = set(nltk.corpus.stopwords.words('english'))
2 print("Stop words:", list(sw)[:7])      

  下面是輸出的部分常用字。

Stop words: ['yours', 'off', 'own', 'so', "won't", 'all', 'doing']      

  請注意,這個語料庫中的所有單詞都是小寫的。

  此外,NLTK還提供了一個Gutenberg語料庫。Gutenberg項目是一個數字圖書館計劃,供人們在網際網路上閱讀圖書。

  下面加載Gutenberg語料庫并輸出部分檔案的名稱。

1 gb = nltk.corpus.gutenberg
2 print("Gutenberg files:\n", gb.fileids()[-5:])      

  下面是輸出的某些書籍的名稱,其中有些你可能比較熟悉。

Gutenberg files:
 ['milton-paradise.txt', 'shakespeare-caesar.txt', 'shakespeare-hamlet.txt', 'shakespeare-macbeth.txt', 'whitman-leaves.txt']      

  現在,從milton-paradise.txt檔案中提取前兩句内容,供下面過濾使用。下面給出具體代碼。

1 text_sent = gb.sents("milton-paradise.txt")[:2]
2 print("Unfiltered:", text_sent)      

  下面是輸出的句子。

Unfiltered: [['[', 'Paradise', 'Lost', 'by', 'John', 'Milton', '1667', ']'], ['Book', 'I']]      

  現在,過濾掉下面的停用字。

1 for sent in text_sent:
2     filtered = [w for w in sent if w.lower() not in sw]
3     print("Filtered:\n", filtered)      

  對于第一句,過濾後變成以下格式。

Filtered:
 ['[', 'Paradise', 'Lost', 'John', 'Milton', '1667', ']']      

  注意,與之前相比,單詞by已經被過濾掉了,因為它出現在停用字語料庫中了。有時,我們希望将文本中的數字和姓名也删掉。我們可以根據詞性(Part of Speech,POS)标簽來删除某些單詞。在這個标注方案中,數字對應着基數(Cardinal Number,CD)标簽。

  姓名對應着單數形式的專有名詞(the proper noun singular,NNP)标簽。标注是基于啟發式方法進行處理的,當然,這不可能非常精确。這個主題過于宏大,恐怕需要整本書來進行描述,詳見前言部分。在過濾文本時,我們可以使用pos_tag()函數擷取文本内所含的标簽,具體如下。

1     tagged = nltk.pos_tag(filtered)
2     print("Tagged:\n", tagged)      

  對于我們的文本,将得到如下各種标簽。

Tagged:
 [('[', 'JJ'), ('Paradise', 'NNP'), ('Lost', 'NNP'), ('John', 'NNP'), ('Milton', 'NNP'), ('1667', 'CD'), (']', 'NN')]      

  上面的pos_tag()函數将傳回一個元組清單,其中各元組的第二個元素就是文本對應的标簽。可見,一些單詞雖然被标注為NNP,但是它們并不是。這裡所謂的啟發式方法,就是如果單詞的第一個字母是大寫的,那麼就将其标注為NNP。如果将上面的文本全部轉換為小寫,那麼就會得到不同的結果,這個留給讀者自行練習。實際上,我們很容易就能利用NNP和CD标簽來删除清單中的單詞,代碼如下。

1     words= []
2     for word in tagged:
3         if word[1] != 'NNP' and word[1] != 'CD':
4            words.append(word[0]) 
5 
6     print("Words:\n",words)      

  下列代碼摘自本書代碼包中的ch-09.ipynb檔案。

1 import nltk
 2 from nltk import data
 3 data.path.append(r'D:\download\nltk_data') # 這裡的路徑需要換成自己資料檔案下載下傳的路徑
 4 for sent in text_sent:
 5     filtered = [w for w in sent if w.lower() not in sw]
 6     print("Filtered:\n", filtered)
 7     tagged = nltk.pos_tag(filtered)
 8     print("Tagged:\n", tagged)
 9 
10     words= []
11     for word in tagged:
12         if word[1] != 'NNP' and word[1] != 'CD':
13            words.append(word[0]) 
14 
15     print("Words:\n",words)      

9.4詞袋模型

   所謂詞袋模型,即它認為一篇文檔是由其中的詞構成的一個集合(即袋子),詞與詞之間沒有順序以及先後的關系。對于文檔中的每個單詞,我們都要計算它出現的次數,即單詞計數(word counts)。據此,我們可以進行類似垃圾郵件識别之類的統計分析。

  如果有一批文檔,那麼每個唯一字(unique word)都可以視為語料庫中的一個特征。這裡所謂的“特征”,可以了解為參數或者變量。利用所有的單詞計數,我們可以為每個文檔建立一個特征向量這裡的“向量”一詞借用的是其數學含義。如果一個單詞存在于語料庫中,但是不存在于文檔中,那麼這個特征的值就為0。令人驚訝的是,NLTK至今尚未提供可以友善建立特征向量的實用程式。不過,我們可以借用Python機器學習庫scikit-learn中的CountVectorizer類來輕松建立特征向量。第10章将對scikit-learn進行更詳盡的介紹。

  下面從NLTK的Gutenberg語料庫加載兩個文本文檔,代碼如下。

1 hamlet = gb.raw("shakespeare-hamlet.txt")
2 macbeth = gb.raw("shakespeare-macbeth.txt")      

  現在我們去掉英語停用詞并建立特征向量,代碼如下。

1 cv = sk.feature_extraction.text.CountVectorizer(stop_words='english')
2 print("Feature vector:\n", cv.fit_transform([hamlet, macbeth]).toarray())      

  下面是兩個文檔對應的特征向量。

Feature vector:
 [[ 1  0  1 ... 14  0  1]
 [ 0  1  0 ...  1  1  0]]      

  下面,我們輸出部分特征(唯一字)值。

1 print("Features:\n", cv.get_feature_names()[:5])      

  這些特征是按字母順序排序的。

Features:
 ['1599', '1603', 'abhominably', 'abhorred', 'abide']      
1 import nltk
 2 import sklearn as sk
 3 from nltk import data
 4 data.path.append(r'D:\download\nltk_data') # 這裡的路徑需要換成自己資料檔案下載下傳的路徑
 5 
 6 hamlet = gb.raw(“shakespeare-hamlet.txt”)
 7 macbeth = gb.raw(“shakespeare-macbeth.txt”)
 8 
 9 cv = sk.feature_extraction.text.CountVectorizer(stop_words=’english’)
10 
11 print(“Feature vector:\n”, cv.fit_transform([hamlet, macbeth]).toarray())
12 
13 print(“Features:\n”, cv.get_feature_names()[:5])      

9.5詞頻分析

   NLTK提供的FreqDist類可以用來将單詞封裝成字典并計算給定單詞清單中各個單詞出現的次數。下面,我們來加載Gutenberg項目中莎士比亞的Julius Caesar中的文本。

  首先,把停用詞和标點符号剔除掉,代碼如下。

1 punctuation = set(string.punctuation)
2 filtered = [w.lower() for w in words if w.lower() not in sw and w.lower() not in punctuation]      

  然後,建立一個FreqDist對象并輸出頻率最高的鍵和值,代碼如下。

1 fd = nltk.FreqDist(filtered)
2 print("Words", list(fd.keys())[:5])
3 print("Counts", list(fd.values())[:5])      

  輸出的鍵和值如下。

Words ['tragedie', 'julius', 'caesar', 'william', 'shakespeare']
Counts [2, 1, 190, 1, 1]      

  很明顯,這個清單中的第一個字并非英語單詞,是以需要添加一種啟發式搜尋(Heuristic)方法,隻選擇最少含有兩個字元的單詞。對于NLTK提供的FreqDist類,我們既可以使用類似于字典的通路方法,也可以使用更便利的其他方法。下面,我們要提取最常出現的單詞以及對應的出現次數。

1 print("Max", fd.max())
2 print("Count", fd['d'])      

  對應下面的結果,你肯定不會感到吃驚。

Max caesar
Count 0      

  目前,我們隻是針對單個單詞構成的詞彙進行了分析,不過可以推而廣之,将分析擴充到含有兩個單詞和3個單詞的詞彙上,對于後面這兩種,我們分别稱之為雙字詞和三字詞。對于這兩種分析,分别有對應的函數,即bigrams()函數和trigrams()函數。現在,我們再次進行文本分析,不過,這次針對的是雙字詞。

1 fd = nltk.FreqDist(nltk.bigrams(filtered))
2 print("Bigrams", list(fd.keys())[:5])
3 print("Counts", list(fd.values())[:5])
4 print("Bigram Max", fd.max())
5 print("Bigram count", fd[('let', 'vs')])      

  結果如下。

Bigrams [('tragedie', 'julius'), ('julius', 'caesar'), ('caesar', 'william'), ('william', 'shakespeare'), ('shakespeare', '1599')]
Counts [1, 1, 1, 1, 1]
Bigram Max ('let', 'vs')
Bigram count 16      
1 import nltk
 2 import string
 3 from nltk import data
 4 data.path.append(r'D:\download\nltk_data') # 這裡的路徑需要換成自己資料檔案下載下傳的路徑
 5 
 6 
 7 gb = nltk.corpus.gutenberg
 8 words = gb.words("shakespeare-caesar.txt")
 9 
10 sw = set(nltk.corpus.stopwords.words('english'))
11 punctuation = set(string.punctuation)
12 filtered = [w.lower() for w in words if w.lower() not in sw and w.lower() not in punctuation]
13 fd = nltk.FreqDist(filtered)
14 print("Words", list(fd.keys())[:5])
15 print("Counts", list(fd.values())[:5])
16 print("Max", fd.max())
17 print("Count", fd['d'])
18 
19 fd = nltk.FreqDist(nltk.bigrams(filtered))
20 print("Bigrams", list(fd.keys())[:5])
21 print("Counts", list(fd.values())[:5])
22 print("Bigram Max", fd.max())
23 print("Bigram count", fd[('let', 'vs')])      

9.6樸素貝葉斯分類

   分類算法是機器學習算法中的一種,用來判斷給定資料項所屬的類别,即種類或類型。比如,我們可以根據某些特征來分辨一部電影屬于哪個流派等。這樣,流派就是我們要預測的類别。第10章還會對機器學習做進一步介紹。此刻,我們要讨論的是一個名為樸素貝葉斯分類的流行算法,它常常用于進行文本文檔的分析。

  樸素貝葉斯分類是一個機率算法,它基于機率與數理統計中的貝葉斯定理。貝葉斯定理給出了利用新證據修正某事件發生的機率的方法。例如,假設一個袋子裡裝有一些巧克力和其他物品,但是這些我們沒法看到。這時,我們可以用P(D)表示從袋子中掏出一塊深色巧克力的機率。同時,我們用P©代表掏出一塊巧克力的機率。當然,因為全機率是1,是以P(D)和P©的最大取值也隻能是1。貝葉斯定理指出,後驗機率與先驗機率和相似度的乘積成正比,具體公式如下。

  P(D|C)

  在上面的公式中,P(D|C)是在事件C發生的情況下事件D發生的可能性。在我們還沒有掏出任何物品之前,P(D) = 0.5,因為我們尚未獲得任何資訊。實際應用這個公式時,我們必須知道P(C|D)和P(C),或者能夠間接求出這兩個機率。

  樸素貝葉斯分類之是以稱為樸素,是因為它簡單假設特征之間是互相獨立的。實踐中,樸素貝葉斯分類的效果通常都會很好,說明這個假設得到了一定程度的保證。近來,人們發現這個假設之是以有意義,理論上是有依據的。不過,由于機器學習領域發展迅猛,現在已經發明了多種效果更佳的算法。

  下面,我們将利用停用詞或标點符号對單詞進行分類。這裡,我們将字長作為一個特征,因為停用詞和标點符号往往都比較短。

  為此,我們需要定義如下所示的函數。

1 def word_features(word):
2    return {'len': len(word)}
3 
4 def isStopword(word):
5     return word in sw or word in punctuation      

  下面,我們對取自古登堡項目的shakespeare-caesar.txt中的單詞進行标注,以區分是否為停用詞,代碼如下。

1 labeled_words = ([(word.lower(), isStopword(word.lower())) for 
2 word in words])
3 random.seed(42)
4 random.shuffle(labeled_words)
5 print(labeled_words[:5])      

  下面顯示了5個标注後的單詞。

[('i', True), ('is', True), ('in', True), ('he', True), ('ambitious', False)]      

  對于每個單詞,我們可以求出其長度。

1 featuresets = [(word_features(n), word) for (n, word) in 
2 labeled_words]      

  前幾章我們介紹過拟合以及通過訓練資料集和測試資料集的交叉驗證來避免這種情況的方法。下面我們将要訓練一個樸素貝葉斯分類器,其中90%的單詞用于訓練,剩下的10%用于測試。首先,建立訓練資料集和測試資料集并針對資料展開訓練,代碼如下。

1 cutoff = int(.9 * len(featuresets))
2 train_set, test_set = featuresets[:cutoff], featuresets[cutoff:]
3 classifier = nltk.NaiveBayesClassifier.train(train_set)      

  現在,我們拿出一些單詞,檢查該分類器的效果。

1 classifier = nltk.NaiveBayesClassifier.train(train_set)
2 print("'behold' class", classifier.classify(word_features('behold')))
3 print("'the' class", classifier.classify(word_features('the')))      

  幸運的是,這些單詞的分類完全正确:

'behold' class False
'the' class True      

  然後,根據測試資料集來計算分類器的準确性,代碼如下。

1 print("Accuracy", nltk.classify.accuracy(classifier, test_set))      

  這個分類器的準确度非常高,幾乎達到85%。下面來看哪些特征的貢獻最大。

1 print(classifier.show_most_informative_features(5))      

  結果顯示如圖9-2所示,在分類過程中字長的作用最大。

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體
1 mport nltk
 2 from nltk import data
 3 data.path.append(r'D:\download\nltk_data') # 這裡的路徑需要換成自己資料檔案下載下傳的路徑
 4 import string
 5 import random
 6 
 7 sw = set(nltk.corpus.stopwords.words('english'))
 8 punctuation = set(string.punctuation)
 9 
10 def word_features(word):
11    return {'len': len(word)}
12 
13 def isStopword(word):
14     return word in sw or word in punctuation
15 gb = nltk.corpus.gutenberg
16 words = gb.words("shakespeare-caesar.txt")
17 
18 labeled_words = ([(word.lower(), isStopword(word.lower())) for 
19 word in words])
20 random.seed(42)
21 random.shuffle(labeled_words)
22 print(labeled_words[:5])
23 
24 featuresets = [(word_features(n), word) for (n, word) in 
25 labeled_words]
26 cutoff = int(.9 * len(featuresets))
27 train_set, test_set = featuresets[:cutoff], featuresets[cutoff:]
28 classifier = nltk.NaiveBayesClassifier.train(train_set)
29 print("'behold' class", classifier.classify(word_features('behold')))
30 print("'the' class", classifier.classify(word_features('the')))
31 
32 print("Accuracy", nltk.classify.accuracy(classifier, test_set))
33 print(classifier.show_most_informative_features(5))      

9.7情感分析

  随着社交媒體、産品評論網站及論壇的興起,用來自動抽取意見的觀點挖掘或情感分析也随之變成一個炙手可熱的新研究領域。通常情況下,我們希望知道某個意見的性質是正面的、中立的還是負面的。當然,這種類型的分類我們前面就曾遇到過。也就是說,我們有大量的分類算法可用。還有一個方法就是,通過半自動的(經過某些人工編輯)方法來編制一個單詞清單,每個單詞賦予一個情感分,即一個數值(如單詞“good”的情感分為5,而單詞“bad”的情感分為−5)。如果有了這樣一張表,就可以給文本文檔中的所有單詞打分,進而得出一個情感總分。當然,類别的數量可以大于3,如五星級評級方案。

  我們會應用樸素貝葉斯分類方法對NLTK的影評語料庫進行分析,進而将影評分為正面的或負面的評價。首先,加載影評語料庫并過濾掉停用詞和标點符号。這些步驟在此略過,因為之前就介紹過了。也可以考慮更精細的過濾方案,不過,需要注意的是,如果過濾得過火了,就會影響準确性。下面通過categories()方法對影評文檔進行标注,代碼如下。

1 labeled_docs = [(list(movie_reviews.words(fid)), cat)
2         for cat in movie_reviews.categories()
3         for fid in movie_reviews.fileids(cat)]      

 完整的語料庫擁有數以萬計的唯一字,這些唯一字都可以用作特征,不過,這樣做會極大地影響效率。這裡,我們選用詞頻最高的前5%的單詞作為特征。

1 words = FreqDist(filtered)
2 N = int(.05 * len(words.keys()))
3 word_features = list(words.keys())[:N]      

  對于每個文檔,可以通過一些方法來提取特征,其中包括以下方法:

  • 檢查給定文檔是否含有某個單詞;
  • 求出某個單詞在給定文檔中出現的次數;
  • 正則化單詞計數,使得正則化後的最大單詞計數小于或等于1;
  • 将上面的數值加1,然後取對數。這裡之是以加1,是為了防止對0取對數;
  • 利用上面的數值組成一個度量名額。

  俗話說“條條大路通羅馬”,不過,這其中肯定不乏有些道路能夠讓我們更安全,也能更快地到達羅馬。下面定義一個函數,該函數會使用原始單詞計數來作為度量名額,代碼為如下。

1 def doc_features(doc):
2     doc_words = FreqDist(w for w in doc if not isStopWord(w))
3     features = {}
4     for word in word_features:
5         features['count (%s)' % word] = (doc_words.get(word, 0))
6     return features      

  現在,可以訓練我們的分類器了,具體方法與前面的例子相似。這裡準确性達到78%,這個成績已經相當不錯了,非常接近情感分析成績的上限。因為據研究發現,即便是人類,也無法對給定文檔的表達出的感情的看法達成一緻。是以,要想讓情感分析軟體獲得滿分是不可能的事情。

下面的圖9-3給出包含資訊量最大的特征。

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

  如果仔細檢查這個清單,我們很容易發現有一些褒義詞,如“wonderful(妙不可言的)”和“outstanding(傑出的)”。而單詞“bad(惡劣的)”“stupid(愚蠢的)”和“boring(無趣的)”則明顯是一些貶義詞。如果有興趣,讀者也可以分析其他的那些特征,這将作為一個作業留給讀者自己完成。以下代碼摘自本書代碼包中的sentiment.py檔案。

1 import random
 2 from nltk import data
 3 data.path.append(r'D:\download\nltk_data') # 這裡的路徑需要換成自己資料檔案下載下傳的路徑
 4 from nltk.corpus import movie_reviews
 5 from nltk.corpus import stopwords
 6 from nltk import FreqDist
 7 from nltk import NaiveBayesClassifier
 8 from nltk.classify import accuracy
 9 import string
10 
11 labeled_docs = [(list(movie_reviews.words(fid)), cat)
12         for cat in movie_reviews.categories()
13         for fid in movie_reviews.fileids(cat)]
14 random.seed(42)
15 random.shuffle(labeled_docs)
16 
17 review_words = movie_reviews.words()
18 print("# Review Words", len(review_words))
19 
20 sw = set(stopwords.words('english'))
21 punctuation = set(string.punctuation)
22 
23 def isStopWord(word):
24     return word in sw or word in punctuation
25 
26 filtered = [w.lower() for w in review_words if not isStopWord(w.lower())]
27 print("# After filter", len(filtered))
28 words = FreqDist(filtered)
29 N = int(.05 * len(words.keys()))
30 word_features = list(words.keys())[:N]
31 
32 def doc_features(doc):
33     doc_words = FreqDist(w for w in doc if not isStopWord(w))
34     features = {}
35     for word in word_features:
36         features['count (%s)' % word] = (doc_words.get(word, 0))
37     return features
38 
39 featuresets = [(doc_features(d), c) for (d,c) in labeled_docs]
40 train_set, test_set = featuresets[200:], featuresets[:200]
41 classifier = NaiveBayesClassifier.train(train_set)
42 print("Accuracy", accuracy(classifier, test_set))
43 
44 print(classifier.show_most_informative_features())      

9.8建立詞雲

   閱讀本書前,我們也許已在Wordle或其他地方見過詞雲了;如果沒見過也不要緊,因為可以在本章看個夠了。雖然Python有兩個程式庫都可以用來生成詞雲,但是它們生成詞雲的效果尚不能與Wordle網站的效果相媲美。是以,我們不妨利用Wordle網站的頁面來建立詞雲。利用Wordle生成詞雲時,需要提供一個單詞清單及其對應的權值,具體格式如下。

Word1 : weight
Word2 : weight      

  現在,對前面例子中的代碼稍作修改,讓它輸出這個字表。這裡,我們将使用詞頻作為度量名額,選取詞頻排名靠前的單詞。實際上,這裡不需要任何新的代碼,具體内容可以參考本書代碼包中的ch-09.ipynb檔案。

1 from nltk import data
 2 data.path.append(r'D:\download\nltk_data') # 這裡的路徑需要換成自己資料檔案下載下傳的路徑
 3 from nltk.corpus import movie_reviews
 4 from nltk.corpus import stopwords
 5 from nltk import FreqDist
 6 import string
 7 
 8 sw = set(stopwords.words('english'))
 9 punctuation = set(string.punctuation)
10 
11 def isStopWord(word):
12     return word in sw or word in punctuation
13 review_words = movie_reviews.words()
14 filtered = [w.lower() for w in review_words if not isStopWord(w.lower())]
15 
16 words = FreqDist(filtered)
17 N = int(.01 * len(words.keys()))
18 tags = list(words.keys())[:N]
19 
20 for tag in tags:
21     print(tag, ':', words[tag])      

  将上面代碼的輸出結果複制粘貼到上面提到的Wordle頁面,就能得到如圖9-4所示的詞雲了。

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體
《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體
plot : 1513
two : 1911
teen : 151
couples : 27
go : 1113
church : 69
party : 183
drink : 32
drive : 105
get : 1949
accident : 104
one : 5852
guys : 268
dies : 104
girlfriend : 218
continues : 88
see : 1749
life : 1586
nightmares : 26
deal : 219
watch : 603
movie : 5771
sorta : 10
find : 782
critique : 61
mind : 451
fuck : 17
generation : 96
touches : 55
cool : 208
idea : 386
presents : 78
bad : 1395
package : 30
makes : 992
review : 295
even : 2565
harder : 33
write : 119
since : 768
generally : 103
applaud : 10
films : 1536
attempt : 263
break : 175
mold : 14
mess : 159
head : 387
lost : 409
highway : 28
memento : 10
good : 2411
ways : 189
making : 602
types : 48
folks : 74
snag : 2
correctly : 17
seem : 574
taken : 225
pretty : 528
neat : 32
concept : 114
executed : 46
terribly : 58
problems : 293
well : 1906
main : 399
problem : 396
simply : 428
jumbled : 12
starts : 316
normal : 111
downshifts : 2
fantasy : 97
world : 1037
audience : 914
member : 126
going : 888
dreams : 131
characters : 1859
coming : 275
back : 1060
dead : 418
others : 288
look : 835
like : 3690
strange : 185
apparitions : 5
disappearances : 3
looooot : 1
chase : 159
scenes : 1274
tons : 21
weird : 100
things : 852
happen : 220
explained : 70
personally : 44
trying : 566
unravel : 9
film : 9517
every : 947
give : 561
clue : 45
kind : 559
fed : 29
biggest : 149
obviously : 228
got : 470
big : 1064
secret : 184
hide : 40
seems : 1033
want : 560
completely : 440
final : 380
five : 284
minutes : 644
make : 1642
entertaining : 314
thrilling : 46
engaging : 79
meantime : 19
really : 1558
sad : 108
part : 714
arrow : 29
dig : 19
flicks : 81
actually : 837
figured : 29
half : 535
way : 1693
point : 685
strangeness : 6
start : 312
little : 1501
bit : 568
sense : 555
still : 1047
guess : 226
bottom : 93
line : 435
movies : 1206
always : 586
sure : 523
given : 502
password : 4
enter : 68
understanding : 57
mean : 242
showing : 147
melissa : 12
sagemiller : 8
running : 323
away : 655
visions : 31
20 : 98
throughout : 302
plain : 82
lazy : 34
okay : 125
people : 1455
chasing : 43
know : 1217
need : 316
giving : 214
us : 1073
different : 430
offering : 38
insight : 54
apparently : 209
studio : 163
took : 164
director : 1237
chopped : 5
shows : 410
might : 635
decent : 164
somewhere : 127
suits : 45
decided : 104
turning : 80
music : 480
video : 322
edge : 104
would : 2109
actors : 706
although : 795
wes : 50
bentley : 9
seemed : 212
playing : 362
exact : 64
character : 2020
american : 559
beauty : 135
new : 1292
neighborhood : 21
kudos : 21
holds : 90
entire : 408
feeling : 225
unraveling : 2
overall : 160
stick : 69
entertain : 53
confusing : 94
rarely : 102
excites : 2
feels : 216
redundant : 14
runtime : 8
despite : 352
ending : 423
explanation : 69
craziness : 4
came : 185
oh : 216
horror : 473
slasher : 84
flick : 196
packaged : 3
someone : 401
assuming : 15
genre : 268
hot : 127
kids : 328
also : 1967
wrapped : 39
production : 300
years : 846
ago : 199
sitting : 93
shelves : 13
ever : 776
whatever : 136
skip : 45
joblo : 30
nightmare : 60
elm : 14
street : 140
3 : 222
7 : 115
10 : 449
blair : 98
witch : 108
2 : 439
crow : 55
9 : 75
salvation : 14
4 : 190
stir : 27
echoes : 31
8 : 140
happy : 215
bastard : 46
quick : 139
damn : 88
y2k : 4
bug : 81
starring : 184
jamie : 42
lee : 266
curtis : 37
another : 1121
baldwin : 89
brother : 268
william : 206
time : 2411
story : 2169
regarding : 31
crew : 214
tugboat : 2
comes : 733
across : 221
deserted : 21
russian : 68
tech : 29
ship : 264
kick : 52
power : 238
within : 227
gore : 110
bringing : 81
action : 1172
sequences : 293
virus : 77
empty : 67
flash : 62
substance : 73
middle : 222
nowhere : 98
origin : 8
pink : 24
flashy : 41
thing : 809
hit : 285
mir : 7
course : 648
donald : 38
sutherland : 39
stumbling : 15
around : 903
drunkenly : 2
hey : 70
let : 425
robots : 27
acting : 695
average : 119
likes : 99
likely : 164
work : 1020
halloween : 60
h20 : 7
wasted : 118
real : 915
star : 761
stan : 12
winston : 4
robot : 45
design : 87
schnazzy : 1
cgi : 50
occasional : 62
shot : 348
picking : 28
brain : 93
body : 269
parts : 207
turn : 363
otherwise : 156
much : 2049
sunken : 4
jaded : 12
viewer : 218
thankful : 7
invention : 11
timex : 1
indiglo : 1
based : 389
late : 238
1960 : 22
television : 220
show : 741
name : 392
mod : 17
squad : 40
tells : 255
tale : 216
three : 695
reformed : 7
criminals : 35
employ : 16
police : 241
undercover : 28
however : 989
wrong : 385
evidence : 66
gets : 865
stolen : 58
immediately : 163
suspicion : 19
ads : 33
cuts : 60
claire : 70
dane : 5
nice : 344
hair : 109
cute : 134
outfits : 21
car : 321
chases : 50
stuff : 208
blowing : 26
sounds : 133
first : 1836
fifteen : 64
quickly : 255
becomes : 526
apparent : 100
certainly : 361
slick : 39
looking : 501
complete : 197
costumes : 71
enough : 910
best : 1333
described : 57
cross : 111
hour : 355
long : 836
cop : 208
stretched : 19      

View Code

     https://wordart.com/create

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

  仔細研究這個詞雲,會發現它遠非完美,還有很大的改進空間。是以,我們不妨做進一步的嘗試。

  進一步過濾:我們應當剔除包含數字元号和姓名的那些單詞。這時,可以借助NLTK的names語料庫。此外,對于在所有語料庫中僅出現一次的那些單詞,我們也可以置之不理,因為它們不大可能提供足夠有價值的資訊。

  使用更好的度量名額:詞頻和逆文檔頻率(The Term Frequency-Inverse Document Frequency,TF-IDF)看起來是一個不錯的選擇。

  度量名額TF-IDF可以通過對語料庫中的單詞進行排名并據此賦予這些單詞相應的權重。這個權重的值與單詞在特定文檔中出現的次數即詞頻成正比。同時,它還與語料庫中含有該單詞的文檔數量成反比,即逆文檔頻率。TF-IDF的值為詞頻和逆文檔頻率之積。如果需要自己動手實作TF-IDF,那麼還必須考慮對數标定處理。幸運的是,實際上我們根本無需考慮這些實作方面的細節,因為scikit-learn已經為我們準備好了一個TfidfVectorizer類,它有效地實作了TF-IDF。該類能夠生成一個稀疏的SciPy矩陣,用術語來說就是一個詞條-文檔矩陣,這個矩陣存放的是單詞和文檔的每種可能組合的TF-IDF值。是以,對于一個含有2 000個文檔和25 000個不重複的詞的語料庫,可以得到一個2 000×25 000的矩陣。由于大部分矩陣值都為0,我們将它作為一個稀疏矩陣來處理比較省勁。對每個單詞來說,其最終的排名權重可以通過對其TF-IDF值求和來得到。

下面我們通過isalpha()方法和姓名語料庫來改善過濾效果,代碼如下。

1 all_names = set([name.lower() for name in names.words()])
2 
3 def isStopWord(word):
4     return (word in sw or word in punctuation) or not word.isalpha() or word in all_names      

  下面建立一個NLTK的FreqDist類,進而過濾掉那些隻出現一次的單詞。對于TfidfVectorizer類,需要為其提供一個字元串清單來指出語料庫中的各個文檔。

  下面建立這個清單,代碼如下。

1 for fid in movie_reviews.fileids():
2     texts.append(" ".join([w.lower() for w in movie_reviews.words(fid) if not isStopWord(w.lower()) and words[w.lower()]      

  建立向量化程式,為了保險起見,令其忽略停用詞。

1 vectorizer = TfidfVectorizer(stop_words='english')      

  建立稀疏的詞條-文檔矩陣。

1 matrix = vectorizer.fit_transform(texts)      

  為每個單詞的TF-IDF值求和并将結果存放到NumPy數組中。

1 sums = np.array(matrix.sum(axis=0)).ravel()      

  下面通過單詞的排名權值來建立一個Pandas DataFrame并進行相應的排序,代碼如下。

1 ranks = []
2 
3 for word, val in zip(vectorizer.get_feature_names(), sums):
4     ranks.append((word, val))
5 
6 df = pd.DataFrame(ranks, columns=["term", "tfidf"])
7 df = df.sort_values(['tfidf'])
8 print(df.head())      

  排名最低的值将被輸出,同時供将來過濾用。

term    tfidf
19963  superintendent  0.03035
8736            greys  0.03035
14010           ology  0.03035
2406          briefer  0.03035
2791      cannibalize  0.03035      
《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體
《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體
matter : 10.160156320157853
review : 10.162109208095815
seeing : 10.193962242951153
jokes : 10.195055387739588
past : 10.229789978743266
romantic : 10.270767948140588
directed : 10.27679275085064
start : 10.302358509215921
finally : 10.315385095902199
video : 10.356897657857315
despite : 10.36356758711268
ship : 10.370281211670585
beautiful : 10.415601266078719
scream : 10.421970655899635
sequence : 10.461140540373234
supposed : 10.473608248283002
shot : 10.497822532176006
face : 10.520647846526677
turn : 10.535466043788666
lives : 10.536265259335323
later : 10.536596993112912
tell : 10.54178804022045
camera : 10.580870634146848
works : 10.585001927065935
children : 10.59229934724306
live : 10.658879764040353
daughter : 10.685408819519905
earth : 10.6855987888017
mr : 10.711280266859085
car : 10.715492238654587
believe : 10.724994487616465
maybe : 10.738295943695265
person : 10.766043701757003
book : 10.799070874951035
worst : 10.801808893863994
hand : 10.815936702218032
named : 10.818013961831461
game : 10.86383795127332
fight : 10.865544776692566
use : 10.88431817114859
used : 10.955534955009918
killer : 11.000641030461054
certainly : 11.001525429842374
begins : 11.070728771014815
perfect : 11.096242361915083
relationship : 11.102758849863765
said : 11.108251601369561
nice : 11.124424984182713
days : 11.138293311572346
kids : 11.181482360800292
called : 11.192445668887236
run : 11.198723318188565
playing : 11.216003589219902
final : 11.223074577571513
tries : 11.242871986273729
unfortunately : 11.295825206128804
group : 11.326924605147562
comic : 11.389992817954504
left : 11.438340356187926
entire : 11.444592282847251
idea : 11.461929302207174
based : 11.487214286939588
head : 11.516296149111216
wrong : 11.562368605644979
second : 11.585319233830582
summer : 11.586686728126798
shows : 11.63522507695588
main : 11.660671883754478
soon : 11.711290550319069
true : 11.754181091867876
turns : 11.821623440558202
getting : 11.874446133551853
human : 11.899923970603947
problem : 11.99620702280271
written : 12.006014424193982
hour : 12.018041625879004
different : 12.151447465665578
boy : 12.202291415418031
performances : 12.239403934569973
house : 12.251961733491308
simply : 12.29153134772739
war : 12.297910070957098
mind : 12.324864356668593
small : 12.327723933187466
especially : 12.352783302524191
rest : 12.359092724325766
tv : 12.368538157058103
lost : 12.399034233379663
completely : 12.435026821722477
looks : 12.4500832891327
humor : 12.480741394166033
line : 12.531463602753462
reason : 12.549615155979115
dead : 12.550701937661323
friend : 12.554066106444134
let : 12.557077785028916
thought : 12.650011584913317
stars : 12.688790737853829
couple : 12.731565830451245
alien : 12.811802155159485
moments : 12.890094530509302
evil : 12.90918008264871
wants : 12.91523366454186
friends : 12.971455692528544
night : 12.972118832796
mother : 13.069867009873525
given : 13.163657271730541
ending : 13.241085606272085
play : 13.241256041448459
feel : 13.260355813064143
gives : 13.541933025342574
got : 13.58120683575867
watching : 13.633462479391907
death : 13.638264772375894
looking : 13.717090584113414
girl : 13.728552228886974
instead : 13.774615981791037
probably : 13.808813918690175
city : 13.842578473593091
school : 13.897593833405203
father : 14.066422069009711
music : 14.075762648197905
help : 14.120498729117202
sure : 14.145480680268529
dialogue : 14.23176869122043
kind : 14.399967422476037
black : 14.447741822146453
actor : 14.522615872729146
sense : 14.629745462015816
want : 14.725766439030554
pretty : 14.809940495890388
making : 14.817117131361535
series : 14.821081643716946
set : 14.887819969400011
half : 14.89251185190553
money : 14.901355104392845
bit : 14.934268735728764
home : 14.976582330659667
place : 15.049919713647064
trying : 15.114785990368087
times : 15.118763478807493
sex : 15.166419440553032
american : 15.359810523477407
hard : 15.454208298004339
picture : 15.506551578323716
woman : 15.639843619779164
hollywood : 15.650704478670258
horror : 15.688047135511049
far : 15.758603032319007
watch : 15.781648059164944
fun : 15.799106034203923
special : 15.894929367222112
course : 15.931964229777236
away : 15.944636479597987
takes : 15.954305763102612
men : 16.036233030752417
wife : 16.103810528049053
interesting : 16.110566312034386
screen : 16.330798169836488
goes : 16.355214344649127
minutes : 16.578665989245824
point : 16.593231934007573
quite : 16.743903021148146
lot : 16.7556321332947
comes : 16.945112986149024
high : 16.96333443299414
day : 17.469931907378594
young : 17.61907491805685
come : 17.710951023055475
plays : 17.825218944178573
actors : 17.841139061718554
acting : 18.056556176317212
effects : 18.156452985282307
fact : 18.358966964131444
family : 18.436351324270554
cast : 18.462652990794695
right : 18.51721470872539
look : 18.709286795233268
original : 18.815142636940738
played : 18.96169328195863
years : 19.058321835992686
long : 19.063431217630267
actually : 19.081359841119173
thing : 19.347634515439985
script : 19.466362068961164
old : 19.68484647913068
things : 19.711840751606182
gets : 19.789969444538826
think : 19.79997034212867
role : 19.829843863051224
performance : 19.991860531957602
better : 20.069030711347395
audience : 20.229377054374694
going : 20.384999917030928
year : 20.404754669356507
seen : 20.741715274906582
real : 20.782565933126254
makes : 20.916315796004714
work : 21.48974389558135
funny : 22.184963867355552
world : 22.430195409736793
end : 22.501145434405117
comedy : 22.724158274326264
big : 23.394018499746736
director : 23.689769490697007
great : 24.489611034104875
scenes : 25.243560471583756
know : 25.25908281181752
new : 25.424409388545484
movies : 25.46489805929262
best : 25.793314078809164
scene : 26.65609949406872
man : 27.271016365028732
people : 27.772749971079577
action : 27.8317318022786
little : 27.912251658495467
make : 28.36244923373348
films : 29.081817882359086
bad : 29.1633844710237
plot : 29.81049966927315
really : 30.211353157494234
life : 30.844185813234848
characters : 33.204457851658944
character : 33.73419369933767
story : 36.69429364590541
time : 36.76869565644592
good : 38.945456240101606
like : 49.087665729852674
movie : 80.3332102380712
film : 109.34008874939663      

接下來,我們輸出排名靠前的單詞,這樣就可以利用Wordle網站生成如圖9-5所示的詞雲了。

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

  令人遺憾的是,我們必須親自運作代碼,才能看到上面詞雲中的不同色彩。相較于單調的詞頻而言,TF-IDF度量名額更富于變化,是以我們就能得到更加豐富的色彩。此外,這樣還能使得雲霧中的單詞看起來聯系更加緊密。下列代碼摘自本書代碼包中的ch-09.ipynb檔案。

1 from nltk import data
 2 data.path.append(r'D:\download\nltk_data') # 這裡的路徑需要換成自己資料檔案下載下傳的路徑
 3 from nltk.corpus import movie_reviews
 4 from nltk.corpus import stopwords
 5 from nltk.corpus import names
 6 from nltk import FreqDist
 7 from sklearn.feature_extraction.text import TfidfVectorizer
 8 import itertools
 9 import pandas as pd
10 import numpy as np
11 import string
12 
13 sw = set(stopwords.words('english'))
14 punctuation = set(string.punctuation)
15 all_names = set([name.lower() for name in names.words()])
16 
17 def isStopWord(word):
18     return (word in sw or word in punctuation) or not word.isalpha() or word in all_names
19 
20 review_words = movie_reviews.words()
21 filtered = [w.lower() for w in review_words if not isStopWord(w.lower())]
22 
23 words = FreqDist(filtered)
24 
25 texts = []
26 
27 for fid in movie_reviews.fileids():
28     texts.append(" ".join([w.lower() for w in movie_reviews.words(fid) if not isStopWord(w.lower()) and words[w.lower()] > 1]))
29 
30 vectorizer = TfidfVectorizer(stop_words='english')
31 matrix = vectorizer.fit_transform(texts)
32 sums = np.array(matrix.sum(axis=0)).ravel()
33 
34 ranks = []
35 
36 for word, val in zip(vectorizer.get_feature_names(), sums):
37     ranks.append((word, val))
38 
39 df = pd.DataFrame(ranks, columns=["term", "tfidf"])
40 df = df.sort_values(['tfidf'])
41 print(df.head())
42 
43 N = int(.01 * len(df))
44 df = df.tail(N)
45 
46 for term, tfidf in zip(df["term"].values, df["tfidf"].values):
47     print(term, ":", tfidf)      

9.9社交網絡分析

   所謂社交網絡分析,實際上就是利用網絡理論來研究社會關系,其中,網絡中的節點代表的是網絡中的參與者。節點之間的連線代表的是參與者之間的互相關系。

  嚴格來講,這應該稱為一個圖。本書僅介紹如何利用流行的Python庫NetworkX來分析簡單的圖。這裡,通過Python庫matplotlib來對這些網絡圖進行可視化。

  為了安裝NetworkX,可以使用如下指令。

1 $ pip3 install networkx      

  關于Networkx,也可以參照這篇文章《資料分析實戰-托馬茲.卓巴斯》讀書筆記第8章--圖(NetworkX、Gephi)修訂版

  下面導入NetworkX并指定一個簡單的别名。

1 Import networkx as nx      

  NetworkX提供了許多示例圖,下面将其列出,代碼如下。

1 print([s for s in dir(nx) if s.endswith('graph')])      

  下面導入Davis Southern women圖并繪制出各個節點的度的柱狀圖,代碼如下。

1 G = nx.davis_southern_women_graph()
2 plt.hist(list(nx.degree(G).values())) #AttributeError: 'DegreeView' object has no attribute 'values',去掉values即可,邀月注。
3 plt.show()      

  最終得到如圖9-6所示的柱狀圖。

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

  下面來繪制帶有節點标簽的網絡圖,所需指令如下。

1 plt.figure(figsize=(8,8))
2 pos = nx.spring_layout(G)
3 nx.draw(G, node_size=10)
4 nx.draw_networkx_labels(G, pos)
5 plt.show()      

  得到的圖形如圖9-7所示。

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

     圖像的可讀性可以再優化:

1 plt.figure(figsize=(8,8))
2 print(help(nx.spring_layout))
3 pos = nx.spring_layout(G, scale=2)
4 
5 nx.draw(G)
6 nx.draw_networkx_labels(G, pos)
7 plt.show()      

  效果如下圖:

《python資料分析(第2版)-阿曼多.凡丹戈》讀書筆記第9章-分析文本資料和社交媒體

  雖然這個例子很短,但是對于簡單體驗NetworkX的功能特性來說已經足夠了。我們可以借助NetworkX來探索、可觀化和分析社交媒體網絡,如Twitter、Facebook以及LinkedIn等。當然,本節講述的方法不僅适用于社交網絡,實際上,所有類似的網絡圖都可以使用NetworkX來處理。

9.10小結

   本章講述了文本分析方面的知識。首先,我們給出了文本分析中剔除停用詞的一個最佳實踐。

  介紹詞袋模型時,我們還建立了一個詞袋來存放文檔内出現的單詞。此外,我們還根據所有的單詞計數,為每個文檔生成了一個特征向量。

  分類算法是機器學習算法中的一種,用來給特定事物進行分類。樸素貝葉斯分類是一種機率算法,它基于機率與數理統計中的貝葉斯定理。這裡,貝葉斯定理指出,後驗機率與先驗機率和相似度之積成正比。

  第10章将對機器學習進行更深入的介紹,因為這是一個充滿了無限希望的研究領域。也許有一天,它将完全替代人類勞動力。屆時,以氣象資料為例,來說明Python機器學習庫scikit-learn的具體使用方法。

 第9章完。

随書源碼官方下載下傳:

https://www.ptpress.com.cn/shopping/buy?bookId=bae24ecb-a1a1-41c7-be7c-d913b163c111

需要登入後免費下載下傳。

邀月注:本文版權由邀月和部落格園共同所有,轉載請注明出處。

助人等于自助!  [email protected]