Makzan
Makzan

我管理世界職業技能競賽之網站技術項目、舉辦本地設計與開發賽事、開課分享技術心得。一個用網頁來表達自己的作家。

Python 如何數一篇文章中最常出現的英文詞語

日常中,我們可能需要統計一篇文章最常出現的英文詞語,這時可以使用內置的 Counter 工具。

日常中,我們可能需要統計一篇文章最常出現的英文詞語,這時可以使用內置的 Counter 工具。

Counter 的用法如下:

from collections import Counter    
most_words = Counter(words).most_common(20)

以下以愛麗斯夢遊仙境為例子。我們從 Gutenberg 網站取得愛麗斯的第一段文字,放到一個純文字檔案中,命名為 alice-chapter-1.txt,放到我們的項目資料夾中。

接著在 Python 編程環境中,我們在同一項目資料夾中建立一個 Jupyter Notebook 互動編程環境。

載入文字檔案

with open("alice-chapter-1.txt") as file_obj:
  chapter_1 = file_obj.read()

有目標文字,數一數一段文字中出現了多少次此文字

chapter_1.count("Alice")
# result: 28

可見,第一章中出現 Alice 字詞共 28 次。

使用 split 把文字拆成列表

有些時候,上述方法不夠用。我們會將字串分柝成列表。例如當我們需要使用 Counter 來數一數文字中出現最多的是哪幾個字時,就需要列表。又或者進一步分析文字,例如進行 NLP 語言分析及預計下一個文字時,也需要先將字串分柝成列表再進行計算。

將文字分柝成列表,我們可以使用 split。

我們若直接將 chapter_1 按空白來分柝,得出的結果不如 28。

words = chapter_1.split()
words.count("Alice")
# result: 20

注:split 可以按我們提供的文字進行分割。例如 "2022-01-02".split("-") 可以得出 ['2022', '01', '02']等。而沒有提供參數時,則是用空白符號(包括跳行)作為分割字符。

為何列表數會數少了?因為我們把文字用空格分柝成列表時,會包括了標點符號等,所以 "Alice" 不同於 "Alice." 亦不同於 "Alice's",但字串不會這樣考慮,所以文字若不經過預處理,使用空格分柝的成果未必如意。

預處理 chapter_1 文字

一般的文字預處理分為以下步驟。

  1. 把文字轉為細楷
  2. 移除 's 的所有格
  3. 移除標點符號

轉細楷,可以使用 lower 函數,移除特定文字,可以使用 replace函數。標點符號,通用的可以在 string.punctuation 中找到,當中包括以下標點符號:

!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~

英文文本亦會常出現文學引號而非上述程序用的引號,亦會有破折號出現,所以我一般會加上以下六個符號,分別是雙引號、單引號、短破折號(En Dash)及長破折號(Em Dash): “”‘’–—

如是者,以下代碼將 chapter_1 按上述步驟處理,處理後再 split 分柝,可以數到 28 個 "alice" 字詞。

# 把文字轉為細楷
chapter_1 = chapter_1.lower()
# 移除 's 的所有格
chapter_1 = chapter_1.replace("'s'","").replace("’s","")
# 移除標點符號
import string
punctuations = string.punctuation + "“”‘’–—"
for punctuation in punctuations:
    chapter_1 = chapter_1.replace(punctuation, "")

# 分柝及數 "alice"
words = chapter_1.split()
words.count("alice")
# result: 28

使用 Counter 數數列表中出現最多次數的字詞

這麼大費周章得出原本 chapter_1.count("Alice") 就可以得出的數字,用法當然不止於此。變成列表後,其中一個用法是使用 Counter 數數最常出現的字詞有哪些。

from collections import Counter 
most_words = Counter(words).most_common(20)
print(most_words)
# Result: [('the', 90), ('she', 79), ('to', 74), ('and', 65), ('it', 65), ('was', 53), ('a', 52), ('of', 41), ('i', 30), ('alice', 28), ('that', 27), ('her', 26), ('in', 26), ('down', 23), ('very', 23), ('for', 21), ('had', 20), ('but', 20), ('you', 18), ('not', 16)]

使用 Pandas 繪製棒型圖

就上述的結果,我們可以進一步使用 Pandas 及 Matplotlib 來繪圖。

首先,我們將上述的結果轉為 Pandas 的 DataFrame 類型。

於 Jupyter Notebook 中使用 DataFrame 有一個優勢,就是可以用較易閱讀的方式打印出數據內容。

import pandas as pd
df = pd.DataFrame(most_words, columns=("Word","Count"))
df # 在 Jupyter Notebook 中印出 DataFrame 數據

得出以下數據:

為準備繪圖,我們將當中的 Word 欄位設定為 Index。

df.set_index("Word", inplace=True)
df

我們將會使用 barh 繪製橫向棒型圖。橫向棒型圖的一個特點是排序會上下倒轉,所以我們會先做一次排序。

df.sort_values(by="Count", ascending=True, inplace=True)
df

為標記 "alice" 的所在位置,我們設定一個 Color 欄位,將除了 alice 以外的都設定為淺灰色。

DataFrame 的特性是隨時可以加欄位,而我們要設定特定一格,可以用 loc[index, column] 來按 index 取得那行記錄,再按 column 定位一格。

df["Color"] = "lightgrey"
df.loc["alice", "Color"] = "blue"
df

經過上述的資料準備,我們可以就 Count 欄位畫出棒型圖,並設定用 Color 欄位作為顏色。

df["Count"].plot(kind="barh", color=df["Color"], figsize=(8,8), title="Count of Alice in chapter 1")

後續:將文字預處理定義為函數重用

上述的文字預處理,於其他文字亦有使用需要。我們可以將當中的處理抽取定義成函數。

def process_text(content):
    import string    
    content = content.lower()
    content = content.replace("'s'","").replace("’s","")
    punctuations = string.punctuation + "“”‘’–—"
    for punctuation in punctuations:
        content = content.replace(punctuation, "")
    return content

process_text("Test. This is Thomas' toy. That’s Alice's toy.")

舉一反三:對網絡文字數出現最多的文字

套用上述的處理及 Counter 用法,我們可以應用於其他網頁上的文章,例如我以一篇澳門光影節新聞為例子:

https://www.gcs.gov.mo/detail/en/N21LdGKSOM?2

使用 Requests 及 BeautifulSoup 爬取網站內容成為內容結構樹,並按此網站的內容結構找出主文章文字,並按上述方式處理及數數。

import requests
from bs4 import BeautifulSoup
from collections import Counter 

url = "https://www.gcs.gov.mo/detail/en/N21LdGKSOM?2"

# 下載網站內容
res = requests.get(url)

# 把網站內容分析成內容結構樹
soup = BeautifulSoup(res.text)

# 按網站的內容結構,主內容位置 class=mainBody 的 class=asideBody 元素內
element = soup.select_one(".mainBody .asideBody")

# 將主內容元素的文章內容處理
article = element.text
article = process_text(article)

# 處理後數出最常出現的 40 個字詞
words = article.split()
Counter(words).most_common(40)

中文如何?

以上做法,只適合用空格來隔開文字的外文,並不適合中文,因為中文不能通過簡單的 split 來達成。 中文需要其他庫來達成分詞,例如「結巴」分詞庫。

稍後我們在另一篇介紹中文數最常出現字詞的範例。

— 麥誠 Makzan,2022-01-08。


我是麥誠軒(Makzan),除了正職外,平常我要麼辦本地賽與辦世界賽,要麼任教編程與網站開發的在職培訓。現正轉型將面授培訓內容寫成電子書、網上教材等,至今撰寫了 7 本書, 2 個視頻教學課程。

如果我的文章有價值,請左下角 👍🏻按讚支持,或訂閱贊助我持續創作及分享。


麥誠 Makzan


CC BY-NC-ND 2.0 版权声明

喜欢我的文章吗?
别忘了给点支持与赞赏,让我知道创作的路上有你陪伴。

加载中…
加载中…

发布评论