4. Conditions: if-elif-else (90%)
1. if-else語法與定義

Youtube 學習影片
[py_if_for] 01 Concept and AQI case introduction: if與for的語法說明和使用以及AQI的案例說明
[py_if_for] 02 Accessing AQI data: 探索AQI資料(JSON)的內容結構
[py_if_for] 03 Categorizing and rescaling: 依照不同數值區間給予標籤,以空污為例
[pf_if_for] 04 Detect prominent: Finding maximum: 用for-loop找到一個list中的最大值
[py_if_for] 05 Sorting manually by for, if, swap, list and dict: 自己打造一個排序演算法:使用兩層的for-loop、if、調換Swap技巧、以及list和dict結構
if搭配for的相關應用
if
的應用
if
的應用選擇性列印出你所要呈現的youbike站台:指定要某幾個、透過if來判斷,指定要滿載的
透過比較來找到最大值和最小值
處理缺漏值,AQX不定期地會有缺漏值,要用
if
來偵測是否有缺漏值,並指定遇到缺漏值要怎麼做處理。或者是查驗輸入的日期是否合理
for+if
的應用
for+if
的應用把所有文字看過一遍,計算文字的出現次數
用
for
遍歷PM2.5的值來對PM2.5做排序,找到最大或者是前三大的PM2.5站台分組。若告訴一個使用者PM2.5是67,他可能不知道這個數字代表什麼意義,但你若透過一個標準的Mapping把它mapping到非常高、高、中、低、非常低等五個等級,並告訴使用者說現在這個區域PM2.5是高,那他就聽得懂。
2. 案例-空氣品質資料-資料清理
範例說明:從資料來源為行政院環保署的空氣品質監測網(PM2.5 introduced by 行政院環保署)可以看到即時的空氣品質,包含指標和嚴重程度(上顏色)。上顏色的方法是依照AQI的值分成六個等級。當要把AQI值繪製為相對應的等級時,必須要判斷AQI值落在哪一個區間,此時,需要用if-else的條件判斷式來判斷,究竟是落在哪個區間。而測站不會只有一個,因此,要一個接一個判斷完現在所有的測站落在哪一個區間的話,那就要用for-loop來判斷完所有的測站。
2.1. 獲取環保署開放資料
import requests
import json
url = " https://taqm.epa.gov.tw/taqm/aqs.ashx?lang=tw&act=aqi-epa&ts=1538460570145"
response = requests.get(url)
aqdata = json.loads(response.text)
print(type(aqdata))
<class 'dict'>
2.2. 觀察資料Traverse and print out AQI data
print(aqdata.keys())
print(type(aqdata["Data"]))
print(aqdata["Data"][0])
dict_keys(['Result', 'Data']) {'SiteId': '84', 'SiteName': '富貴角', 'SiteKey': 'FugueiCape', 'AreaKey': 'North', 'MonobjName': '背景', 'Address': '新北市石門區富貴角海邊', 'lat': '25.294147', 'lng': '121.539141', 'AQI': '63', 'MainPollutant': '細懸浮微粒', 'MainPollutantKey': 'PM25', 'CityCode': '31', 'PM10': '42', 'PM10_AVG': '40', 'PM25': '16', 'PM25_AVG': '20', 'O3': '42', 'O3_8': '53', 'SO2': '0.4', 'CO': '0.25', 'CO_8': '0.30', 'NO2': '3.6', 'SO2_VFLAG': '1', 'CO_VFLAG': '1', 'O3_VFLAG': '1', 'PM10_VFLAG': '1', 'NO2_VFLAG': '1', 'THC_VFLAG': '1', 'PM25_VFLAG': '1', 'SO2_PRETXT': '', 'CO_PRETXT': '', 'O3_PRETXT': '', 'NO2_PRETXT': '', 'PM10_PRETXT': '', 'PM25_PRETXT': '', 'HO_AQI_VAL': '63', 'THC': '1.9', 'x': '121', 'y': '86', 'DataSrc': 'epa', 'Time': '2020-03-22 22:00:00', 'AQIStyle': 'AQI2'}
列印dictionary的key
print(aqdata["Data"][0].keys())
dict_keys(['SiteId', 'SiteName', 'SiteKey', 'AreaKey', 'MonobjName', 'Address', 'lat', 'lng', 'AQI', 'MainPollutant', 'MainPollutantKey', 'CityCode', 'PM10', 'PM10_AVG', 'PM25', 'PM25_AVG', 'O3', 'O3_8', 'SO2', 'CO', 'CO_8', 'NO2', 'SO2_VFLAG', 'CO_VFLAG', 'O3_VFLAG', 'PM10_VFLAG', 'NO2_VFLAG', 'THC_VFLAG', 'PM25_VFLAG', 'SO2_PRETXT', 'CO_PRETXT', 'O3_PRETXT', 'NO2_PRETXT', 'PM10_PRETXT', 'PM25_PRETXT', 'HO_AQI_VAL', 'THC', 'x', 'y', 'DataSrc', 'Time', 'AQIStyle'])
用for列印所有站台的數值
for site in aqdata["Data"]:
print(site["SiteName"], site["AQI"], site["PM25"])
# print("{}\t{}\t{}".format(site["SiteName"], site["AQI"], site["PM25"]))
2.3. 取代遺漏未收得的空品資料
在這個例子中,由於我發現PM2.5與AQI偶而會有空值(就該場站的資料沒有傳回來),我可以用for
-each掃過所有的資料,然後用if
來判斷,如果資料是空值的話(如下面的空字串""
或者被標示為ND
),就把-1
assign給他,不然的話就保留原本的數值,或是像下面一樣,用int()
把它轉為整數。我在for
-each掃過所有資料時,我用了一個變數site
,他的效果相當於把aqdata["Data"]
裡面的每一個項目走訪一遍,當site
內容被改變時,aqdata["Data"]
的內容也會一併被改變。
底下將介紹三種作法用for
來走訪過List裡面的每一個項目,並用if
來判斷是否有缺漏值,有的話再進行更新。三種方法分別為
用
for
去走訪每個item用
for
去走訪List中的每個index用
for
去同時走訪List中的每個item和index
2.3.1 Method 1: for to iterate each term
這時候的for
相當於是for-each的概念,他用一個變數指到List中的每個物件。當所指到的那個物件的PM2.5為空值(""
)或"ND"
時,便將其取代為-1
。但這樣會變成site["PM25"]
的型態不一(原本的型態都是<str>)且運算不易,所以我就順便把所有PM2.5的值通通從<str>轉為<int>。
for site in aqdata["Data"]:
if site["PM25"] == "" or site["PM25"] == "ND":
site["PM25"] = -1
else:
site["PM25"] = int(site["PM25"])
# print out the first 10 records for glimpse
for aq in aqdata["Data"]:
print(aq["SiteName"], aq["AQI"], aq["PM25"])
2.3.2 Method 2: Using index of list to iterate each item
我也可以透過index去accessaqdata["Data"]
中第0個、第1個、第2個dict的內容。但此時你就要去用index存取aqdata["Data"]
內的值,你的程式碼會稍微長一點點。
# for site in aqdata["Data"]:
print(len(aqdata["Data"]))
for i in range(len(aqdata["Data"])): # iteration
if aqdata['Data'][i]['PM25'] == "" or aqdata['Data'][i]['PM25'] == "ND":
aqdata['Data'][i]['PM25'] = -1
else:
aqdata['Data'][i]['PM25'] = int(aqdata['Data'][i]['PM25'])
# print(i, aqdata['Data'][i]['SiteName'])
# print out the frist 10 records for glimpse
for site in aqdata["Data"]:
print(site["SiteName"], site["AQI"], site["PM25"])
2.3.3 Method 3: Using index and item to iterate content of List
前面我們介紹到兩個版本,
for site in aqdata["Data"]:
是用site
來直接存取所有的資料項目,for i in range(len(aqdata["Data"])):
的做法則是用i
作為index來提供存取原本的aqdata["Data"]
。
但其實還有第三種選擇就是用enumerate()
來同時存取index和資料項目,當我需要access這是第幾個項目,或者需要access原本的項目值時,那我就用index,但我如果只是要判斷一下大小,那我就可以用被取出來的資料項目。寫法如下:
for i, site in enumerate(aqdata["Data"]):
print(type(enumerate(aqdata["Data"])))
for i, site in enumerate(aqdata["Data"]):
if site["PM25"] == "" or site["PM25"] == "ND":
site["PM25"] = -1
else:
site["PM25"] = int(site["PM25"])
for site in aqdata["Data"][:10]:
print(site["SiteName"], site["AQI"], site["PM25"])
3. 將AQI數值對映到空品程度
前面說到一般民眾其實不太了解AQI數值的意義,也不知道最大最小值是多少,所以跟民眾講AQI的數值是沒意義的。民眾比較容易了解的是「現在嚴不嚴重」。因此,我們可以依照環保署對於空氣品質的等級劃分方法來將所有站台區分成幾個等級。程式邏輯上要做的事情是,用if-elif-else來設計好不同的區間判斷,然後用for-loop把所有AQI的值走訪過一遍,看每個站台落入哪個區間。
環保署的標準:https://taqm.epa.gov.tw/taqm/tw/b0201.aspx
Label
Range
Good
0~50
Moderate
51~100
Unhealthy for Sensitive Groups
101~150
Unhealthy
151~200
Very Unhealthy
201~300
Hazardous
301~500
3.1 用if-elif-else來判斷數值區間
注意資料型別的轉換:判斷的時候必須注意要把AQI值給轉為整數,因為原本的JSON內容都是文字。或者可考慮一開始獨進資料的時候就全部先轉好。
儲存判斷的結果到dict:當判斷完每個站台是屬於哪個區間後,一種方式是把它直接列印出來(像下面comment處所示),但如果未來繪圖會用到,比較好的方式是,把該範圍的狀態標籤儲存成一個dict的key所指的value,例如以下的
site["status"] = "Moderate"
我便是判斷完後直接儲存到一個key。另,如果有一個dict為adict = {},我想要在裡面增加一個值,寫法是adict["akey"] = avalue
。
for site in aqdata["Data"]:
if int(site["AQI"]) >= 0 and int(site["AQI"]) < 51:
site["status"] = "Good"
# print(site['SiteName'], "Good", site["AQI"])
elif int(site["AQI"]) >= 51 and int(site["AQI"]) < 100:
site["status"] = "Moderate"
# print(site['SiteName'], "Moderate", site["AQI"])
else:
site["status"] = "Uncategorized"
# print(site['SiteName'], "Uncategorized", site["AQI"])
for site in aqdata["Data"][:10]:
print(site['SiteName'], site["status"], site["AQI"])
3.2 用固定區間來計算分佈
為了避免過多的if-elif-else或者switch的使用,可以先把scale和category寫成兩個List,然後用多一層for-loop來trace該value會落在List的哪個區間。
degree = ['Good', 'Moderate', 'Unhealthy for Sensitive Groups', 'Unhealthy',\
'Very Unhealthy', 'Hazardous']
scaler = [50, 100, 150, 200, 300, 501]
for site in aqdata["Data"]:
if site['AQI'] != "":
for i, n in enumerate(scaler):
if int(site['AQI']) < n:
d = i
break
site["status"] = degree[d]
for site in aqdata["Data"]:
print(int(site['AQI']), site['SiteName'], site["status"])
4 找到突出的數值
通常我們有了這類的資料後,第一個想到的分析方法就是找到最嚴重、最差、變化最劇烈的地區。以下這是個非常好的例子說明要如何運用if
和for
找出AQI最高的地區。在過程中,必須要注意,原本的資料的資料型態為何,甚至要注意,原本的資料是否有缺漏。在這個案例中,我希望找出,現在AQI最高的是哪些站台,且把該站台列印出來。注意,我要列印的是,AQI最高的站,而不是最高的AQI值;此外還要注意,AQI最高的站台可能不只一個(這時候該怎麼處理?)。
4.1 尋找最大值
找到最大值或最小值的概念:就個人的邏輯思考一下,我要怎麼找到一群數的最大值?解題邏輯:把第一個數先拿來當標準,之後確認過每個數,有沒有比這個數更大的,若有的話,最大值就是找到的那個數,沒有的話,那第一個數就是最大值。最小值的想法依此類推。
若要找最大值,那我會先把最大值預先設得很小,好在後面找到比他更大的。反之,若要找最小值,那我會把最小值預先設的很大,以在後面找到比他更小的。
所以,以底下的程式碼找最小值為例,我先把mina設的非常大,
一開始mina是999999
當a為5的時候,mina > a,所以我把a assign給mina,此時mina是5。
當a為3的時候,mina > a,所以我又把a assign給mina,所以mina為3。
當a為2的時候,mina > a,所以我又把a assign給mina,所以mina為2。
當a為4的時候,mina < a,所以什麼事情都不做。
當a為1的時候,mina > a,所以我再把a assign給mina,所以mina為1。
(依此類推)
# finding the maximum value
alist = [5, 3, 2, 4, 1, 3, 2, 4, 7, 82, 19, 23, 42]
print(max(alist), min(alist))
# mina, maxa = 100, 0
mina, maxa = 999999, -99999
for a in alist:
if mina > a:
mina = a
if maxa < a:
maxa = a
# print maxa, a
print(mina, maxa)
4.2 使用內建函數max()
這類比較大小的簡單功能一定都有人幫忙寫好了(如以下的例子),只要記得使用它就好。但是,要記得這樣的函式其實就是透過類似上述方法來比較大小以找到最大值。
print(min(alist), max(alist))
print(sum(alist), len(alist))
print(float(sum(alist))/len(alist))
4.3 尋找空氣品質指數最惡劣的站台
接下來我們將以上述的演算邏輯來找到AQI最高的那個site。我們先用上述的核心邏輯來找到最大值(下方的max_value
)為何,然後再用一個for
-loop把所有站台看一遍,看看哪些站台的AQI值等於max_value
。
print(aqdata["Data"][0].keys())
# Initialize a very small maximum value
max_AQI = -1
# Compare to see if any one larger than the maximum value
for site in aqdata["Data"]:
if int(site['AQI']) > max_AQI:
max_AQI = int(site['AQI'])
print("The maximum AQI value: ", max_AQI)
# Traverse all sites by for loop to see whose value equals to the maximum PM2.5 value
for site in aqdata["Data"]:
if int(site['AQI']) == max_AQI:
print(site['SiteName'], site['AQI'], site['PM25'], site['status'])
4.4 尋找前N嚴重的站台
參見5. Flow: for-loop 關於用for-loop搭配if來排序所有AQI值的教學。
5. 練習:瀏覽youbike資料
5.1 載入youbike資料
import requests
import json
response = requests.get('https://tcgbusfs.blob.core.windows.net/blobyoubike/YouBikeTP.gz')
print(type(response)) # <class 'requests.models.Response'>
print(type(response.text)) # <class 'str'>
bike_data = json.loads(response.text)
5.2 計算站台的停車比例(Fullness)
目標:假設我用perc = sbi/tot
現有腳踏車數除以總腳踏車格數作為滿站的比例,並希望每個站的資料中多一個key為perc
來存放滿站比例。請嘗試寫寫看。
5.3 儲存資料至JSON檔
在Python中把資料dump成json會遠比csv來得方便,在R中則剛好相反。原因是Python的dictionary與list兩項資料結構恰好json的格式一一相符,所以非常容易就可以把資料dump出去或者read進來。如果是要dump成csv檔的話,就必須想辦法把資料整理成pandas dataframe的二維型態。
json.dump(bike_data, open('ubike.json', 'w'))
Last updated