iT邦幫忙

2022 iThome 鐵人賽

DAY 8
1
AI & Data

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

[Day 8] 時間都去哪了?資料前處理:當一個有原則有效率的快樂打工人-if & loop

  • 分享至 

  • xImage
  •  

  嗨各位!昨天跟大家介紹了正規表達式的使用方法,還是老話一句,多練習就會自然而然熟悉跟內化。學會用正規表達是可以幫我們省去很多時間,所以一起成為努力型人才吧~(美好的一天就從灌雞湯開始

if語法

  到目前為止我們學到的程式碼都是跟python一個口令另一個動作吩咐他做事,但是這樣好像顯得太過簡單了一點。但凡用過冷氣又想省電的人一定會使用「定時」的功能為冷氣設下一些運作的條件。就像大家都會有自己的原則一樣,當遇到某些情況的時候會觸發某個特定的反應、情緒或動作,我們也可以使用if讓python當一個有原則的打工人。而使用方法則是if條件:,[換行縮排]結果。這樣寫感覺非常抽象,所以讓我們實際用一個生活化的例子嘗試看看:如果學生的成績是60分以上就恭喜他及格了。

score_1 = 90
if score_1 >= 60:
    print("恭喜及格!")
# 輸出
"恭喜及格!"

  我們可以看到因為條件符合,所以Python照著我們的指示印出了一行字。那如果條件不符呢?

score_2 = 58
if score_2 >= 60:
    print("恭喜及格!")

  這幾行程式碼沒有輸出,因為如果你試著執行這個儲存格就會發現,什麼都沒有發生。這是應該是很好理解的啦,畢竟我們之前就一直強調python是絕對的一個口令一個動作。我們只告訴他符合條件的話要做某件事,卻沒有說不符合的話要幹嘛,他當然會毫無反應。所以如果我們想告訴他不符合條件的時候要怎麼做,就必須使用else。使用的時候記得要加上冒號,然後不能縮排,因為縮排的指令代表它附屬於上面沒縮排的指令。

if score_2 >= 60:
    print("恭喜及格!")
else:
    print("重修吧你!")
重修吧你!

  所以這整個假設語法可以這樣翻譯:如果符合條件,印出「恭喜及格」,如果不符合條件,印出「重修吧你!」。
  這樣這個功能看起來就完整多了。但這世界上不是所有事情都可以用二分法解決,總是會有些灰色地帶嘛,所以如果可能發生的情況比兩種還要多的時候,我們就要用到elif。其實就是else if的縮寫啦。它會出現的位置就在ifelse中間,語法的使用基本上都跟它們兩個一樣。

score_3 = 95
score_4 = 70
 
if score_3 >= 90:
    print("書卷有望~")
elif score_3 >= 60:
    print("重修吧你!")
else:
    print("恭喜及格!")

    
if score_4 >= 90:
    print("書卷有望~")
elif score_4 >= 60:
    print("重修吧你!")
else:
    print("恭喜及格!")
"書卷有望~"
"重修吧你!"

  do rei mi so(^∀^●)ノシ 這樣我們就成功讓python變成一個有原則的打工人了。基本上elif的使用是沒有次數限制的,所以大家可以按照自己的需求決定數量。而且如果其實你希望某些條件可以毫無反應,也不一定需要else的存在,還是滿彈性的。看到這裡可能會出現兩個問題:

  1. 條件擺放的順序有差嗎?
  2. 不能直接用很多個if就好嗎?

  這兩個問題可以一起用if, elif, 跟else的結構說明得到解答。當我們有一個下面這樣的結構時,python的運作事先看a是否被滿足。如果滿足的話,做了task1他就會停下來;如果不滿足他就會看b是否被滿足。如果滿足的話,做了task2他就會停下來;如果不滿足的話,他會做task3。也就是說,在這樣的結構底下python只會完成其中一個指定條件底下的指令,而且會按照順序確認是否符合條件。只要符合這個條件,整個程序就會停止。所以用上面的例子來講的話,我們就不能把a設定為score_3 >= 60b設定為score_3 >= 90,因為它會先滿足成績大於60分這個條件而停止動作。如果我們把elif改成if呢?那python就會先印出「恭喜及格!」再印出「書卷有望~」,因為每個if都是獨立的條件。

if a:
    do task1:
elif b:
    do task2:
else c:
    do task3:

  結論就是,如果使用到elifelse,而且條件之間有所重疊,那順序的擺放就會有差。如果條件之間有所重疊,而且希望只要符合條件就做不只一件事情,那就應該使用if

  如果我們希望使用elif結構的時候不要受到順序的影響,在上面例子的情況下,其實有一個解方,那就是用and把條件設定得更嚴格一點。以上面的例子來說,三個條件盡量不重疊地列出來應該會像下面這樣:

  1. 成績 >= 90:書卷有望~
  2. 60 <= 成績 < 90:恭喜及格!
  3. 成績 < 60:重修吧你!

  所以只要我們在「恭喜及格!」的地方加上比90小這個條件,就可以達到三者不重疊的目標,免去受到順序影響的問題。

if score_3 >= 90:
    print("書卷有望~")
elif score_3 >= 60 and score_3 < 90:
    print("恭喜及格!")
else:
    print("重修吧你!")
    
if score_3 >= 60 and score_3 < 90:
    print("恭喜及格!")
elif score_3 >= 90:
    print("書卷有望~")
else:
    print("重修吧你!")
# 輸出
"書卷有望~"
"書卷有望~"

  只要使用and,python就必須在同時符合and前後兩個條件的情況下才會動作。當然如果我們的條件沒有那麼嚴苛,只需要滿足兩個當中的一個的話,可以使用or。舉例來說:卡卡西跟伊魯卡都是鳴人的老師,所以鳴人在遇到他們的時候應該要說「老師好」,而遇到佐助的時候只需要說「喂佐助」就可以了。

encounter = "卡卡西"
if encounter == "卡卡西" or encounter == "伊魯卡":
    print("老師好")
elif encounter == "佐助":
    print("喂佐助")
    
encounter = "佐助"
if encounter == "卡卡西" or encounter == "伊魯卡":
    print("老師好")
elif encounter == "佐助":
    print("喂佐助")
    
encounter = "伊魯卡"
if encounter == "卡卡西" or encounter == "伊魯卡":
    print("老師好")
elif encounter == "佐助":
    print("喂佐助")
# 輸出
"老師好"
"喂佐助"
"老師好"

  這邊要特別注意的是,相等的運算符號必須使用==而非=。原因就跟之前說過的一樣,因為=是用來宣告變數的。當然,能夠用來表示條件的不只這些運算符號,也可以是innot in,只要執行後會有回傳的值讓python可以判斷,都可以作為條件使用。

  但elif結構底下的順序只要條件不重疊就無所謂嗎?我個人認為並非如此。這主要有兩個原因。第一個是,當條件有長有短的時候,把最長的條件留給else就不用打出來,這樣比較省時間。(這是懶人我最常用的判斷依據XDD)
  第二,因為會按照出現順序進行條件判斷,把最多資料符合的條件放在最前面可以節省掉繼續往後判斷要花的時間。雖然條件判斷需要花費的時間對我們來說可能很難感受到長短差異,但如果有超多資料需要判斷,還是會積少成多產生差別,所以推薦大家把最多資料符合的條件放在最前面。用一個更具體的例子說明,當一箱水果裡面有蘋果、葡萄跟香蕉,三者的數量分別是50、32、18,而且每一次python判斷TrueFalse所需的時間是t時,不同順序寫法的elif結構總共需要的時間就會像下圖那樣。紅色的箭頭代表蘋果在判斷結構中的終點,紫色代表葡萄,橘黃色代表香蕉。從圖裡面可以看到,當我們按照數量多寡排列判斷式的時候(T1),可以達到最快的完成速度。所以當我們在寫資料很多且很長的判斷式時,按照符合條件數量的多寡決定判斷式的排列順序會式一個比較好的選擇。
https://ithelp.ithome.com.tw/upload/images/20220910/20151687WM98GbiwVT.png

迴圈(loop)

  看到上面假設判斷的語法示範,不知道有沒有人開始覺得判斷不同的東西要重複打很多次一樣的指令很麻煩?複製貼上之後萬一忘記改變數名稱也是個問題,難道python就不能更有效率一點用一個指令讓事情多次重覆嗎?當然可以,只要我們掌握迴圈(loop)的使用就可以!

for loop

  使用for迴圈的時候,只要我們告訴python一個執行的範圍跟要執行的指令就可以讓他幫我們重複做一樣的事。語法的結構就像下面這樣:

for b in a:
    do task

  稍微翻譯一下就是以a裡面的b為對象,依序做task。

sum = 0
number = [1, 2, 3, 4]
for i in number:
    sum = sum + i
sum

  這邊做的事情就是把number這個串列裡面的東西依序當作i,讓sum這個變數等於自己加上i。這其實就相當於叫python做下面這段程式碼做的事情。

sum = 0
sum = sum + 1
sum = sum + 2
sum = sum + 3
sum = sum + 4
sum

  所以輸出會是:

# 輸出
10

  i只是一個隨便指示的東西,就像未知數一樣,所以大家開心用什麼都可以,只要記得縮排裡面的task跟著統一就好。
  使用loop語法的時候我們需要注意的事情是,被指定為範圍的變數不能被改變。所以假設今天我有好幾個電子信箱的帳號想要寄信給他們,需要加上網域的話,不可以這樣做:

mails = ['heyitsmamail', '11111', 'genius0824']
for i in mails:
    i = i + "@gmail.com"
print(mails)

  python會保護範圍內的變數不受改變,所以如果這樣做的話,得到的結果會跟原本一模一樣。

['heyitsmamail', '11111', 'genius0824']

  所以我們常用的做法是結合range()這個函式的使用來達成目標。他是專門用來產生迭代對象的函式,所以只要在range()裡面放入需要的數字n,他就會從0開始回傳n個數字給迴圈。除了單純以數字做為參數之外,我們也可以指定開始跟結束的點,但他產生的迭代對象不會包含結束的數字。如果我們希望數字不是以1為單位產生,也可以多加上希望的單位做為第三個參數。

# 從0產出4個數字做為迭代對象
list = []
for i in range(4):
    list.append(i)
print(list)

# 從1到5產生迭代對象
list = []
for i in range(1,5):
    list.append(i)
print(list)

# 從5到31,以5為單位產生迭代對象
list = []
for i in range(5, 31, 5):
    list.append(i)
print(list)
# 輸出
[0, 1, 2, 3]
[1, 2, 3, 4]
[5, 10, 15, 20, 25, 30]

  應用range()去完成上面想做到的事情就會是這樣:

for i in range(len(mails)):
    mails[i] = mails[i] + "@gmail.com"
print(mails)

  把從0開始的3個數字依序當成i,讓mail裡面的第i個資料跟"@gmail.com"合併。

# 輸出
['heyitsmamail@gmail.com', '11111@gmail.com', 'genius0824@gmail.com']

  我們當然也可以把if的條件判斷跟迴圈做結合,讓我們把上面的例子存到串列裡面繼續沿用。

score = [score_1, score_2, score_3, score_4]
for i in score:
    if i >= 90:
        print("書卷有望~")
    elif i < 60:
        print("重修吧你!")
    else:
        print("恭喜及格!")
# 輸出
書卷有望~
重修吧你!
書卷有望~
恭喜及格!

  鏘鏘~程式碼變得超級乾淨又省時間~python變成有原則又有效率的快樂打工人了~

nested loop(巢狀迴圈)

  既然我們可以把if指令包在迴圈裡面,當然也就可以像俄羅斯娃娃一樣把一個迴圈包在另一個迴圈裡面。而這種迴圈被稱做巢狀迴圈。下面簡單做一個示範,如果我需要python幫我從A到C依序列出他們跟1到3結合的字串存進一個新的串列裡面,可以這樣做:

alphabet = ["A", "B", "C"]
number = ["1", "2", "3"]
target = []

for i in alphabet:
    for j in number:
        target.append(i+j)
print(target)

  上面的東西翻譯一下就是從alphabet這個倉庫拿出A之後,依序從number倉庫拿出東西跟A結合;接下來從alphabet倉庫拿出B,再依序從number倉庫拿出東西跟B結合;最後從alphabet倉庫拿出C,依序從number倉庫拿出東西跟C結合。所以輸出就會長這樣:

# 輸出
['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']

while loop

  使用for loop的時候我們只是給定一個範圍讓python重複執行動作。如果希望迴圈有條件地重複的話,就需要用到whilewhile會一直運作到條件不符才停止。直接拿剛剛for loop的第一個範例來做,在剛剛的範例裡面為了讓1加到4,我們設定了一個等於0的變數,再宣告了一個包含1到4數字的串列。透過一個一個把數字拿出來完成我們的目標。而用while來做會更簡單一點:

num = 0
counter = 1

while counter <= 4:
    num = num + counter
    counter = counter+1

print(num)

  稍微翻譯一下這段:當counter小於等於4的時候,num會等於num加上counter,且counter會等於counter+1。透過這樣的方法,我們就可以讓數字一路1加到4再停下來。

# 輸出
10

  這邊再看一個比較複雜的例子,我希望我有一個字串,然後想要找到裡面出現的第三個t,可以這樣做:

string = "sdhfkjheuubtektfjkdnstkdnslsa"
counter = 0
index = -1
while counter < 3:
    index = string.find("t", index+1)
    counter = counter+1
print(index)

  因為跟前面比起來複雜一點,所以這邊說明一下。find()的功能是找到指定範圍內第一個符合條件的索引值,所以我事先設定了兩個變數。一個用來計算我們找到第幾個索引值了(counter),另外一個用來存放目標物的索引值(index)。先從while裡面的第一行開始說明,因為我希望每找到一次目標物,find()的搜索範圍就往前推進,所以index這個數字會隨著我找到的目標物數量增加而變大,並且取代前一個索引值。然後在find()裡面的參數要加1才可以讓搜索範圍縮減。接下來因為我想要找到第3個目標物的索引值,所以每找到一個目標物,counter為了記錄就要加1(基本上就是一個計數器)。因此,當我找到第三個目標物的時候,index就會變成第三個目標物的索引值,迴圈也會因為大於3而終止。這個時候我們就可以得到第三個目標物的索引值:

# 輸出
21

  再來一個例子,如果我想要讓一個變數一直乘2,但不能超過100:

num = 2
while num < 100:
    num = num*2
print(num)
# 輸出
128

  問題來了,我其實是希望變數不要超過100才設下這個條件限制,但這樣迴圈還是會運作到超過100才停下來,感覺還是不太符合我的要求。這個時候應該要用到更進階的技巧,用break限制才行。

break & continue

  使用whilw跟for迴圈的時候,我們可能會遇到需要中途停止迴圈的情況。像是剛剛我希望數字不要超過100,但不確定他什麼時候可以停下來的話,可以先設一個大一點的範圍,然後再用break設定他應該停下來的時間:

num = 1
while num < 100:
    num = num*2
    if num > 100:
        break
    print(num)

  當我們使用break的時候,如果滿足break的條件,迴圈就會直接停止,break以後的動作也不會進行。如果不滿足break,他後面的動作就可以繼續進行。所以在上面的示範裡面,python會幫我們印出所有小於100的2的次方數。

# 輸出
2
4
8
16
32
64

  如果我們可以讓迴圈因為某個條件觸發而終止的話,能不能在某些條件觸發的時候只是略過指令,直接進入下一個迴圈呢?當然可以,這種時候就要用到continue。大家應該都玩過369遊戲,就是那個玩家輪流報數,如果出現3的倍數就要用拍手帶過的遊戲。我們也可以用continue跟python玩這個遊戲(靈感來源):

num = []
for i in range(1, 16):
    if i%3 == 0:
        continue
    num = num.append(i)
print(num)

  這邊我們使用的是用來求餘數的運算符號%。我們告訴python,如果數字可以被3整除(也就是餘數為0),就跳過後面的指令繼續新的一輪迴圈,所以可以得到下面的結果:

# 輸出
[1, 2, 4, 5, 7, 8, 10, 11, 13, 14]

  看來我們應該不能跟python玩這個遊戲,因為只要指令正確,這個傢伙根本不會出錯啊TT

  幹話結束,最後就結合昨天的正規表達式來練習一下if跟迴圈的使用。這邊需要用到上次IMDB的電影資料,所以請先把他們引到python裡面(提示:熊貓很重要)再嘗試完成下面的任務:

  1. 幫meta score這個欄位的分數分級,分數超過8的comment是"Watch it right now!";介於5分到8分的comment是"If you have time...";低於5分的comment是"Don't waste your time on this."(提示:結合if跟for loop)
  2. 把genre裡面出現過的類型全部寫進一個串列裡面(要求:試著用正規表達式完成)
  3. 新建一行紀錄跟情感有關的keyword。如果description裡面出現 “life”, “young”, “mysterious”, “family”, 跟 “friends”,就把他們寫進這個欄位。同一個欄位裡面出現多個關鍵字的時候,用|把他們隔開。

  一樣有任何問題都歡迎留言,明天見/images/emoticon/emoticon29.gif


上一篇
[Day 7] 時間都去哪了?資料前處理:合格社畜的工作方式-正規表達式(Regular Expression)
下一篇
[Day 8.5] 時間都去哪了?資料前處理:客製化你的專屬函式-function
系列文
文理組人都能上手的入門 NLP(自然語言處理)31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言