天天看點

Python練手小程式—從摘要中提取關鍵詞

在GitHub上發現一些很有意思的項目,由于本人作為Python的初學者,程式設計代碼能力相對薄弱,為了加強Python的學習,特此利用前輩們的學習知識成果,自己去親自實作。

來源:GitHub

Python練手小程式項目位址:https://github.com/Show-Me-the-Code/python

寫作日期:2019.12.14

今天練習第0006題,題目如下:

Python練手小程式—從摘要中提取關鍵詞

這個題目,打算改變一下資料集,我打算利用kaggle上的NIPS-Papers的paper.csv檔案,找出每個英文摘要中最重要的詞,也就是所謂的提取關鍵詞。

Python練手小程式—從摘要中提取關鍵詞

下面通過文本處理的幾個步驟一一展開:

  • 1、導入資料集

    首先資料集paper.csv檔案,我們主要分析摘要這個字段,在分析過程中其他字段先不處理。

# 利用pandas加載資料集
import pandas as pd
dataset = pd.read_csv('nips-papers/papers.csv')
# 檢視資料集
dataset.head(5)
           
Python練手小程式—從摘要中提取關鍵詞
  • 2、文本探索

    在進行預處理之前,我們先根據每個摘要的字數,快速浏覽一下整體摘要的情況,主要看下摘要的平均字數,總摘要數,以及摘要的最小字數和最大字數等資訊。

# 對每個文章摘要計算字數
dataset['word_count'] = dataset['abstract'].apply(lambda x: len(str(x).split()))
# 檢視下部分資料
dataset[['abstract', 'word_count']].head(5)
# 統計摘要字數分布情況
dataset['word_count'].describe()
           
Python練手小程式—從摘要中提取關鍵詞

可以看出一共有7241篇摘要,每個摘要平均字數是80.5,摘要字數範圍從最小值2到最大值313.

下面我們看下最常見和最不常見的單詞

# ' '.join(dataset['abstract']) 這個動作是先把所有摘要都拼接在一起
freq = pd.Series(' '.join(dataset['abstract']).split()).value_counts()
# 檢視摘要中最常見的單詞
print(freq[:30])
# 檢視摘要中最不常見的單詞
print(freq[-30:])
           
  • 3、文本預處理
    • 1)單詞标準化處理,用到nltk,pip install nltk

      這裡的标準化,指的通過删除字尾來歸一化文本,比如learn, learned, learning, learner,歸一化這些轉換為單個标準化版本:learn

    • 2)删除停用詞處理
    • 3)清洗語料中的特殊字元及數字等
import nltk
# 要提前下載下傳,執行:nltk.download('stopwords')和nltk.download('wordnet')
from nltk.corpus import stopwords
from nltk.stem.porter import PorterStemmer
from nltk.tokenize import RegexpTokenizer
from nltk.stem.wordnet import WordNetLemmatizer
# nltk庫中的停用詞
stop_words = set(stopwords.words("english"))
# 建立自定義停用詞
new_words =  ["using", "show", "result", "large", "also", "iv", "one", "two", "new", "previously", "shown"]
# 合并停用詞
stop_words = stop_words.union(new_words)
# 存預處理後的語料資料,例子corpus = ['word1 word2', 'word3 word4', ……]
corpus = []
for i in range(len(dataset)):
    # 删除标點符号,用空格代替
    text = re.sub('[^a-zA-Z]', ' ', dataset['abstract'][i])
    # 轉小寫字母
    text = text.lower()
    # 删除多餘的空格
    text = re.sub('(\d|\W)+', ' ', text)
    text = text.split()
    # 單詞歸一化
    lem = WordNetLemmatizer()
    text = [lem.lemmatize(word) for word in text if word not in stop_words] 
    text = ' '.join(text)
    corpus.append(text)  
           
  • 資料探索

主要是文本資料的可視化操作,利用wordcloud生成詞雲,pip install wordcloud

from PIL import Image
from wordcloud import WordCloud, STOPWORDS,ImageColorGenerator
import matplotlib.pyplot as plt
%matplotlib inline
wordcloud = WordCloud(
		background_color = 'white',
    	stopwords = stop_words,
    	max_words = 100,
    	max_font_size = 50,
    	random_state = 42
).generate(str(corpus))
# 畫圖
fig = plt.figure(1)
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
fig.savefig('word.png', dpi=900)
           
Python練手小程式—從摘要中提取關鍵詞
  • 文本特征提取

我們使用skearn中的CountVectoriser來标記文本,并建構已知單詞的詞彙表。

from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(max_df=0.8, stop_words=stop_words, max_features=10000, ngram_range=(1,3))
# 建構詞彙表,X.shape: (7241, 10000),7241:代表摘要數,10000:代表單詞數
bag_of_words = cv.fit_transform(corpus)
# 檢視前10個詞彙
print(list(cv.vocabulary_.keys())[:10])
# 檢視每個單詞對應的唯一序号
print(list(cv.vocabulary_.items())[:10])
# 每個單詞在不同文檔中 的 文檔數量, 傳回一個矩陣,類似:matrix([[1, 3, 1, 2, 1, 1]], dtype=int64)
sum_words = bag_of_words.sum(axis=0)
# 每個單詞在不同文檔中 的 文檔數量,傳回(word, freq:有多少個文檔中含有word)
words_freq = [(word, sum_words[0, idx]) for word, idx in cv.vocabulary_.items()]
words_freq = sorted(words_freq, key=lambda x: x[1], reverse=True)
# 擷取不同摘要中的高頻單詞前20個,轉化為dataframe資料類型,然後畫圖
top_words = words_freq[:20]
top_df = pd.DataFrame(top_words)
top_df.columns = ['Word', 'Freq']
import seaborn as sns
sns.set(rc={'figure.figsize':(13, 8)})
g = sns.barplot(x='Word', y='Freq', data=top_df)
g.set_xticklabels(g.get_xticklabels(), rotation=30)
           
Python練手小程式—從摘要中提取關鍵詞

countVectoriser獲得的單詞計數的不足之處在于,大量的某些常用單詞可能會稀釋語料庫中更多上下文特定單詞的影響。這被TF-IDF克服,TF-IDF是詞頻分數,它突出顯示對上下文更重要的單詞,而不是那些在文檔中頻繁出現的單詞。

from sklearn.feature_extraction.text import TfidfTransformer
tfidf_transformer = TfidfTransformer(smooth_idf=True, use_idf=True)
# bag_of_words是上面的詞頻數
tfidf_transformer.fit(bag_of_words)
# 擷取特征名稱,上面限定的10000個
feature_names = cv.get_feature_names()
# 針對某個摘要提取,tfidf向量,是稀疏資料類型:scipy.sparse.csr.csr_matrix
doc = corpus[534] # 随便找個摘要,本文隻是單純看一個摘要的tf-idf值
tf_idf_vector = tfidf_transformer.transform(cv.transform([doc]))
           

根據TF-IDF分數,我們提取分數最高的單詞來擷取文檔的關鍵字

from scipy.sparse import coo_matrix
# 資料格式轉換:scipy.sparse.csr.csr_matrix ——> scipy.sparse.coo.coo_matrix
coo_matrix = tf_idf_vector.tocoo()
# coo_matrix.col表示稀疏資料不為0時對應的索引,coo_matrix.data表示稀疏資料不為0時索引下的取值
tuples = zip(coo_matrix.col, coo_matrix.data)
sorted_items = sorted(tuples, key=lambda x: (x[1], x[0]), reverse=True)

# 擷取tf-idf前10個最大值
sorted_items = sorted_items[:10]
score_vals = []
feature_vals = []

# idx:索引 和 tf-idf:tf-idf值
for idx, score in sorted_items:
    score_vals.append(round(score, 3))
    feature_vals.append(feature_names[idx])
# 把tf-idf取值最大的前10個,擷取其特征名與對應的tf-idf值,放入results字典中
results = {}
for idx in range(len(feature_vals)):
    results[feature_vals[idx]] = score_vals[idx]
# 結果列印出來
print('\nAbstract:')
print(doc)
print("\nkeywords:")
for k in results():
    print(k, results[k])
           

寫在最後,本文主要根據摘要中單詞的tf-idf的前10個最大值,确定其為關鍵詞。本文隻給了某一個摘要,去尋找tf-idf前10個最大,大家可以把所有的摘要都一一擷取關鍵詞。

文中涉及的知識點有

pandas的簡單實用

sklearn中的TfidfTransformer和CountVectorizer的實用

詞雲繪圖wordcloud

等,有些庫需要自己安裝,相對比較簡單,pip install xxx即可,一般都不會報錯。今天寫到這裡,繼續加油!

參考資料: 使用NLP從文章中自動提取關鍵字