iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0
AI & Data

文理組人都能上手的入門 NLP(自然語言處理)系列 第 7

[Day 7] 時間都去哪了?資料前處理:合格社畜的工作方式-正規表達式(Regular Expression)

  • 分享至 

  • xImage
  •  

  嗨大家!今天的開頭是問答遊戲!請問在機器學習運作的流程中,通常哪一個階段最耗時間呢?

(A) 資料前處理
(B) 機器訓練
(C) 參數調整
(D) 結果評估


  答案就在今天的標題裡面。答案是資料前處理!因為我們從各處收集來的文字資料來自不同的個體,每個人都可能有一些寫作風格差異造成的不同。而根據目標不同,未處理的資料裡面一定會有我們需要處理掉的雜質。再加上網路資料裡面流行用語興盛,也不像傳統寫作有比較多格式或書寫限制,通常需要我們花更多時間去整理,資料前處理(包含特徵標記)絕對是整個機器學習過程中最耗時間的階段。

  既然都知道前處理是最花時間的事情,我們當然不能草草帶過囉。接下來幾天會告訴大家各種資料前處理的眉眉角角,盡量透過一些聰明的方法來提高資料處理的成效。

  之前介紹python語法的時候說過,我們在操作python的過程就像是一個公司內部的運作一樣。而社畜python作為資深老員工又是怎麼讓自己好好完成工作的呢?第一步就是用自己良好的統整能力找出隱藏在各處的規律!為什麼會需要找規律呢?舉個例說明一下,日期的表示方法常常因人而異,但為了資料處理方便,我們通常會想要統一一種表示方法就好。這種時候如果把他們一個一個找出來再手工修改真的太浪費時間了,應該要有更快速的方法可以解決才對。如果想把澳洲的日期格式「日期/月份/年份」改成美國的日期格式「月份/日期/年份」,我們的第一個想法會是什麼呢?把月份跟日期調換就好了嘛。但是電腦又不認得誰是日期誰是月份,我們應該要怎麼做?這個時候就是正規表達式Regular Expression)出場的時刻了。

  正規表達式是用來幫助我們透過規則搜尋字串的好東西,雖然根據使用的地方不同可能會有些微的差別,但基本上都是大同小異的。在正規表達式中,對字串裡面不同型態的物件都有規定的表達方式。例如十進位數字會用\d表示(取digital的首字母),字符會用\w表示等。我們可以透過正規表達式的輔助,去完成用手動進行要花費很多時間但其實有規則可循的任務。這邊先提供常用的正規表達式統整表格,接下來再透過實作的方式帶大家熟悉他們的使用。

常見字符指令

Regex 用途
\d 十進位的數字字符(0~9)
\D 非十進位數字的字符
\s 空白字符(包含tab及換行等)
\S 非空白字符
\w 字母跟數字字符
\W 非字母跟數字的字符
\n 換行符號(new line)
\t tab
. \n以外的所有字符

數量限制指令(counter)

Regex 用途 實例
* 0或更多(即可有可無) nt*m表示n跟m中間沒有t或是n跟m中間只有t(不管幾個t)的字串
+ 1或更多(即至少1個) nt+表示n後面接至少一個t的字串
? 0或1 nt?m表示nm或ntm
{n} n個 ap{3}表示appp
{n,} n或n個以上 ap{3,}表示a後面至少連續3個p
{n,m} n到m個 ap{1,3}表示ap, app, appp

位置限制指令(anchor)

Regex 用途 實例
^ 單行字串的開頭或多行字串中每一行的開頭 ^The表示以The開頭的所有字串
\A 單行跟多行字串的開頭 \AThe表示以The開頭的字串
$ 字串的結尾或多行字串中每一行的結尾 head\.$表示以head.結尾的所有字串
\b 詞分界 \bred\b表示red這個詞(可以排除Fred等其他包含red在內的詞)
\B 非詞分界 \Bred\B表示包含red在內的詞(可以排除red)
\< 詞的開頭 \<un表示un開頭的詞
\> 詞的結尾 ist\>表示ist結尾的詞

數量限制指令跟位置限制指令都有用到特殊符號,所以當我們真的要指涉這個特殊符號的時候,必須加上逃脫字元\ 例如:想在字串當中尋找半形問號時,應該要打成\?才可以避免python誤以為我們要限制什麼東西的數量。

群組、範圍指令

Regex 用途 實例
() 群組 s(ang)表示字串sang中的ang為一個群組
\n 第n個群組(register) 上例中之ang可以\\1表示
` `
[] 對裡面的符號組有分割作用 [cats]表示小寫c、a、t或s
[^] 非範圍標示內之字符 s[^ia]ng表示s跟ng之間非i或a之字串
- 用來表示區間 如下所示
[0-9] 十進位數字 等同\d
[A-Z] 大寫字母
[a-z] 小寫字母
[A-z] 所有字母 等同[A-Za-z]

Operator Precedence Hierarchy(運算子優先序)

  在使用上面這些指令的時候,除了要記得分辨逃脫字元的使用時機以外,也要注意運算子優先序的問題。當正規表達式寫得比較複雜的時候,會有各種不一樣的指令混在一起。

小括號( ) > 數量限制指令 > 字符組 & 位置限制指令 > 或 |

  根據上面這個排序,當一個正規表達式長得像the*這樣的時候,因為數量限制指令優先於字符組,他尋找的是th後面沒有e或出現1個以上e的字串,而不是重複很多次的the。如果想要找到重複很多次the的字串,應該要用(the)+才對。;同理,因為字符組優先於|apple|banana要找的是apple或banana,而不是appleanana或applbanana。

Greedy Mode (貪婪模式)

  使用正規表達式的時候,除了需要注意上面提到的優先順序問題以外,還要注意避免greedy的現象產生。所謂greedy指的就是正規表達式嘗試找出符合條件最大的字串的情況,大概是因為太過貪心的感覺才用這個詞來形容(?)舉例來說,我想要找到同時出現beautiful跟ugly這兩個詞的句子,所以寫出beautiful.*ugly。乍看之下好像滿有道理的,反正中間有什麼詞都沒關係嘛。但是問題出在當我們這樣打的時候,python會盡量幫我們找到符合條件的最大字串。也就是說,即便有一個句子真的同時包含這兩個詞,只要更遙遠的另外一個句子裡面有ugly出現,他就會把那個ugly視為真正的終點。所以這種時候應該要用beautiful[^\.]*ugly才對。(因為我想要的是一個句子裡面同時出現這兩個詞,所以只要避免句點的出現就可以了)

  講完要特別注意的兩件事之後,這邊就借用Speech and Language Processing裡的例子稍微演示一下運用正規表達式的邏輯思考方式。如果我們希望在一篇英文文章找到所有"the",那一開始應該會想這樣做:

the

  但是這樣會錯過在句首的the,所以要應用[]

[Tt]he

  改成這樣之後又發現這樣會抓到其他詞(像是theif之類的),所以在前後加上詞邊界的標記。

\b[Tt]he\b

  可是這樣如果the旁邊剛好有特殊符號(底線或是數字之類的)的話就會抓不出來,所以應該改成排除前後有字母的可能性就好:

[^A-z][Tt]he[^A-z]

  這麼做雖然看起來更完美了,但是因為我們在前面跟後面使用了[^A-z],python會預設不管The還是the的前面跟後面都應該要有字符才行。也就是說上面這個正規表達式沒辦法找到位在字串最前面跟最後面的"the"。因此這裡應該要用到|來加入這兩種可能性:

(^|[^A-z])[Tt]he($|[^A-z])

  這樣才是一個最大程度不漏掉文章裡面的"the"的正規表達式。感覺起來很麻煩很複雜,對初學者來說實際上也是如此。甚至對很熟練的老鳥來說可能也沒辦法一次就想到最完美的解方,但多使用幾次就會發現真的很好用啦~

  在python裡面如果要使用正規表達式的話需要引入一個叫做re的內建套件,所以我們先把他引進來。

import re

re.search() & re.findall()

  接下來如果我們想找出字串裡面有沒有目標字串的時候可以用search()findall()兩個功能。兩者的差別在search()只會找第一個符合條件的字串,findall()則是會把所有符合條件的字串都回傳到一個串列裡面。
  然後需要特別注意的是,因為反斜線\原本在python裡面就有逃脫字元的功能,為了避免python搞混,在有使用正規表達式的字串前面都要加上r

string = "There are many colors in the world. My favorite colour is pink. What about your favorite color?"
print(re.search(r"colou?r", string))
print(re.findall(r"colou?r", string))
# 輸出
<re.Match object; span=(15, 20), match='color'>
['color', 'colour', 'color']

  因為英式英語跟美式英語中的「顏色」拼法不同,但實際都是一樣的詞,為了統計方便,我們通常會全部找出來以後再統一一種拼法。所以這邊應用表達0或1的?來表示u的可有可無。

  這兩個功能就跟之前介紹過的字串搜尋功能一樣,可以設定搜尋範圍,只要加上索引值的參數就行。這邊就不多做說明,留給大家自己嘗試了。

re.sub()

  如同上面提到color跟colour需要統一,想要取代特定字串時可以使用re.sub()。第一個參數放置想要取代掉的字串,第二個參數放置取代目標的字串,第三個則是要進行取代的變數。如果想限定替代次數也可以加上第四個參數。

new_string = re.sub(r"colou?r", "color", string)
print(new_string)
# 輸出
"There are many colors in the world. My favorite color is pink. What about your favorite color?"

  為了熟悉正規表達式的使用,我們這邊再做一個練習。首先是上面美澳日期格式的轉換:

aus_date = "05/09/2011 26/11/1985 30/06/2022"
us_date = re.sub(r"(\d{2})/(\d{2})/(\d{4})", r"\2/\1/\3", aus_date)
print(us_date)

  前面也提到,讓澳洲日期格式變成美國日期格式的方法就是把日期跟月份的位置對調。為了讓python能夠認得日期跟月份,我這邊用()幫他們分了組。然後因為這些字串的規律就是「兩個數字/兩個數字/四個數字」,所以把代表數字的\d填進去以後再加上{}限制數字的個數。如此一來,對python來說,日期就是群組1\1;月份就是群組2\2;年份則是群組3\3。接下來就只要在第二個參數的位置把他們重新排列就行了。

# 輸出
"09/05/2011 11/26/1985 06/30/2022"

  資料前處理時最常遇到的難題就是專有名詞的處理。像Los Angelas這樣的專有名詞,如果不經過調整的話,在斷詞的時候會因為中間有空格就被當成分開的兩個字。所以我們需要事先把他們整合成一個字再進行斷詞。這個時候就會需要用到正規表達式了。實際操作的難點再於,可能也存在三個分開詞的專有名詞(例:King George VI)或者是中間有破折號的專有名詞,很多問題都需要被考慮到。

  大家可以試著把下面這段文字的專有名詞都處理成合在一起的樣子(例:Los Angelas → LosAngelas),我把自己的解法放在這裡,有興趣的人可以自己先試做過後再點進去看~

A chronicle of Martin Luther King's campaign to secure equal voting rights via an epic march from Selma to Montgomery, Alabama in 1965.
Quentin Tarantino and Robert Rodriguez's homage to exploitation double features in the 60s and 70s with two back-to-back cult films that include previews of coming attractions between them.
The Manzoni family, a notorious mafia clan, is relocated to Normandy, France under the witness protection program, where fitting in soon becomes challenging as their old habits die hard.
A has-been actor best known for playing the title character in the 1980s detective series "Mindhorn" must work with the police when a serial killer says that he will only speak with Detective Mindhorn, whom he believes to be a real person.

  正規表達式的使用就跟程式碼一樣,並沒有正確答案,只要能達到目標就是好答案,所以關於日期的格式統整還有上面的練習,或許也有其他的方式可以進行,歡迎大家在留言處交流~如同上面示範的一樣,只要我們熟悉正規表達式的應用就能大大縮減作業時間,提高工作效率,所以強烈建議大家多花時間練習正規表達式的運用~

  最後附上一個有用的網站,如果不確定自己寫出來的正規表達式是否正確,可以透過這個網站先進行測試喔(記得在FLAVOR的地方選擇python)~明天見!

Reference

Speech and Language Processing 2nd Edition

註:雖然這邊放的是購書連結,但他們其實有釋出draft的電子檔,有興趣的人可以下載來讀讀看~


上一篇
[Day 6] 電腦選的花生到底好不好?機器學習的成效評估
下一篇
[Day 8] 時間都去哪了?資料前處理:當一個有原則有效率的快樂打工人-if & loop
系列文
文理組人都能上手的入門 NLP(自然語言處理)31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言