5. Flow: for-loop (90%)

for-loop的概念

無論是在列數「水果」或者把所有腳踏車站的資訊列印出來,都需要用到一個指令For。他會讓程式在指定的範圍內重複執行,所以也經常被稱為for-each

range()for i in range(10)range(10)相當於一個List [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]。尚可指定要從哪個數開始累計起,例如:for i in range(2, 11)。也可以指定一次要跳幾個數,例如for i in range(1, 11, 2) 這樣的表示式就代表要從1列數到10,但是一次要跳兩個,所以range(1, 11, 2)實際上會是[1, 3, 5, 7, 9]

累計:Youbike總數

這個練習的目標是累計全台北市一共有幾個腳踏車格和目前共有幾台車在位置上(沒在位置上代表被騎走了)。先用requestsjson packages爬取Youbike資料並轉為dict與list所組成的物件格式。然後用兩個整數變數來累計總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)

先初始化兩個變數為0來分別累計sbi與tot的數量(建議可以試試看沒初始化會產生什麼樣的錯誤訊息,會跟你說沒有sbi_sum這個變數存在)。

sbi_sum, tot_sum = 0, 0
for usite in bike_data['retVal'].keys():
    sbi_sum = sbi_sum + int(bike_data['retVal'][site]['sbi'])
    tot_sum = tot_sum + int(bike_data['retVal'][site]['tot'])
print(sbi_sum, tot_sum)

計數:成績分佈

將不同的數值範圍,對應到不同的List位置並計算其數量。下面這個例子是計算成績分佈的方法,以10分做為一個距離,如果是6X分的話,就記錄到glist的6的位置;若是8X分的話,就記錄到glist的8的位置。

grades = [17, 65, 5, 74, 93, 1, 94, 16, 80, 95, 32, 78, 17, 70]
glist = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
for g in grades:
    glist[int(g/10)] += 1
    print(glist)

接下來可用matplotlib這個套件來繪製長條圖,以累計grades的分布狀態。雖然上面已經會累計每個區間各有多少個數值,但matplotlib的繪圖函式會自動計算每個項目的數量。在程式碼plt.hist(grades, 10)第二個參數10指的便是以10為區間來進行計數。其他兩個參數facecoloralpha分別為顏色與透明程度。

%matplotlib inline
import matplotlib.pyplot as plt

grades = [17, 65, 5, 74, 93, 1, 94, 16, 80, 95, 32, 78, 17, 70, 22, 43, 95, 67, 3, 30, 40, 51, 96, 17, 13, 21, 4, 47, 19, 44, 59, 6, 0, 83, 40, 89, 39, 57, 99, 35, 81, 31, 89, 63, 80, 85, 36, 60, 17, 68, 62, 36, 3, 28, 8, 38, 83, 73, 67, 2]
plt.hist(grades, 10, facecolor='green', alpha=0.5)

排序(Sorting):列出top-N嚴重的空氣測站

排序章節可參考以下影片內容

我的目標是找到前n嚴重的空氣品質測站,最好的方法並不是一個一個找處最嚴重、次嚴重、第三嚴重的測站,而是把所有測站的值做排序(Sorting),排序後存成一個List,再用index來取出第n嚴重的站台。

1. Sorted by built-in sorted function

通常現在的程式語言都會提供一些必備、常用的函式,例如找到最大值或排序。預設排序方式是由小而大,加了reverse=True的參數後,會由大而小排序。

alist = [1, 3, 2, 4, 3, 5, 4, 7, 8]
print(sorted(alist))
print(sorted(alist, reverse = True))

[1, 2, 3, 3, 4, 4, 5, 7, 8] [8, 7, 5, 4, 4, 3, 3, 2, 1]

目前以下程式要找的是AQI的前n大,程式碼想法如下:

  1. 我先把所有的AQI的值給存起來,要注意要儲存為整數才能夠在後續正確地進行大小比較,文字和整數的排序方法不同,文字的排法是一個一個單字看,整數是看整個數字。

  2. 存起來以後,我用sorted()這個函式把這些值做排序。

  3. 排序後的List我就很方便取一個閾值(最大值、前四分之一大的值或者是前三大的值)。例如前三大的閾值,就剛好是該排序過的list的0, 1, 2,所以閾值就是print(AQI_list[2])。而前10大就是以AQI_list[9]為閾值。

  4. 然後我把大於等於該閾值的站台都給列印出來,即為所求。這邊要用大於等於(>=)的原因是,該閾值可能會涵蓋不只一個項目,使得該閾值以上的項目超過10個,此時,寧可多取也不要少取,所以用大於等於。

## Use a list to store all AQI value
AQI_list = []
for site in aqdata['Data']:
    AQI_list.append(int(site["AQI"]))
print(AQI_list)

print(sorted(AQI_list, reverse = True))
# print(AQI_list)
# print(sorted(AQI_list))

print(sorted(AQI_list, reverse = True))

for site in aqdata['Data']:
    if int(site['AQI']) >= sorted(AQI_list, reverse = True)[9]:
        print(site['SiteName'], site['AQI'], site['status'])

練習:找到AQI指標大於前四分之一的站台

可以用int(len(aqi_list)/4)或者是int(len(aqi_list)/4)-1來找出排序好的List的前25%項目,若一個List共有12個項目,int(len(aqi_list)/4)會是3,相當於第四個項目的值。

print(len(AQI_list)/4)
for site in aqdata['Data']:
    if int(site['AQI']) >= sorted(AQI_list, reverse = True)[int(len(AQI_list)/4)]:
        print(site['SiteName'], site['AQI'], site['status'])

2. Implement sorted function by sorting algorithm

在前面的例子我用了一個sorted函式就排序了所有的AQI的值,那這類sorted函式是怎麼寫的呢?我們可以用兩層的排序演算法來達成此事。觀念很簡單:

  1. 我先抓住第一個,一一和後面比較,看看有沒有人比他大,有的話,就和後面做置換(Swap),沒有的話就不動,這樣跑過一輪,就可以保證第一個最大。

  2. 接下來抓住第二個,一一和後面比較,看看有沒有人比他大,,有的話,就和後面做置換(Swap),沒有的話就不動,這樣跑過一輪,就可以保證第二個是次大的。

  3. 依此類推。

這邊先用一個簡單的List來解釋一下過程:

3, 6, 2, 1, 7 (i = 0, j = 1) # j位置的數大於i的,所以要調換i與j對應的數字 6, 3, 2, 1, 7 (i = 0, j = 2) # 6 > 2 6, 3, 2, 1, 7 (i = 0, j = 3) # 6 > 1 6, 3, 2, 1, 7 (i = 0, j = 4) # 6 < 7 j的大於i的,要對調(SWAP) 7, 3, 2, 1, 6 (i = 1, j = 2) # 3 > 2 7, 3, 2, 1, 6 (i = 1, j = 3) # 3 > 1 7, 3, 2, 1, 6 (i = 1, j = 4) # 3 < 6 要對調 7, 6, 2, 1, 3 (i = 2, j = 3) # 2 > 1 7, 6, 2, 1, 3 (i = 2, j = 4) # 2 < 3 要對調 7, 6, 3, 1, 2 (i = 3, j = 4) # 1 < 2 要對調 7, 6, 3, 2, 1

alist = [3, 6, 2, 1, 7]

for i in range(len(alist)-1):
    for j in range(i+1, len(alist)):
        if alist[i] < alist[j]:
            alist[i], alist[j] = alist[j], alist[i]
        print("[%d]%d [%d]%d: "%(i, alist[i], j, alist[j]), alist) 

Reference

只排序PM2.5的數值

pm_list = []
for site in aqdata["Data"]:
    if site['PM25'] != "" or site['PM25'] != "ND":
        pm_list.append(int(site['PM25']))
        
for i in range(len(pm_list)-1):
    for j in range(i+1, len(pm_list)):
        if pm_list[i] < pm_list[j]:
            pm_list[i], pm_list[j] = pm_list[j], pm_list[i]

print(pm_list)

[58, 57, 57, 56, 53, 52, 50, 50, 50, 47, 46, 45, 45, 45, 45, 44, 42, 42, 41, 41, 41, 40, 40, 40, 36, 36, 36, 35, 35, 33, 33, 32, 29, 27, 23, 22, 22, 21, 21, 21, 20, 20, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 12, 12, 12, 11, 11, 7, 5, -1, -1]

排序PM2.5的數值之時順便排序站名

pm_list = []
site_list = []
site_dict = {}
for site in aqdata["Data"]:
    if site['PM25'] != "" or site['PM25'] != "ND":
        pm_list.append(int(site['PM25']))
        site_list.append(site['SiteName'])
    site_dict[site['SiteName']] = site
        
for i in range(len(pm_list)-1):
    for j in range(i+1, len(pm_list)):
        if pm_list[j] > pm_list[i]:
            pm_list[i], pm_list[j] = pm_list[j], pm_list[i]
            site_list[i], site_list[j] = site_list[j], site_list[i]
for i in range(int(len(pm_list)/4)):
    print(site_list[i], pm_list[i], site_dict[site_list[i]]['AQI'], site_dict[site_list[i]]['status'])
        

print(pm_list)

Sorting by pythonic way

Tips. https://stackoverflow.com/questions/72899/how-do-i-sort-a-list-of-dictionaries-by-a-value-of-the-dictionary

Method 1.

newlist = sorted(list_to_be_sorted, key=lambda k: k['name'])

Method 2.

from operator import itemgetter newlist = sorted(list_to_be_sorted, key=itemgetter('name'))

Sorted by itemgetter

from operator import itemgetter
sortedlist = sorted(aqdata['Data'], key=itemgetter('AQI'), reverse = True) 
for site in sortedlist[:10]:
    print(site['SiteName'], site['AQI'], site['status'])

Sorted by lambda

sortedlist = sorted(aqdata['Data'], key=lambda k: k['AQI'], reverse = True) 
for site in sortedlist[:10]:
    print(site['SiteName'], site['AQI'], site['status'])

for: More applications

Generating fibonnacci sequence

費氏數列第一個為0,第二個為1,第三者之後為前兩項相加,請問第10項是多少? 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...

## Generating fibonnacci number sequence
# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...
a, b = 0, 1
for i in range(10):
    a, b = b, a+b
    print(i+1, "\t", b)

Generating Pi 3.1415...

Gregory and Leibniz found Pi/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9... https://mathworld.wolfram.com/PiFormulas.html

3.1415919869232103

x, s = 0.0, 1

for i in range(0,3000000,2):
    x += s*(1.0/(1+i))*4
    s= -s
print(x)

Generating 9x9 table

for j in range(1, 10):
    for i in range(1, 10):
        print("%2d*%-2d= %-2d "%(j, i, i*j), end="")
    print("")

Last updated