昨天簡單介紹過python裡面的資料型態以後,今天會針對NLP必備的字串處理進行介紹。
昨天提到在宣告一個變數是字串的時候,可以按照個人喜好使用單引號''
跟雙引號""
。其實這只是建立在字串內容不包含單引號或雙引號的時候。當字串內容包含單引號在內時,使用雙引號才能避免python搞錯字串在哪裡結束;同理,當字串內容包含雙引號在內時,使用單引號才能避免python搞錯字串在哪裡結束。
sentence1 = "I'm a student."
sentence2 = 'You are such a "smart" person.'
從上面的例子裡面應該就可以清楚看出單雙引號的區別使用時機。一般來說,當內容不含有這兩個符號時,宣告字串要使用誰就真的只看個人喜好了。但是如果有人跟我一樣強迫症很嚴重,不喜歡單引號雙引號變來變去的話,其實還有一個辦法可以不受字串內容拘束,那就是使用逃脫字元(Escape Character) \
。當我們在一個符號前面加上反斜線的時候,就是在告訴python這個符號在這裡出現不是它原本的用途。所以當我們像下面的第一個例子一樣在單引號前面加上反斜線的時候,python就會知道它現在不是被用來標示字串的東西,只是單純的符號而已。(貼心提醒:反斜線通常都在enter鍵的正上方喔)
sentence1 = 'I\'m a student.'
sentence2 = "You are such a \"smart\" person."
當我們想知道一個字串裡面包含幾個字的時候,可以使用len()
來計算它的長度。需要注意的地方是當我們使用這個功能時,所有的特殊符號,包含空白都會被納入計算。
print(len(sentence1))
sentence3 = "我是學生。"
print(len(sentence3))
大家可以先按照剛剛的說明計算預測一下這三行程式碼印出的結果再往下看實際運行的結果是不是跟你的理解一樣。
# 輸出
14
5
不知道有沒有人覺得len()
這個函式很眼熟,其實它就是昨天提到用來看串列(list)長度的函式喔。有時候同樣一個函式可以跟不同的資料型態結合,而根據資料型態的不同,運作方式也會有所差異。這些差異也是我們在進行資料處理的時候需要特別注意的地方!
字串跟串列一樣可以透過[]
進行索引,只是針對字串進行索引的時候是以「字符」作為單位來計算。
word1 = "python"
word2 = "基礎python教學"
print(word1[2])
print(word2[8])
# 輸出
t
教
如果想把字串合在一起的話,可以使用+
這個運算符號。
sentence1 = "Hello."
senctence2 = "How are you?"
print(sentence1 + sentence2)
# 輸出
Hello.How are you?
句點跟下一個句子之間竟然沒有空格,真是太可怕了,趕快幫它加上去!
print(sentence1 + " " + sentence2)
# 輸出
Hello. How are you?
在使用+
的時候,我們不只可以做變數之間的結合,也可以像上面那樣視個人需求直接加上新的字串。這其實讓我們在進行字串處理的時候多了很大的彈性,可以做像下面這樣的事。
fruit = "apple"
price = "20"
sentence = "The " + fruit + " cost me " + price + " dollars this morning."
print(sentence)
# 輸出
"The apple cost me 20 dollars this morning."
如果可以把不同的字串合在一起的話,能不能把特定的字串重複很多遍呢?當然可以。這邊要請出*
來做乘法。
sentence = "This is an example. "
print(sentence*5)
# 輸出
"This is an example. This is an example. This is an example. This is an example. This is an example."
有能把字串結合的方式,當然就有可以把字串分割的方式。字串分割在NLP裡面最常被使用的時機應該就是做英文文本的斷詞了。關於斷詞的重要性跟其他相關的東西會在之後專門有一天用來講解。這邊我們只要知道是把文本裡面的詞一個一個切開來就好。
字串分割的函式split()
預設用來分割的依據是空白字元,所以如果我們不在括號裡面加東西的話,它就會把有空白字元的地方都分開。如果有其他想要用來分割的依據,就以字串的形式填入括號。舉例來說,下面我想把一個人的email只留下帳號的部份的話,就可以用@當作分割依據。
print(sentence.split())
email = "thisismamail@gmail.com"
print(email.split("@"))
# 輸出
['This', 'is', 'an', 'example.']
['thisismamail', 'gmail.com']
從上面我們可以看到,被split()
分割過後的字串會以字串的形式存在一個串列裡面。使用split()
的時候要特別注意,它不會直接把變數切開,只是切給我們看而已。所以如果我們要留下分割後的結果,就必須宣告一個變數給它。
print(email)
email_split = email.split("@")
print(email_split)
# 輸出
thisismamail@gmail.com
['thisismamail', 'gmail.com']
這邊宣傳一下。使用這些指令的時候,不管我們用的指令會不會修改原本的變數,最好都安排一個新的變數給它們。在資料修修改改的過程中,養成設定新變數的習慣是很重要的,因為我們很可能做著做著就發現自己下錯指令,需要回到某個狀態的資料。這個時候如果前面全部都像上面的示範一樣靠純指令完成的話就欲哭無淚了TT 我還是推薦大家像下面這樣留下作業痕跡會比較好。就像算數學的時候,如果只有答案就不能知道到底是在算式的哪個步驟出錯了一樣(?
英文的書寫系統跟中文其中一個相異點就是有大小寫之分。在某些情況下,同一個詞彙的大小寫差異可能就會造成意思的不同或是執行結果的差異(例:首字母大寫對專有名詞辨識(NER)有很大的影響),但在其他情況下也可能沒有那麼大的區別(例:單純想知道每個詞在特定文本中出現的頻率時)。如果今天我們要做的事情對大小寫不是很敏感的話,可以使用python裡面大小寫轉換的指令來把他們統一起來。
sentence = "I'm having some Monday blues."
low_case = sentence.lower()
up_case = sentence.upper()
print(low_case)
print(up_case)
# 輸出
i'm having some monday blues.
I'M HAVING SOME MONDAY BLUES.
除了最基本的大小寫統一之外,我們也可以用capitalize()
讓python幫我們確保字串的第一個字母有大寫;用title()
讓它幫我們把字串裡面每一個字的首字母都大寫;也可以用swapcase()
讓字串裡的大小寫互換。
capital = low_case.capitalize()
title = low_case.title()
swap = title.swapcase()
print(capital)
print(title)
print(swap)
# 輸出
"I'm having some monday blues."
"I'M Having Some Monday Blues."
"i'm hAVING sOME mONDAY bLUES."
如果想要在字串裡面尋找特定條件的東西,主要有三種方法可以使用:
1️⃣一問一答
這個方法就是真的直接用in
跟not in
來問python我們想要找的東西有沒有在指定的字串裡面,然後python也會用布林值(Boolean,也就是True
跟False
)來回答我們。
greeting = "Have a nice day."
print("good" in greeting)
print("nice" in greeting)
print("good" not in greeting)
print("nice" not in greeting)
# 輸出
False
True
True
False
2️⃣用運算符號
上面的方法是用在我們只需要確認字串裡面是否有特定條件的東西的時候。如果我們想要找到跟條件一模一樣的字串時,可以使用==
這個運算符號。因為=
已經被用來當作宣告變數的指令了,所以當我們想判斷兩個東西是否相同時,一定要用兩個等號;如果只需要確認他們沒有完全相同的話,也可以用!=
。
print(greeting == "Have a nice day.")
print(greeting == "Have a good day.")
print(greeting != "Have a nice day.")
print(greeting != "Have a good day.")
用運算符號來確認是兩個物件是否完全相同時,輸出的也是布林值。
True
False
False
True
3️⃣用函式
如果想知道這個搜尋目標在字串中的位置,可以使用find()
跟rfind()
這兩個函式。前者會回傳目標物在字串中第一次出現的開始索引值,後者則是回傳最後一次出現的開始索引值。如果我們要找的目標物不在字串裡面的話,它會回傳-1給我們。
sentence = "Coding is fun, but debugging isn't."
print(sentence.find("is"))
print(sentence.rfind("is"))
print(greeting.find("good"))
# 輸出
7
29
-1
除了這兩個函式之外,index()
也可以用來在字串裡面找我們指定的東西。跟上面兩者唯一的不同是,如果字串裡面不包含我們指定的東西,它會直接出現error code。
print(sentence.index("is"))
print(sentence.index("good"))
# 輸出
7
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-17-fc681fe13a61> in <module>
1 print(sentence.index("is"))
----> 2 print(sentence.index("good"))
ValueError: substring not found
使用這三個函式的時候需要特別注意的是他們都只會找到一個就停下來,沒有辦法告訴我們所有目標物的位置。關於怎麼找到所有目標物的解方,我之後會在介紹loop的時候示範給大家看。
雖然不能知道所有目標物出現的位置,我們還是可以計算目標物總共出現幾次,只要使用count()
就可以得到目標物的總數。
sentence.count("is")
# 輸出
2
除此之外,我們還可以指定它在字串裡面的查詢範圍。如果我們希望它從索引值0開始找到15就好,可以這樣做:
sentence.count("is", 0, 15)
# 輸出
1
既然都可以找出目標物的位置也可以計算他們的數量了,我們當然也可以把它們取代成別的東西。這個時候會用到的是replace()
。在replace()
裡面需要用到的參數有兩個,被取代的目標物跟取代者。
sentence_new = sentence.replace("is", "was")
print(sentence_new)
# 輸出
"Coding was fun, but debugging wasn't."
如果我們希望它取代的次數是有限制的,可以在裡面加入第三個參數來限定取代次數。
sentence_new = sentence.replace("is", "was", 1)
print(sentence_new)
# 輸出
"Coding was fun, but debugging isn't."
今天關於字串處理的介紹就到這邊告一個段落,這麼多函式本來就不可能一次記住,多練習多使用才是最有效率的學習方法喔~明天會講解怎麼把一些酷東西(套件)引進python裡面讓它幫我們做更多事,基本上就像是幫辦公室添購新的用品增加工作效率一樣。講解完之後會介紹pandas
這個套件的使用方法。那麼就明天見了!
最後放個表格統整一下今天提到的函式
程式碼 | 功能 | 所需參數 | 備註 |
---|---|---|---|
len() |
求字串長度 | 字串 | |
a+ b |
字串結合 | 兩個字串 | |
a* b |
字串重複 | 字串, 重複次數 | |
string.split() |
分割字串 | 用來作為分割標準的字串/預設為空白字元 | |
string.lower() |
字串內容全部小寫 | 無 | |
string.lower() |
字串內容全部大寫 | 無 | |
string.capitalize() |
字串第一個字首字母大寫 | 無 | |
string.title() |
字串內每個字首字母大寫 | 無 | |
string.swapcase() |
字串內容大小寫互換 | 無 | |
a in b |
看a字串是否包含在b字串內 | 兩個字串 | 回傳布林值 |
a not in b |
看a字串是否不在b字串內 | 兩個字串 | 回傳布林值 |
a == b |
看a字串是否跟b字串相等 | 兩個字串 | 回傳布林值 |
a != b |
看a字串是否不跟b字串相等 | 兩個字串 | 回傳布林值 |
string.find() |
在字串中尋找特定子字串 | 子字串 | 回傳第一個符合條件的索引值 |
string.rfind() |
在字串中尋找特定子字串 | 子字串 | 回傳最後一個符合條件的索引值 |
string.index() |
在字串中尋找特定子字串 | 子字串 | 回傳第一個符合條件的索引值 |
string.count() |
計算字串中含有子字串的數量 | 子字串, (範圍) | |
string.replace() |
替代字串中的特定子字串 | 子字串, 替代字串, (次數) |