Advanced Materials of Python
Table of Contents
1. Function II
1.1. Function的特性
- Fnction為物件
1: def yell(text): 2: return text.upper() + '!' 3: 4: print(yell('hello')) 5: bark = yell # assign給其他變數 6: print(bark('woof'))
HELLO! WOOF!
- Function可被放在資料結構中
1: def yell(text): 2: return text.upper() + '!' 3: 4: bark = yell 5: 6: funcs = [bark, str.lower, str.capitalize] 7: 8: for func in funcs: 9: print(func('hey there!'))
HEY THERE!! hey there! Hey there!
- Function可做為參數傳給其他function
Function是物件,所以除了能指定給變數,更可以當成其他function的參數。
1: def yell(text): 2: return text.upper() + '!' 3: 4: bark = yell 5: 6: def greet(func): 7: greeting = func('Hi, I am a Python Program') 8: print(greeting) 9: 10: greet(bark)
HI, I AM A PYTHON PROGRAM!
這種能接鶢其他function做為參數的格式也稱為高階function(higher-order function)。Python內建的map function即為higher-order的經典範例,它接受一個function object與一個可走訪物件(iterable)做為參數,然後會套用該function到iterable object內的每一元素,產出一系列結果:
1: list = map(function, iterable object)
例如:
1: def yellp(text): 2: return text.upper() + '!' 3: 4: bark = yell 5: 6: list1 = ['hello', 'hey', 'hi'] 7: print(list(map(bark, list1)))
- Function可構成巢狀結構
在function內定義function,稱之為巢狀function(nested function)或內部function(inner function)
1: def speak(text): 2: def whisper(t): 3: return t.lower() + '...' 4: return whisper(text) 5: 6: print(speak('Hello, world'))
hello, world...
每次呼叫speak時,它會先定義內部function,而這個inner function在speak外並不存在。若一定要在speak之外存取內部function whisper,則要先把內部function傳回給父function的呼叫者:
1: def get_speak_func(volume): 2: def whisper(text): 3: return text.lower() + '...' 4: def yell(text): 5: return text.upper() + '!' 6: if volume > 0.5: 7: return yell 8: else: 9: return whisper 10: 11: print(get_speak_func(0.3)) 12: print(get_speak_func(0.7))
<function get_speak_func.<locals>.whisper at 0x101986af0> <function get_speak_func.<locals>.yell at 0x101986a60>
- Inner function可記住父function的參數狀態
Function還可「捕捉並保留父function的部份狀態」:
1: def get_speak_func(text, volume): 2: def whisper(): 3: return text.lower() + '...' 4: def yell(): 5: return text.upper() + '!' 6: if volume > 0.5: 7: return yell 8: else: 9: return whisper 10: 11: func = get_speak_func('Hello, world', 0.7) 12: print(func())
HELLO, WORLD!
這種會捕捉並記住外部參數的function,稱之為詞法閉包(lexical closuer),或簡稱閉包(closure)。閉包會記住外圍程式範圍裡的變數值,即便其程式流程已經離開該變數所在的範圍也一樣。
- 物件也能像function一樣被呼叫
在Python中,function皆為object,反之object不見得是function,不過,我們能讓不是function的object變成可呼叫(callable),許多情況下,甚至可以把callable object當成function,在後面加上小括號來呼叫它,甚至能傳入參數。要使object變為callable,方法是在類別加入__call__:
1: class Addr: 2: def __init__(self, n): 3: self.n = n 4: def __call__(self, x): 5: return self.n + x 6: 7: plus_3 = Addr(3) #建立Addr物件,屬性n=3 8: print(plus_3(4))
7
1.2. lambda
lambda提供了可用來宣告小型匿㢱function的快速宣告方式:
函式名稱 = lambda 參數:運算式
這種語法也稱為function expression,這與用def宣告的function一樣
1: add = lambda x, y : x + y 2: 3: print(add(5, 3))
8
那麼,以lambda定義function的優勢為何?
1: print((lambda x, y : x + y)(5, 3))
8
在許多場合中,使用lambda來定義臨時性的function就比較方便,但labmda function有個語法上的限制:只能含有一條運算式。
lambda function使用時機: 任何需要快速用上function object的地方,例如sorted():
1: sorted(iterable object, key=function)
其中參數key可輸入一個function, sorted()會依據function的傳回值來排序物件的元素,如果忽略key參數,則會依據原始元素(或者元素的第一個值)來排序。
1: tuples_list = [(1, 'd'), (2, 'b'), (4, 'a'), (3, 'c')] 2: print(sorted(tuples_list)) 3: 4: print(sorted(tuples_list, key=lambda x: x[1])) #指定key以tuple元素的第二子元素來排序 5: print(sorted(range(-5, 6), key=lambda x: x**2)) #指定依平方值來排序 6: # 上述方式只是為展示lambda用法,更精簡的方式為: 7: print(sorted(range(-5, 6), key=abs))
[(1, 'd'), (2, 'b'), (3, 'c'), (4, 'a')] [(4, 'a'), (2, 'b'), (3, 'c'), (1, 'd')] [0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5] [0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5]
使用lambda的缺點:不易閱讀
1.3. decorator
Python的修飾器(decorator)可用來間接修改callable object(function, method, class)的行為,但不直接改動object本身,其用途包括:
- 加入日誌(logging)
- 存取權限管控與身份驗證
- 加入監測function及衡量執行時間
- 限制function執行頻率
- 用快取暫存function的執行結果
- 修飾無參數function
1: def uppercase(func): 2: def wrapper(): 3: original_result = func() 4: modified_result = original_result.upper() 5: return modified_result 6: return wrapper 7: 8: @uppercase #以uppercase修飾greet 9: def greet(): 10: return 'Hello' 11: 12: print(greet())
HELLO
- 多重修飾
1: def strong(func): 2: def wrapper(): 3: return f'<strong> { func() } </strong>' 4: return wrapper 5: 6: def emphasis(func): 7: def wrapper(): 8: return f'<em> { func() } </em>' 9: return wrapper 10: 11: @strong 12: @emphasis 13: def greet(): 14: return 'Hello!' 15: 16: print(greet) 17: print(greet())
<function strong.<locals>.wrapper at 0x10fd3ab80> <strong> <em> Hello! </em> </strong>
唯一要注意的是:若套用修飾器的層級過多會影響程式執行效能。
- 修飾有傳入參數的function
若要修飾的function有帶參數,則wrapper()要改為wrapper(*args, **kwargs),這分別用來收集所有傳入的positional與keyword類參數,以tuple及dict形式儲存在arfs與kwargs變數裡,然後wrapper利用*與**將收集到的參數unpack後傳給原始func,如此就不會限制到參數個數。
1: def trace(func): 2: def wrapper(*args, **kwargs): 3: print(f'trace: callable function {func.__name__}, with arguments: {args}, {kwargs}') 4: original_results = func(*args, **kwargs) 5: print(f'trace: function {func.__name__} reutrn results: {original_results}!') 6: return original_results 7: return wrapper 8: 9: @trace 10: def say(name, line): 11: return f'{name}, {line}' 12: 13: print(say('James', 'hi'))
trace: callable function say, with arguments: ('James', 'hi'), {} trace: function say reutrn results: James, hi! James, hi幫function加上效能監測功能
1: import time 2: def eval_time(func): 3: def wrapper(*args, **kwargs): 4: start = time.perf_counter() 5: res = func(*args, **kwargs) 6: finish = time.perf_counter() 7: print(f'Finished in {round(finish-start, 2)} second(s)') 8: return res 9: return wrapper 10: 11: @eval_time 12: def sigma(n): 13: sum = 0 14: for i in range(n): 15: sum = sum + i 16: return sum 17: 18: print(sigma(100000000))
Finished in 5.3 second(s) 4999999950000000
1.4. *args and **kwargs
*與**能讓function接受數量不定的額外參數,讓module與class能提供更有彈性的存取介面:
1: def foo(required, *args, **kwargs): 2: print(required) 3: if args: 4: print(f'args: {args}') 5: if kwargs: 6: print(f'kwargs: {kwargs}') 7: 8: foo('1. Hi') 9: foo('2. Hi', 1, 2, 3) 10: foo('3. Hi', 1, 2, 3, Key1='value1', key2=456)
1. Hi
2. Hi
args: (1, 2, 3)
3. Hi
args: (1, 2, 3)
kwargs: {'Key1': 'value1', 'key2': 456}
- *args接收額外的位置型參數(positional paramenters)(沒有鍵的參數),將之放入一個tuple。
- **kwargs接收額外的關鍵字參數(keyword parameters)(有鍵的參數),將之放入一個dict。
1.5. Unpack function parameters
*與**也可用來unpack function的args與kwargs,在呼叫function時,若在iterable object前加上*,就會unpack這個物件,將元素當成個別的positional parameters傳入參數;若加上**,則可unpack keyword parameters。
1: def print_vector(x, y, z): 2: print(f'<{x}, {y}, {z}>') 3: 4: tuple_vector = (1, 0, 1) 5: print_vector(tuple_vector[0], tuple_vector[1], tuple_vector[2]) 6: print_vector(*tuple_vector) 7: 8: dict_vector = { 'y': 4, 'z': 5, 'x': 3} 9: print_vector(**dict_vector)
<1, 0, 1> <1, 0, 1> <3, 4, 5>
1.6. Return or not
- return 與return None效果一樣
- 如果沒有傳回值,則可省略return,例如,一個只負責print的function並無任值可傳回,寫return會很奇怪。
2. Class v.s. Object
2.1. TODO 什麼是class? 什麼是object?
把這個看完: https://youtu.be/JeznW_7DlB0:
Everything in Python is object.
1: a = 2022 2: b = 'TNFSH' 3: 4: def c(): 5: print('TNFSH') 6: 7: print(type(a)) 8: print(type(b)) 9: print(type(c))
<class 'int'> <class 'str'> <class 'function'>
由上面的例子,我們可以發現:在Python裡,幾乎所有我們用到的變數、函數都是class。
當我們寫下
1: a = 2022
其實是在說,我們有一個變數,它是一種int class,它的值是2022。這個變數就是一個object: 某種屬於某一類class的實體。
一種常見的說法是:class是一張藍圖,依照這張藍圖蓋出的房子有同樣的架構,有同樣的功能,但是可能在外觀或顏色上會有些許差異,就好比一樣是整數物件,每個變數的值可能會有所不同。
不同的class有其特定的功能與限制,例如:
1: x = 1 2: y = 'TNFSH' 3: print(x+y)
執行上述程式會得到以下錯誤訊息:
File "<stdin>", line 3, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'str'
這裡告訴我們:int與str這兩種class的object不支援+這個運算。我們再來看另一個例子:
1: x = 'tnfsh' 2: y = 1 3: print(x.upper()) 4: print(y.upper())
TNFSH
上述程式會產生如下錯誤訊息:
#+begin_src shell -r -n :results output :exports both
Traceback (most recent call last):
File “<stdin>”, line 4, in <module>
AttributeError: ’int’ object has no attribute ’upper’
#+end_src<<sh
意思是:int這種class的object沒有一種叫做upper的屬性。這就是我們上面所說的,不同的class有其特定的功能與限制。
2.2. 建立自己的class
1: class Dog: 2: def bark(self): 3: print('汪汪汪') 4: 5: a = Dog() 6: print(type(a)) 7: a.bark()
<class '__main__.Dog'> 汪汪汪
在上述範例中,我們建立了一個叫Dog的class,然後再建立一個屬於Dog這個class的object。
從這個object的type,可以看出這是一個叫<class ’main.Dog’>的類別,這裡的__main__意思是這個class被定義(或撰寫)在目前這支Python程式中,當然我們也可以另外開一個新的.py檔專問來儲存這個class。
2.3. == v.s. is
- ==: equal
- is: identical
1: a = [1, 2, 3] 2: b = [1, 2, 3] 3: c = a 4: print(a == b) 5: print(a is b) 6: print(a == c) 7: print(a is c)
True False True True
2.4. Shallow copy v.s. Deep copy
簡單來說,淺與深的區別1:
- 淺複製僅複製容器中元素的地址
- 深複製完全複製了一份副本,容器與容器中的元素地址都不一樣
- shallow copy
1: xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 2: ys = list(xs) # shallow copy 3: print(xs == ys) # 二者內容相同 4: print(xs is ys) # 但為不同物件 5: 6: xs.append([10, 11, 12]) 7: print(xs) 8: print(ys) 9: 10: # 但若是修改二者相同的部份,如 11: xs[1][0] = 'X' 12: print(xs) 13: print(ys)
True False [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]] [[1, 2, 3], [4, 5, 6], [7, 8, 9]] [[1, 2, 3], ['X', 5, 6], [7, 8, 9], [10, 11, 12]] [[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
由上述例子中可發現,xs與ys其實共享同一份資料。
- deep copy
1: import copy 2: xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 3: zs = copy.deepcopy(xs) 4: 5: print(xs == zs) 6: print(xs is zs) 7: 8: xs[1][0] = 'X' 9: print(xs) 10: print(zs)
True False [[1, 2, 3], ['X', 5, 6], [7, 8, 9]] [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
- copy any object
不管想copy什麼object,包括自訂物件,解決之道亦是用copy module的copy()與deepcopy()。
1: import copy 2: 3: class Point: 4: def __init__(self, x, y): 5: self.x = x 6: self.y = y 7: def __repr__(self): 8: return f'Point({self.x}, {self.y})' 9: 10: a = Point(23, 42) 11: b = Point(23, 42) 12: #b = copy.copy(a) 13: print(a) 14: print(b) 15: print(a == b) 16: print(a is b)
Point(23, 42) Point(23, 42) False False
2.5. ABC: Abstract Base Class
C++、Java等語言都有「介面」(interface)這種本身無法建立object、卻能當成建立object template的機制。Python則可藉由ABC來實作。藉由ABC來建立class,便是可確保繼承之sub-class均有正確的特定method。
1: from abc import ABC, abstractmethod 2: 3: class Base(ABC): 4: 5: @abstractmethod 6: def foo(self): 7: pass 8: 9: @abstractmethod 10: def bar(self): 11: pass 12: 13: class Concrete(Base): 14: def foo(self): 15: pass 16: 17: print(issubclass(Concrete, Base))
True
2.6. object/class/static method
1: class MyClass: 2: 3: def objmethod(self): #object method 4: print(f'呼叫object method: {self}') 5: 6: @classmethod 7: def clsmethod(cls): 8: print(f'呼叫class method: {cls}') 9: 10: @staticmethod 11: def stcmethod(): 12: print(f'呼叫static method') 13: 14: obj = MyClass() 15: obj.objmethod() 16: obj.clsmethod() 17: obj.stcmethod()
呼叫object method: <__main__.MyClass object at 0x1081f7940> 呼叫class method: <class '__main__.MyClass'> 呼叫static method
- objmethod為一般object method,必須有self參數,該參數指向object本身
- csmethod為class method,必項有cls參數,指向class本身
- stcmethod為static method,沒有參數,無法修改物件或類別狀態,只能處理user傳入的參數
- DEMO
1: class Drink: 2: def __init__(self, name, price, counts): 3: self.name = name 4: self.price = price 5: self.counts = counts 6: 7: def costs(self): 8: return self.drink_costs(self.price, self.counts) 9: 10: @classmethod 11: def blacktea(cls): 12: return cls('紅茶', 1, 25) 13: 14: @classmethod 15: def coffee(cls): 16: return cls('熱美式', 1, 50) 17: 18: @staticmethod 19: def drink_costs(price, counts): 20: return price * counts 21: 22: drink1 = Drink.blacktea() 23: drink2 = Drink.coffee() 24: drink3 = Drink('義式', 60, 3) 25: print(f'{drink1.name}') 26: print(f'{drink2.price}') 27: print(f'{drink3.name!r} * {drink3.counts} = {drink3.costs()}')
紅茶 1 '義式' * 3 = 180
2.7. class variable v.s. instance variable
- class variable: 宣告在classs定義裡,但宣告位置在所有method之外
- instance variable: 屬於由class所建立的某個instance,其內容並非存在class中,而是由各別instance負責
- 差異
1: class Dog: 2: num_dogs = 0 #Demo class variable的適用時機 3: num_leg = 4 4: 5: def __init__(self, name): 6: self.name = name 7: self.__class__.num_dogs += 1 8: 9: dd = Dog('DaiDai') 10: bd = Dog('Bad luck') 11: lk = Dog('Lucky') 12: bd.num_leg = 3 13: print(dd.num_leg) 14: print(bd.num_leg) 15: print(f'目前共有{Dog.num_dogs}隻狗')
4 3 目前共有3隻狗
由class variable num_dogs的示範也可看出,善用class variable可以在各個instance object間取得某程程度的連繫。
3. 資料型別 II
3.1. Dict
- collections.ChainMap
collections.ChainMap可以把多個dict集結串成單一個dict,如
1: import collections 2: 3: dict1 = {'one':1, 'two': 2} 4: dict2 = {'two':'貳', 'three': 'III', 'four': 4} 5: chain = collections.ChainMap(dict1, dict2) 6: 7: print(chain) 8: print(chain['two']) 9: chain['five'] = 5 10: print(chain)
ChainMap({'one': 1, 'two': 2}, {'two': '貳', 'three': 'III', 'four': 4}) 2 ChainMap({'one': 1, 'two': 2, 'five': 5}, {'two': '貳', 'three': 'III', 'four': 4})要留意的是:若對ChainMap做新增、修改、刪除,則只會影響裡面的第一個dict。
3.2. types.MappingProxyType
透過MappingProxyType class可以把dict變為唯讀。
1: from types import MappingProxyType 2: 3: writable = {'one': 1, 'two': 2} 4: writable['three'] = 3 5: readOnly = MappingProxyType(writable) 6: readOnly['three'] = 4 #TypeError: 'mappingproxy' object does not support item assignment 7: readOnly['four'] = 4 #TypeError: 'mappingproxy' object does not support item assignment
3.3. array.array
若要和C語言程式交換資料,或是只需要一個儲存數值型態資料的空間,則可用array.array,優點是比list或tuple省空間。array.array的method與list大致相同,許多情況下甚至可直接交換二者的資料型別而無需修改相關程式碼。
1: import array 2: arr = array.array('f', (1.0, 3.5, 2.0, 6.1)) 3: print(arr) 4: print(arr[1]) 5: arr.append(2.1) 6: del arr[1] 7: print(arr)
array('f', [1.0, 3.5, 2.0, 6.099999904632568])
3.5
array('f', [1.0, 2.0, 6.099999904632568, 2.0999999046325684])
3.4. Record
同樣以儲存car record(color, mileage, automatic)來比較
- list/tuple
- 建立速度快、執行效率高,
- 但沒有欄位名稱,建立資料時可能弄錯順序
- 很難檢查兩個資料物件是否有一致的欄位
1: car1 = ['red', 3812, True] #list, 可變 2: car2 = ('blue', 3123, False) #tuple, 不可變 3: car3 = (343, 'black', True, 'James') #順序錯誤、欄位也不一致
- 建立速度快、執行效率高,
- dict
- 可透過key/value來當成欄位名稱
- 仍缺乏欄位監控機制,防止使用者在建立object時打錯或漏打欄位
1: Car = { 2: 'color': 'red', 3: 'mileage': 3213, 4: 'automatic': True 5: } 6: print(Car) 7: print(Car['color']) 8: Car['windshield'] = 'broken' 9: print(Car)
{'color': 'red', 'mileage': 3213, 'automatic': True} red {'color': 'red', 'mileage': 3213, 'automatic': True, 'windshield': 'broken'} - 可透過key/value來當成欄位名稱
- 自訂類別
- 麻煩,在要__init__建構子內設定每個欄位,還要自己寫__repr__ method
1: class Car: 2: def __init__(self, color, mileage): 3: self.color = color 4: self.mileage = mileage 5: 6: car = Car('red', 1234) 7: car.mileage = 4343 #可修改欄位內容 8: car.automatic = True #可新增欄位
- 麻煩,在要__init__建構子內設定每個欄位,還要自己寫__repr__ method
- typing.NamedTuple
- readonly
- 欄位型別無強制性
1: from typing import NamedTuple 2: 3: class Car(NamedTuple): 4: color: str 5: mileage: float 6: automatic: bool 7: 8: car = Car('red', 3123, True) 9: print(car) 10: #car.mileage = 3123 # 不能變更欄位內容 11: #car.windshield = 'broken' #不能新增欄位 12: car2 = Car('blue', 'test', 1234) 13: print(car2)
Car(color='red', mileage=3123, automatic=True) Car(color='blue', mileage='test', automatic=1234)
- readonly
- types.SimpleNamespace
- 本質上是dict,把dict的key變成class attribute
- 可以新增、修改、刪除attribute
- 不若想動用到class,此為簡易替代品
- 仍缺乏結構
1: from types import SimpleNamespace 2: 3: car = SimpleNamespace(color = 'red', 4: mileage = 1341, 5: automatic = True) 6: 7: car.color = 'blue' 8: print(car)
namespace(color='blue', mileage=1341, automatic=True)
- 本質上是dict,把dict的key變成class attribute
- dataclass (Python 3.7+)
- 能自動建立__init__、__repr__等method
- 可加入自訂method
- 可繼承
- 可設為read only(在@dataclass後加入參數 frozen=True)
1: from dataclasses import dataclass 2: 3: @dataclass 4: class Car: 5: color: str 6: mileage: float 7: automatic: bool 8: 9: def mileage_km(self): #可自訂method 10: return self.mileage * 1.609 11: 12: car = Car('red', 3123, True) 13: print(dir(car)) 14: print(car.mileage_km) 15: print(car.__repr__) 16: 17: @dataclass 18: class ElectronicCar(Car): #繼承 19: charge: float = 0.0 20: 21: car2 = ElectronicCar('white', 3123, True, 1000) 22: print(car2) 23:
['__annotations__', '__class__', '__dataclass_fields__', '__dataclass_params__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'automatic', 'color', 'mileage', 'mileage_km'] <bound method Car.mileage_km of Car(color='red', mileage=3123, automatic=True)> <bound method __create_fn__.<locals>.__repr__ of Car(color='red', mileage=3123, automatic=True)> ElectronicCar(color='white', mileage=3123, automatic=True, charge=1000)
- __slots__
如果你的程式會建立同一類別的大量object,但不會增刪object attribute,則可以考慮使用__slots__來節省object占用的記憶體,同時加快存取速度。
1: from dataclasses import dataclass 2: 3: @dataclass 4: class Car: 5: 6: __slots__ = ['color', 'mileage', 'automatic'] 7: 8: color: str 9: mileage: float 10: automatic: bool
__slots__不限於dataclass,在任何class均可使用
- 能自動建立__init__、__repr__等method
- struct.struct
- 與C的struct交換資料的管道
1: from struct import Struct 2: 3: MyStruct = struct('1?f') #指定資料格式 4: data = MyStruct.pack(23, False, 42.0) #資料打包成二進位資料 5: print(data) 6: x = MyStruct.unpack(data) 7: print(x)
- 與C的struct交換資料的管道
3.5. Set
- Python建立set,除了用{}、也可以用set comprehension
- 若要建立empty set,一定要call set()建構子,因為空的{}會被當成dict
1: vowels = {'a', 'e', 'i', 'o', 'u'} 2: squares = { x ** 2 for x in range(10) } 3: empty = set() 4: 5: print(vowels) 6: print(squares) 7: print(empty)
{'a', 'o', 'i', 'u', 'e'}
{0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
set()
- frozenset
- frozenset class為set的不可變版本,一旦建立就無法增刪修,只能查詢
- 但frozenset為靜態iterable object,故為hashable object,能當成dict object的key或其他set的element,這是一般set無法做到的事
1: vowels = frozenset({'a', 'e', 'i', 'o', 'u'}) 2: d = {vowels: 'hello'} # set變為dict的key 3: print(d) 4:
{frozenset({'e', 'o', 'u', 'a', 'i'}): 'hello'} - frozenset class為set的不可變版本,一旦建立就無法增刪修,只能查詢
- collections.Counter
- collections module底下的Counter實作了「多重集合」(multisets),能輸入多個iterable object(set, dict, string)並統整各元素的出現次數
- Counter實際為dict的subclass,當我們把新的iterable object輸入Counter object時,該iterable object的element會新增為dict的key,而該element出現次數則為dict的value
1: from collections import Counter 2: 3: inventory = Counter() 4: loot = {'sward': 1, 'bread': 3} #裝備品名及數量 5: inventory.update(loot) 6: print(inventory) 7: 8: more_loot = ['sward', 'coins'] 9: inventory.update(more_loot) 10: 11: yet_more_loot = ['sward', 'bread', 'drink'] 12: inventory.update(yet_more_loot) 13: print(inventory)
Counter({'bread': 3, 'sward': 1}) Counter({'bread': 4, 'sward': 3, 'coins': 1, 'drink': 1}) - collections module底下的Counter實作了「多重集合」(multisets),能輸入多個iterable object(set, dict, string)並統整各元素的出現次數
3.6. Stack
幾種實作Stack的結構
- list
- python內部以動態陣列來實作list,所以會配置比element所需更大的空間,視需要增減。故push與pop不見會需要調整大小,平均效率可達O(1)
- 但為了達到這種效率,element必須從tail做push, pop
- 若改由list的head做push,pop,則list內所有element均需位移,效率會降為O(n)
1: a = [] 2: a.append('eating') 3: a.append('sleeping') 4: a.append('coding') 5: print(a) 6: a.pop() 7: print(a) 8: 9: #自head做push, pop 10: a.insert(0, 'drinking') 11: a.insert(0, 'playing') 12: a.pop(0) 13: print(a)
['eating', 'sleeping', 'coding'] ['eating', 'sleeping'] ['drinking', 'eating', 'sleeping']
- python內部以動態陣列來實作list,所以會配置比element所需更大的空間,視需要增減。故push與pop不見會需要調整大小,平均效率可達O(1)
- collections.deque
- 快速穩定的stack
- 支援從head,tail做push,pop,效率均為O(1)
- 若要取出中間element,效率為O(n)
1: from collections import deque 2: 3: a = deque() 4: 5: a.append('eating') 6: a.append('sleeping') 7: a.append('coding') 8: print(a) 9: a.pop() 10: print(a) 11: 12: #自中間取值,效能差 13: print(a[1]) 14:
deque(['eating', 'sleeping', 'coding']) deque(['eating', 'sleeping']) sleeping
- 快速穩定的stack
- queue.LifoQueue
- 可用於multithreadin進行parallelism平行運算時的共享資料
- 這些資料結構還提供了上鎖機制,好讓同一時間只有一個thread能從queue取出資料
- queue module裡的LifoQueue class雖名為queue,但採Last In, First Out(LIFO),運作上與Stack相同
1: from queue import LifoQueue 2: s = LifoQueue() 3: s.put('eating') 4: s.put('sleeping') 5: s.put('coding') 6: print(s.get()) 7: print(s.get())
coding sleeping
- 可用於multithreadin進行parallelism平行運算時的共享資料
3.7. Queue
幾種實作queue的資料結構
- list
- 非常慢,沒有效率
- 非常慢,沒有效率
- collections.deque
- 快速穩定
1: from collections import deque 2: q = deque() 3: q.append('eating') 4: q.append('sleeping') 5: q.append('coding') 6: q.append('writing') 7: print(q) 8: print(q.popleft()) 9: print(q.popleft()) 10: print(q) 11:
deque(['eating', 'sleeping', 'coding', 'writing']) eating sleeping deque(['coding', 'writing'])
- 快速穩定
- queue.Queue
- 和queue.LifoQueue一樣,內建上鎖機制,可以用來讓multithreading共享資料或任務
- 和queue.LifoQueue一樣,內建上鎖機制,可以用來讓multithreading共享資料或任務
- multiprocessing.Queue
- multi-thread在python中其實不算真正的平行運算,各thread實際上是在同一個interpreter下執行,只用到一個core,藉由不斷切換的方式來達到平行運算的效果。
- multi-thread在python中其實不算真正的平行運算,各thread實際上是在同一個interpreter下執行,只用到一個core,藉由不斷切換的方式來達到平行運算的效果。
- priority queue
- 不遵循FIFO原則,而是以priority為順序考量
- priority由totally-ordered key來決定
- 不遵循FIFO原則,而是以priority為順序考量
- heapq
- 借用list實作的binary tree(heap)結構
- 可實作priority
- 可於O(log n)的時間完成插入元素或取出最小元素
- heapq的各note實際上是存在一個list中;node在list中是依binary tree的順序儲存
- 借由binary tree所依據的sort key即可實作出priority queue
- 限制之一是預設為由小到大排序

Figure 1: heapq
1: import heapq 2: 3: q = [] 4: heapq.heappush(q, (2, 'coding')) 5: heapq.heappush(q, (1, 'eating')) 6: heapq.heappush(q, (3, 'sleeping')) 7: while q: 8: next_item = heapq.heappop(q) 9: print(next_item)
(1, 'eating') (2, 'coding') (3, 'sleeping')
- 借用list實作的binary tree(heap)結構
- queue.PriorityQueue
- 可用於multi-thread的heapq
**
- 可用於multi-thread的heapq
4. LOOP
4.1. enumerate()
- 如果想保留for-each的寫法,但同時又希望能取得每個element的index
1: my_items = ['a', 'b', 'c'] 2: print(list(enumerate(my_items))) 3: for i, item in enumerate(my_items): 4: print(f'{i}: {item}')
[(0, 'a'), (1, 'b'), (2, 'c')] 0: a 1: b 2: c
4.2. zip()
- 可用來同時走訪多個container
- 可用來旋轉二維list
1: my_items = ['a', 'b', 'c'] 2: my_no = [1, 2, 3] 3: print(list(zip(my_no, my_items))) 4: 5: for no, item in zip(my_no, my_items): 6: print(f'{no}: {item}')
[(1, 'a'), (2, 'b'), (3, 'c')] 1: a 2: b 3: c
4.3. comprehension
- 即簡單的單行for-loop
- 可以讓我們用單行程式產生出包含特定元素的list, set, dict
- 語法
1: list = [運算式 for 變數 in iterable object]
- list comprehension
1: squares = [x ** 2 for x in range(10)] 2: print(squares)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
- comprehension過濾條件
1: list = [x ** 2 for x in range(10) if x % 2 == 0] 2: print(list)
[0, 4, 16, 36, 64]
- set與dict comprehension
1: setA = [x ** 2 for x in range(10)] 2: print(setA) 3: dicA = {x: x ** 2 for x in range(10)} 4: print(dicA)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
4.4. list
- slice
語法
1: list[index start: index end: step]
1: nums = [1, 2, 3, 4, 5, 6, 7, 8] 2: print(nums[::]) 3: print(nums[1:6:2]) 4: nums[1:6:2] = [10, 20, 30] 5: print(nums) 6:
[1, 2, 3, 4, 5, 6, 7, 8] [2, 4, 6] [1, 10, 3, 20, 5, 30, 7, 8]
- copy
1: l = list(range(6)) 2: print(l) 3: cl = l # 指向同一list 4: dl = l[::] # shallow copy 5: l[3] = 10 6: print(cl) 7: print(dl)
[0, 1, 2, 3, 4, 5] [0, 1, 2, 10, 4, 5] [0, 1, 2, 3, 4, 5]
5. Python 套件管理
5.1. pip v.s. conda
- pip or conda
- Python 的一大優勢之一便是龐大的第三方函式庫,讓使用 python 的程式設計師可以方便的呼叫、進行如網路資料下載解析、資料的視覺化、甚或是大數據的複雜分析與人工智慧的相關套件。,
- 目前用來管理這些龐大套件的工具主要有二:pip 與 Conda。
- Python 的一大優勢之一便是龐大的第三方函式庫,讓使用 python 的程式設計師可以方便的呼叫、進行如網路資料下載解析、資料的視覺化、甚或是大數據的複雜分析與人工智慧的相關套件。,
- pip
- Pip是Python Packaging Authority推薦、用於從Python Package Index安裝套件的工具,提供了對 Python 套件的搜㝷、下載、安裝、卸載的功能。
- 若在 python.org 下載最新版本的 python,則已內建 pip 安裝套件。 Python 3.4+ 以上版本均已包括 pip
- 該工具類似 Linux 下的 apt/yum 或 MAC 下的Homebrew。
- Pip是Python Packaging Authority推薦、用於從Python Package Index安裝套件的工具,提供了對 Python 套件的搜㝷、下載、安裝、卸載的功能。
- conda
- Conda 是一個開源的跨平台工具軟體,它被設計作為 Python、R、Lua、Scala、C/C++、FORTRAN/ 與 Java 等任何程式語言的套件、依賴性以及工作環境管理員,特別受到以 Python 作為主要程式語言的資料科學團隊所喜愛。
- 適用平台:Windows, macOS, Linux
- 傳統 Python 使用者以 pip 作為套件管理員(package manager)、以 venv 作為工作環境管理員(environment manager),而 conda 則達成了「兩個願望、一次滿足」既可以管理套件亦能夠管理工作環境。2
- Conda 是一個開源的跨平台工具軟體,它被設計作為 Python、R、Lua、Scala、C/C++、FORTRAN/ 與 Java 等任何程式語言的套件、依賴性以及工作環境管理員,特別受到以 Python 作為主要程式語言的資料科學團隊所喜愛。
- In both cases:
- Written in Python
- Open source (Conda is BSD and pip is MIT)
- 五種開源授權規範的比較 (BSD, Apache, GPL, LGPL, MIT)
- Written in Python
- difference between conda, anaconda, and miniconda
- conda is both a command line tool, and a python package. 3
- Anaconda 發行版會預裝很多套件,而 Miniconda 是最小的 conda 安裝環境, 一個乾淨的 conda 環境。
- pip 只是運與安裝 python package,而 conda 用來安裝管理任何語言的包。
不一定要安裝 Anaconda 或 Miniconda,也可透過 pip 直接安裝 conda
pip install conda
- conda is both a command line tool, and a python package. 3
5.3. python package 安裝(conda)
安裝 package:
conda install packageName
conda install pandas
移除 package:
conda remove packageName
conda remove pandas
安裝特定版本 python
conda install python=version
conda install python=3.5
了解目前系統可用套件
conda list
5.4. Python 常用函式庫
- 爬蟲
- Scrapy:

- Scrapy,Python 開發的一個快速、高層次的 web 數據抓取框架,用於抓取 web 站點並從頁面中提取結構化的數據。Scrapy 用途廣泛,可以用於數據挖掘、監測和自動化測試4。
- Scrapy 吸引人的地方在於它是一個框架,任何人都可以根據需求方便的修改。它也提供了多種類型爬蟲的基類,如 BaseSpider、sitemap 爬蟲等。
- Scrapy,Python 開發的一個快速、高層次的 web 數據抓取框架,用於抓取 web 站點並從頁面中提取結構化的數據。Scrapy 用途廣泛,可以用於數據挖掘、監測和自動化測試4。
- beautifulsoup4:
- Selenium
- Scrapy:
- 網站
- Django

- Django (ˈdʒæŋɡoʊ jang-goh) 可以說是 Python 最著名的 Web Framework,一些知名的網站如 Pinterest, Instagram, Disqus 等等都使用過它來開發。9
- 免費開放原始碼
- 著重快速開發、高效能
- 遵從 DRY ( Don’t Repeat Yourself ) 守則,致力於淺顯易懂和優雅的程式碼
- 使用類似 Model–view–controller (MVC) pattern 的架構
- 10 Popular Websites Built With Django
- Django (ˈdʒæŋɡoʊ jang-goh) 可以說是 Python 最著名的 Web Framework,一些知名的網站如 Pinterest, Instagram, Disqus 等等都使用過它來開發。9
- Flask

- Flask 是一個使用 Python 撰寫的輕量級 Web 應用程式框架,由於其輕量特性,也稱為
micro-framework(微框架)。10 - Flask 和 Django 不同的地方在於 Flask 給予開發者非常大的彈性(當然你也可以說是
需要思考更多事情),可以選用不同的用的 extension 來增加其功能。 - 相比之下,Django 雖然完善但技術選擇相對不彈性,不論是 ORM、表單驗證或是模版引
擎都有自己的作法。 - 沒有最好的框架,只有合適的使用情境。
- Flask 是一個使用 Python 撰寫的輕量級 Web 應用程式框架,由於其輕量特性,也稱為
- Django
- 資料處理科學計算
- Numpy

- Numpy 底層以 C 和 Fortran 語言實作,所以能快速操作多重維度的陣列。11
- 當 Python 處理龐大資料時,其原生 list 效能表現並不理想(但可以動態存異質資料),而 Numpy 具備平行處理的能力,可以將操作動作一次套用在大型陣列上。
- 此外 Python 其餘重量級的資料科學相關套件(例如:Pandas、SciPy、Scikit-learn 等)都幾乎是奠基在 Numpy 的基礎上。
- Numpy 底層以 C 和 Fortran 語言實作,所以能快速操作多重維度的陣列。11
- Scipy

- 科學計算神器
- Numpy 是以矩陣基礎做數據的數學運算,SciPy 就是以 Numpy 為基礎做科學、工程的運算處理的 package,包含統計、優化、整合、線性代數、傅立葉轉換圖像等較高階的科學運算。12
- 科學計算神器
- Pandas

- 建構在 NumPy 之上,提供資料結構與資料處理工具,讓資料清理與分析更為快速與方便
- 適合處理表格或異質資料 (NumPy 適合處理同質之數值陣列資料)
- 建構在 NumPy 之上,提供資料結構與資料處理工具,讓資料清理與分析更為快速與方便
- Numpy
- 視覺化
- matplotlib

- Matplotlib 就是 MATLAB+Plot+Library 的簡稱,因為是模仿 MATLAB 建立的繪圖庫,所以繪
圖風格會與 MATLAB 有點類似。13 - 為了提高處理大量資料的性能,Matplotlib 大量使用了 NumPy 和其相關的擴展代碼。 為了方便快速繪圖, Matplotlib 通過 pyplot 模組提供了一套和 MATLAB 類似的繪圖 API,只需要調用 pyplot 模組所提供的函數,就可以實現快速繪圖及設置圖表的各種細節。
- 另一方面 Matplotlib 也適合互動式繪製圖表,可以很方便地處理二維和三維的圖表。
- Matplotlib 就是 MATLAB+Plot+Library 的簡稱,因為是模仿 MATLAB 建立的繪圖庫,所以繪
- seaborn
- ggplot

- R 語言視覺化神器的 Python 版本
- ggplot2 是一個十分強大的 R 語言可視化包。它的核心理念是將繪圖與數據分離,數據相關
的繪圖與數據無關的繪圖分離。16 - 它是按圖層作圖的,一個語句做一個僅包含基礎作圖單元的圖層,然後通過不同圖層的疊
加最後成圖。
- R 語言視覺化神器的 Python 版本
- plotly

- 這個神器是個 js 庫,不過也有各種流行的語言介面
- plotly 是一個能讓你畫出互動式圖表的一個開源套件,其基本操作是免費的,如果你想要使用網頁的版本,可以使用更多 plotly 的進階功能的話,就必須付費
- 這個神器是個 js 庫,不過也有各種流行的語言介面
- matplotlib
- 機器學習
- scikit-learn

- 幾乎所有機器學習演算法都囊括
- scikit-learn,又寫作 sklearn,是一個開源的基於 python 語言的機器學習工具包。它通過 NumPy, SciPy 和 Matplotlib 等 python 數值計算的庫實現高效的算法應用,並且涵蓋了幾乎所有主流機器學習算法。17
- sklearn 中常用的 module 有分類、回歸、聚類、降維、模型選擇、預處理。
- 幾乎所有機器學習演算法都囊括
- NLTK
- TensorFlow

- Tensorflow 最初為 Google Brian 所開發。在 2015 時,Google 將之開源,為現今重要的深
度學習框架之一,它支援各式不同的深度學習演算法,並已應用於各大企業服務上,Ex:
Google, Youtube, Airbnb, Paypal … 等。19 - 此外,Tensorflow 也支援在各式不同的
device 上運行深度學習 Ex: Tensorflow Lite 、 Tensorflow.js 等等。 - Tensorflow 為目
前最受歡迎的機器學習、深度學習開源專案。不管是 github fork 的數量、論文的使用次
數以及熱門程度,均比其他的框架來的多20。
深度學習套件
- Tensorflow 最初為 Google Brian 所開發。在 2015 時,Google 將之開源,為現今重要的深
- Keras

- 在 2015 年 TensorFlow 推出的同時,美國麻省理工學院(又是它,這學校開門就能賺錢)
也推出一套能很容易被使用者透過 Python 寫 Deep learning 的應用程式介面 API ,叫
做 Keras 。 - Keras 只有介面喔! Keras 本身還是得透過 TensorFlow ,或者其
他像是微軟的 CNTK 這類引擎當作底層,才能執行。21 - Keras 使用上比較接近人類的想法( TensorFlow 設計上沒有錯,只不過比較是針對電腦
系統跟網路通訊的想法),所以透過 Python 呼叫 Keras 能夠很簡單地描述我們人類想
要電腦達到 Deep Learning 要做的事情。目前在教學上, Keras 的普及率相當高 - Keras 可以快速有方便運算的主要原因是,它已經將訓練模型的輸入層、隱藏層、輸出層,做好架構,使用者只需要加入並且填寫正確的參數 ex.神經元個數、activation function 的函式…等。22
- 在 2015 年 TensorFlow 推出的同時,美國麻省理工學院(又是它,這學校開門就能賺錢)
- PyTorch

- Numpy 的 GPU 版
- PyTorch 為 Facebook 在 2017 年初開源的深度學習框架,其建立在 Torch 之上,且標
榜 Python First ,為量身替 Python 語言所打造,使用起來就跟寫一般 Python 專案沒
兩樣,也能和其他 Python 套件無痛整合。PyTorch 的優勢在於其概念相當直觀且語法簡
潔優雅,因此視為新手入門的一個好選項;再來其輕量架構讓模型得以快速訓練且有效運
用資源。23 - 於 PyTorch 框架中,資料類型定義為張量(Tensor),張量可以是純量、向量或是更高維度的矩陣,而 torch 函式庫負責張量在 CPU/GPU 的運算。
- Numpy 的 GPU 版
- scikit-learn
5.5. python 執行環境建立與維護
建立24
conda create -n envName
啟用
conda activate envName
退出
conda deactiveate
刪除
conda env remove -n envName
列出目前系統中所有的虛擬環境
conda env list
5.6. 將執行環境匯入 jupyter
- 於終端機(for windows: Anaconda prompt)下建立、啟用所需虛擬環境
- 將環境滙至 jupyter kernel
python -m ipykernel install --user --name 虛擬環境名稱 --display-name "在jupyter中的名稱"
- 啟動 jupyter
6. NumPy
6.1. 關於 NumPy
- Numpy 是 Python 的一個重要模組,主要用於資料處理上。Numpy 底層以 C 和 Fortran 語言實作,所以能快速操作多重維度的陣列。25
- 當 Python 處理龐大資料時,其原生 list 效能表現並不理想(但可以動態存異質資料),而 Numpy 具備平行處理的能力,可以將操作動作一次套用在大型陣列上。
- Python 多數重量級的資料科學相關套件(例如:Pandas、SciPy、Scikit-learn 等)都幾乎是奠基在 Numpy 的基礎上。因此學會 Numpy 對於往後學習其他資料科學相關套件打好堅實的基礎。
- 延伸閱讀: 為什麼要用 Numpy?
6.3. 匯入模組
- 使用模組裡的函式要加模組名稱
import numpy
- 匯入 numpy 模組並使用 np 作為簡寫,這是 Numpy 官方倡導的寫法
import numpy as np
- Numpy 中的多維資料型別稱為 ndarray
6.4. Create ndarray
- 一維陣列
- 可以將python的list 或 tuple 轉成NumPy Array
import numpy as np np1 = np.array( [1, 2, 3, 4] ) print(np1)
[1 2 3 4]
- 使用 np.arange( ) 方法
import numpy as np np2 = np.arange(5) print("=====np2=====") print(np2) np3 = np.arange(1, 4, 0.5) print("=====np3=====") print(np3)
=====np2===== [0 1 2 3 4] =====np3===== [1. 1.5 2. 2.5 3. 3.5]
- np.arange() v.s. range()
差異:
- range()為 python 內建函數
- range() return 的是 range object,而 np.nrange() return 的是 numpy.ndarray()
- range()不支援 step 為小數,np.arange()支援 step 為小數
- range()為 python 內建函數
- 可以將python的list 或 tuple 轉成NumPy Array
- 二維陣列
import numpy as np np4 = np.array( [[1, 2, 4], [3, 4, 5]] ) print("shape:", np4.shape) print("np4:\n", np4) print("取出第0列:",np4[0]) print("取出第1行:",np4[:,1]) np5 = np.array([np.arange(3), np.arange(3)]) print('np5:\n', np5) np6 = np.arange(8).reshape(2, 4) print('np6:\n', np6)
shape: (2, 3) np4: [[1 2 4] [3 4 5]] 取出第0列: [1 2 4] 取出第1行: [2 4] np5: [[0 1 2] [0 1 2]] np6: [[0 1 2 3] [4 5 6 7]]
- 多維陣列
import numpy as np np7 = np.arange(24).reshape(2, 3, 4) print('np7:\n',np7) np8 = np.arange(13, 60, 2).reshape(2, 3, 4) print("np8:\n", np8)
np7: [[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [[12 13 14 15] [16 17 18 19] [20 21 22 23]]] np8: [[[13 15 17 19] [21 23 25 27] [29 31 33 35]] [[37 39 41 43] [45 47 49 51] [53 55 57 59]]] - 隨機矩陣
import numpy as np np8 = np.random.random((3, 2)) #矩陣大小以tuple表示 print('np8:\n', np8) # 四個人擲骰子,每人擲兩次 np9 = np.random.randint(1, 7, size=[4, 2]) #矩陣大小以list表示 print('np9:\n', np9)
np8: [[0.7263954 0.71063088] [0.07725825 0.11562424] [0.57923875 0.85345365]] np9: [[5 6] [2 2] [5 5] [5 2]]
- 0/1 矩陣
- np.zeros: np.zeros( (陣列各維度大小用逗號區分) ):建立全為 0 的陣列,可以小括號定義陣列的各個維度的大小
import numpy as np zeros = np.zeros( (3, 5) ) print("zeros=>\n{0}".format(zeros))
zeros=> [[0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.]]
- np.ones: np.ones( (陣列各維度大小用逗號區分) ):用法與 np.zeros 一樣
import numpy as np ones = np.ones( (4, 3) ) print("oness=>\n{0}".format(ones))
oness=> [[1. 1. 1.] [1. 1. 1.] [1. 1. 1.] [1. 1. 1.]]
- np.zeros: np.zeros( (陣列各維度大小用逗號區分) ):建立全為 0 的陣列,可以小括號定義陣列的各個維度的大小
- TODO 型態轉換
astype
- TODO 補入教材
6.5. Numpy計算時間比較
(setq org-babel-python-command "ipython --no-banner --classic --no-confirm-exit")
自行比較以下兩個版本的執行時間
ar = np.arange(1000) %timeit ar**3
1: %timeit [i**3 for i in range(1000)]
import numpy as np ar = np.arange(1000) %timeit ar**3
6.6. [課堂練習]
模擬一個37人/7科的全班月考成績(隨機生成)
1 : 33 66 29 2 : 86 1 11 3 : 84 66 81 4 : 76 38 16 5 : 13 27 77 6 : 88 14 47 7 : 45 70 35 8 : 94 79 98
6.7. 矩陣運算
- 矩陣變形:reshape()
- reshape()
- transpose()
1: import numpy as np 2: n1 = np.random.random((12)) #矩陣大小以tuple表示 3: 4: n2 = n1.reshape(3, 4) 5: print(n1) 6: print(n2) 7: n3 = n2.transpose() 8: print(n2) 9: print(n3) 10: n4 = n3.flatten() 11: print(n4)
[0.7338917 0.94396356 0.58422402 0.02469613 0.24878891 0.32737463 0.69404152 0.48386995 0.45738145 0.56101421 0.54547518 0.43416765] [[0.7338917 0.94396356 0.58422402 0.02469613] [0.24878891 0.32737463 0.69404152 0.48386995] [0.45738145 0.56101421 0.54547518 0.43416765]] [[0.7338917 0.94396356 0.58422402 0.02469613] [0.24878891 0.32737463 0.69404152 0.48386995] [0.45738145 0.56101421 0.54547518 0.43416765]] [[0.7338917 0.24878891 0.45738145] [0.94396356 0.32737463 0.56101421] [0.58422402 0.69404152 0.54547518] [0.02469613 0.48386995 0.43416765]] [0.7338917 0.24878891 0.45738145 0.94396356 0.32737463 0.56101421 0.58422402 0.69404152 0.54547518 0.02469613 0.48386995 0.43416765]
- reshape()
- 索引(Indexing)、切片(Slicing)
索引(Indexing)的用途不外乎就是為了要從陣列和矩陣中取值,但除此之外有很多種功能!可以取出連續區間,還可以間隔取值!26
- 選取連續區間 [a:b]
1: import numpy as np 2: a = np.arange(10) ** 2 3: print("a=> {0}".format(a)) 4: print("a[2:5]=> {0}".format(a[2:5]))
a=> [ 0 1 4 9 16 25 36 49 64 81] a[2:5]=> [ 4 9 16]
- 間隔選取[::c]
以1維陣列來說明x[a:b:c]
- a:選取資料的起始索引
- b:選取資料的結束索引+1
- c:選取資料間隔,以索引值可以被此值整除的元素,不指定表示1
1: import numpy as np 2: a = np.arange(10) ** 2 3: print("a==> {0}".format(a)) 4: a[2:9:3] = 999 5: print("a==> {0}".format(a))
a==> [ 0 1 4 9 16 25 36 49 64 81] a==> [ 0 1 999 9 16 999 36 49 999 81]
- a:選取資料的起始索引
- 選取連續區間 [a:b]
- 取出特定行列、特定範圍
- 取出第x列: Ary[x]
- 取出第x行: Ary[:,x]
- 取出第x_{1}~x_{2}列、第y_{1}~y_{2}行的範圍: Ary[x_{1}:x_{2} + 1, y_{1}:y_{2} + 1]
1: import numpy as np 2: a = np.arange(12).reshape(3, 4) 3: print(a) 4: print(a[1]) 5: print(a[:,1]) 6: print(a[1:3, 1:3])
[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [4 5 6 7] [1 5 9] [[ 5 6] [ 9 10]]
- 取出第x列: Ary[x]
- 刪除行/列
np.delete(temp,0,axis=1)#temp為操作物件,0表示要刪除的物件索引,axis表示行還是列,axis=0表示刪除行,axis=1表示刪除列。
1: import numpy as np 2: 3: a = np.arange(12).reshape(3, 4) 4: print(a) 5: a_delCol1 = np.delete(a, 1, 0) 6: print('刪除第1行') 7: print(a_delCol1) 8: a_delRow1 = np.delete(a, 1, 1) 9: print('刪除第1列') 10: print(a_delRow1) 11: print('現在的a') 12: print(a)
[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] 刪除第1行 [[ 0 1 2 3] [ 8 9 10 11]] 刪除第1列 [[ 0 2 3] [ 4 6 7] [ 8 10 11]] 現在的a [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]]
- 迭代(輸出)
如果是要對整個矩陣的值做運算,無需使用迴圈
- 一維陣列
1: import numpy as np 2: a = np.arange(4) ** 2 3: print("a: ",a) 4: for i in a: 5: print("a**(1/2)=> {0}".format(np.round(i**(1/2), 0)))
('a: ', array([0, 1, 4, 9])) a**(1/2)=> 1 a**(1/2)=> 1 a**(1/2)=> 1 a**(1/2)=> 1 - 多維陣列: 多維陣列在for loop中取值時,會以第一維度為優先!
1: import numpy as np 2: a = np.arange(1, 41).reshape(5, 8) 3: for row in a: 4: for i in row: 5: print("{0:3d}".format(i), end='') 6: print() 7:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
- 一維陣列
- 基礎運算
- 維度相同的矩陣相加、減
import numpy as np a = np.array( [6, 7, 8, 9] ) b = np.arange( 4 ) c = a - b print("a=>{0}".format(a)) print("b=>{0}".format(b)) print("c=>{0}".format(c))
a=>[6 7 8 9] b=>[0 1 2 3] c=>[6 6 6 6]
- 矩陣與常數運算
import numpy as np import math a = np.random.randint(100, size=(2, 4)) #矩陣大小以tuple表示 b = a + 10 c = a**2 print("a=>{0}".format(a)) print("b=>{0}".format(b)) print("c=>{0}".format(c))
a=>[[51 74 98 37] [13 13 74 86]] b=>[[ 61 84 108 47] [ 23 23 84 96]] c=>[[2601 5476 9604 1369] [ 169 169 5476 7396]]
- 維度相同的矩陣相加、減
- 矩陣轉置
\[a=\begin{bmatrix}1&0\\2&3\end{bmatrix}, a^T=\begin{bmatrix}1&2\\0&3\end{bmatrix}\]
import numpy as np a = np.array([[1, 0], [2, 3]]) print(a) print('--Matrix transpose--') print(a.transpose())
[[1 0] [2 3]] --Matrix transpose-- [[1 2] [0 3]]
- 矩陣相乘
- 矩陣乘法
\[a=\begin{bmatrix}a_{11}&a_{12}\\a_{21}&a_{22}\end{bmatrix}, b=\begin{bmatrix}b_{11}&b_{12}&b_{13}\\b_{21}&b_{22}&b_{23}\end{bmatrix}\]
\[a \cdot b=\begin{bmatrix}a_{11}*b_{11}+a_{12}*b_{21}&a_{11}*b_{12}+a_{12}*b_{22}&a_{11}*b_{13}+a_{12}*b_{23}\\a_{21}*b_{11}+a_{22}*b_{21}&a_{21}*b_{12}+a_{22}*b_{22}&a_{21}*b_{13}+a_{22}*b_{23}\end{bmatrix}\]
import numpy as np A = np.array([[1, 2, 3], [4, 3, 2]]) B = np.array([[1, 2], [2, 0], [3, -1]]) print("{0}".format(A.dot(B)))
[[14 -1] [16 6]]
- 相對位置乘法
\[a=\begin{bmatrix}a_{11}&a_{12}\\a_{21}&a_{22}\end{bmatrix}, b=\begin{bmatrix}b_{11}&b_{12}\\b_{21}&b_{22}\end{bmatrix}\]
\[a \cdot b=\begin{bmatrix}a_{11}*b_{11}&a_{12}*b_{12}\\a_{21}*b_{21}&a_{22}*b_{22}\end{bmatrix}\]
import numpy as np A = np.array([[1, 2], [4, 5]]) B = np.array([[7, 8], [9, 10]]) print("A:\n{0}".format(A)) print("B:\n{0}".format(B)) print("A*B:\n{0}".format(A*B))
A: [[1 2] [4 5]] B: [[ 7 8] [ 9 10]] A*B: [[ 7 16] [36 50]]
- 取代矩陣中元素
這裡也可以看出NumPy對於選取矩陣中元素的極好彈性,可直接以條件來當成選取方式
import numpy as np C = np.array([5, -1, 3, 9, 0]) print(C<=0) # 將矩陣中小於等於0的元素取代為0;其他轉為1 C[C<=0] = 0 C[C>0] = 1 print(C)
[False True False False True] [1 0 1 1 0]
- [課堂練習]
- 模擬一個37人/7科的全班月考成績(隨機生成, 0~100)
- 將所有 55<=分數<60 的成績均改為60
- 模擬一個37人/7科的全班月考成績(隨機生成, 0~100)
- 矩陣乘法
- 反矩陣
AB=BA=I, 其中 I 為單位矩陣
import numpy as np A = np.array([[4, -7], [2, -3]]) print("A:\n", A) B = np.linalg.inv(A) print("B:\n", B) print("A dot B:\n", A.dot(B))
A: [[ 4 -7] [ 2 -3]] B: [[-1.5 3.5] [-1. 2. ]] A dot B: [[1. 0.] [0. 1.]]
- 合併矩陣
- vstack
import numpy as np a = np.ones((2, 2)) b = np.zeros(2) print(a) print(b) c = np.vstack((a, b)) print(c)
[[1. 1.] [1. 1.]] [0. 0.] [[1. 1.] [1. 1.] [0. 0.]]
- hstack
import numpy as np a = np.ones((2, 2)) b = [[3], [4]] print(a) print(b) c = np.hstack((a, b)) print(c)
[[1. 1.] [1. 1.]] [[3], [4]] [[1. 1. 3.] [1. 1. 4.]]
- vstack
6.8. 矩陣函數
- 官網
- numpy.sum()
- 語法
numpy.sum(a, axis=None, dtype=None, out=None, keepdims=<no value>, initial=<no value>, where=<no value>)
其他參數用法詳見官方網站
- 範例
import numpy as np np_array_2x3 = np.array([[0,2,4],[1,3,5]]) print('=====Array=====') print(np_array_2x3) print('=====列=====') print(np.sum(np_array_2x3, axis = 0)) print('=====行=====') print(np.sum(np_array_2x3, axis = 1)) print('=====陣列總和1=====') print(np.sum(np_array_2x3)) print('=====陣列總和2=====') print(np_array_2x3.sum())
=====Array===== [[0 2 4] [1 3 5]] =====列===== [1 5 9] =====行===== [6 9] =====陣列===== 15 =====陣列===== 15
- 語法
- 其他常用函數
- min()
- max()
- argmin
- mean()
- std()
- var()
- sqrt()
- size()
- dtype()
- itemsize()
import numpy as np np1 = np.random.randint(0, 10, size=[3, 2]) print("np\n", np1) print("np1.sum", np1.sum()) print("sum:", sum(np1)) print("sum:", sum(np1,3)) print("min:", np1.min()) print("max:", np1.max()) print("mean:", np.mean(np1))
np [[0 0] [0 3] [0 3]] np1.sum 6 sum: [0 6] sum: [3 9] min: 0 max: 3 mean: 1.0
- min()
- sum() v.s. np.sum()

Figure 3: sum() v.s. np.sum()
- numpy.max()
- 語法
numpy.max(a, axis=None, out=None, keepdims=False)
- 求序列的最值
- 最少接收一個引數
- axis:預設為列向(也即 axis=0),axis = 1 時為行方向的最值;
- 求序列的最值
- 範例
import numpy as np np_array_2x3 = np.array([[9,2,8],[4,7,5]]) print('=====Array=====') print(np_array_2x3) print('=====列=====') print(np.max(np_array_2x3, axis = 0)) print('=====行=====') print(np.max(np_array_2x3, axis = 1)) print('=====陣列=====') print(np.max(np_array_2x3))
=====Array===== [[9 2 8] [4 7 5]] =====列===== [9 7 8] =====行===== [9 7] =====陣列===== 9
- 語法
- numpy.maxium()
- 語法
numpy.maximum:(X, Y, out=None)- X 與 Y 逐位比較取其大者;
- 最少接收兩個引數
- X 與 Y 逐位比較取其大者;
- 範例
import numpy as np npA1 = np.array([[9,-9,8],[4,7,5]]) npA2 = np.array([[0,1,8],[10,-7,5]]) print("=====npA1=====") print(npA1) print("=====npA2=====") print(npA2) print("=====maximum=====") print(np.maximum(npA1, npA2))
=====npA1===== [[ 9 -9 8] [ 4 7 5]] =====npA2===== [[ 0 1 8] [10 -7 5]] =====maximum===== [[ 9 1 8] [10 7 5]]
- 語法
- numpy.argmax()
- 語法
numpy.argmax(a, axis=None, out=None)[source]¶
- a: 可以轉換為陣列的陣列或物件,我們需要在其中找到最高值的索引。
- axis: 沿著行(axis=0)或列(axis=1)查詢最大值的索引。預設情況下,通過對陣列進行展平可以找到最大值的索引。
- out: np.argmax 方法結果的佔位符。它必須有適當的大小以容納結果。Returns the indices of the maximum values along an axis.
- a: 可以轉換為陣列的陣列或物件,我們需要在其中找到最高值的索引。
- 範例
- 一維陣列
import numpy as np a=np.array([2,6,1,6]) print("Array:") print(a) req_index=np.argmax(a) print("\nIndex with the largest value:") print(req_index) print("\nThe largest value in the array:") print(a[req_index])
Array: [2 6 1 6] Index with the largest value: 1 The largest value in the array: 6
- 二維陣列
import numpy as np a = np.array([[2,1,6], [7,14,5]]) print("Array:") print(a) req_index=np.argmax(a, axis=0) print("\nIndex with the largest value(axis=0):") print(req_index) req_index=np.argmax(a, axis=1) print("\nIndex with the largest value(axis=1):") print(req_index) req_index=np.argmax(a) print("\nIndex with the largest value:") print(req_index)
Array: [[ 2 1 6] [ 7 14 5]] Index with the largest value(axis=0): [1 1 0] Index with the largest value(axis=1): [2 1] Index with the largest value: 4

Figure 4: Sequence of Arg in array
- 一維陣列
- 語法
- about axis

Figure 5: Axis in ndarray
- tile()
numpy.tile()是個什麼函數呢,說白了,就是把數組沿各個方向複製
import numpy as np print('=====一維tile=====') b = np.array([[1, 2], [3, 4]]) b1 = np.tile(b, 2) print(b1) print('=====二維tile=====') b2 = np.tile(b, (2, 3)) print(b2)
=====一維tile===== [[1 2 1 2] [3 4 3 4]] =====二維tile===== [[1 2 1 2 1 2] [3 4 3 4 3 4] [1 2 1 2 1 2] [3 4 3 4 3 4]]
- numpy.allclose
- Returns True if two arrays are element-wise equal within a tolerance.
- The tolerance values are positive, typically very small numbers. The relative difference (rtol * abs(b)) and the absolute difference atol are added together to compare against the absolute difference between a and b.
- NaNs are treated as equal if they are in the same place and if equal_nan=True. Infs are treated as equal if they are in the same place and of the same sign in both arrays.
- 語法
numpy.allclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)
- Parameters
- a, b: array_like. Input arrays to compare.
- rtol: float. The relative tolerance parameter (see Notes).
- atol: float. The absolute tolerance parameter (see Notes).
- equal_nan: bool. Whether to compare NaN’s as equal. If True, NaN’s in a will be considered equal to NaN’s in b in the output array.
- a, b: array_like. Input arrays to compare.
- Returns
- allclose: bool. Returns True if the two arrays are equal within the given tolerance; False otherwise.
- allclose: bool. Returns True if the two arrays are equal within the given tolerance; False otherwise.
- See also:
- DEMO
import numpy as np import math print(math.pi) x = np.array([[3.14159265, -0.1], [-0.1, 0.1]]) y = np.array([[math.pi, -0.1], [-0.1, 0.1]]) z1 = np.array([[[3.14159265, -0.1], [-0.1, 0.1]], [[3.14159265, -0.1], [-0.1, 0.1]]]) z2 = np.array([[[math.pi, -0.1], [-0.1, 0.1]], [[math.pi, -0.1], [-0.1, 0.1]]]) print(np.allclose(x,y)) # Returns true, as expected print(np.allclose(x,z1)) # Also returns true, even though matrices are different shapes. Unwanted. a = np.array([[2,2,2], [3,3,3]]) b = np.array([[2,3,2], [3,3,2]]) print("=====allclose(atol=0.5)=====") print(np.allclose(a, b, atol=0.5)) print("=====allclose(atol=1.0)=====") print(np.allclose(a, b, atol=1.0)) print("=====equal()=====") print(np.equal(a,b)) a = np.array([[2,2,2], [3,3,3.1]]) print(np.allclose(a, b, atol=1.0)) print('=====isclose()=====') print(np.isclose(a, b, atol=1.0))
3.141592653589793 True True =====allclose(atol=0.5)===== False =====allclose(atol=1.0)===== True =====equal()===== [[ True False True] [ True True False]] False =====isclose()===== [[ True True True] [ True True False]]
- Returns True if two arrays are element-wise equal within a tolerance.
6.9. [作業1]
- 隨機產生一組 30*5 個 0~100 的陣列,模擬成一個班級的某次考試成績(30 人*5 科)。
- 輸出此次 5 科考科的全班總分、平均、最高分、最低分、標準差。
- 將全班分數以「開根號乘以 10」的方式進行調整。
- 如果調整完分數還是不及格,把分數改為39分。
- 輸出不及格人數。
- 輸出全班分數(至小數點第二位,關於小數點的控制請自行Google關鍵字“numpy.set_printoptions”)。
- 結果範例
各科總分: [1341 1522 1548 1411 1627] 各科平均: [44.7 50.73 51.6 47.03 54.23] 各科最高分: [94 95 94 98 96] 各科最低分: [6 0 4 0 0] 各科標準差: [23.78 27.85 28.07 29.48 27.59] 全班不及格科目數: 51 調整後分數: 1: 39.00 86.60 60.00 39.00 62.45 2: 84.85 90.00 39.00 81.24 39.00 3: 39.00 86.60 80.62 80.00 39.00 4: 83.07 39.00 39.00 74.83 77.46 5: 72.80 39.00 91.10 67.82 39.00 6: 39.00 70.00 65.57 95.39 97.98 7: 39.00 90.00 96.95 82.46 88.88 8: 60.00 92.74 85.44 87.75 83.67 9: 64.81 39.00 92.20 68.56 39.00 10: 69.28 68.56 88.88 39.00 85.44 11: 39.00 39.00 94.87 39.00 76.16 12: 39.00 39.00 39.00 98.99 78.10 13: 75.50 88.32 39.00 74.16 66.33 14: 39.00 39.00 39.00 95.92 79.37 15: 70.00 39.00 39.00 83.07 73.48 16: 39.00 39.00 68.56 39.00 95.92 17: 65.57 39.00 39.00 39.00 74.83 18: 95.39 63.25 39.00 39.00 88.32 19: 39.00 95.39 60.83 39.00 74.83 20: 75.50 39.00 67.08 39.00 96.95 21: 39.00 39.00 83.67 39.00 77.46 22: 71.41 60.83 69.28 88.88 77.46 23: 74.16 97.47 39.00 73.48 93.27 24: 83.67 84.85 80.00 74.83 39.00 25: 96.95 95.92 87.18 68.56 39.00 26: 39.00 83.67 74.83 39.00 39.00 27: 60.83 77.46 83.67 83.07 91.65 28: 69.28 64.81 89.44 39.00 39.00 29: 88.88 83.07 92.74 82.46 95.92 30: 86.60 74.16 88.32 86.60 39.00
6.10. 解聯立方程式
Numpy的linalg function可以用來解線性方程組,若目標方程式為\[Ax=b\],其中\[x\]為所求之解,求解語法為:
1: import numpy as np 2: x = np.linalg.solve(A, b)
- 範例1
以如下方程式為例
\[\begin{cases}2x+y=5\\x+y=3\end{cases}\]
可將之視為求解\[Ax=b\],其中
\[A=\begin{pmatrix}2&1\\1&1 \end{pmatrix}\], \[b=\begin{pmatrix} 5\\3 \end{pmatrix} \]
- 範例2
求解下列方程式
\[\begin{cases}3x_0+2x_1+x_2=11\\2x_0+3x_1+x_2=13\\x_0+x_1+4x_2=12 \end{cases}\], 以\[Ax=b\]表示,則
\[A=\begin{pmatrix}3&2&1\\2&3&1\\1&1&4\end{pmatrix}, x=\begin{pmatrix}x_0\\x_1\\x_2\end{pmatrix}, b=\begin{pmatrix}11\\13\\12\end{pmatrix}\]
6.11. 讀取外部檔案
- Google Colab
- 先將資料檔儲存到Google Drive中
將要讀取的資料檔(txt, csv)上傳到Google雲端硬碟
- 要求授權
在Colab新增一個cell,執行下列程式
from google.colab import drive drive.mount('/content/drive')
選擇“Connected to Google Drive”
- 測試
- 要求授權
- 先點這裡下載範例資料
- 上傳資料檔
執行以下程式
!ls drive/MyDrive/ !cat drive/MyDrive/scores.csv
如果能看到資料的內容,就表示成功了,接下來就能用NumPy來讀取、分析這個資料檔
- 要求授權
- 先將資料檔儲存到Google Drive中
- 用NumPy來讀資料檔
- load
在Numpy內會使用.loadtxt或特定的np.genfromtxt來讀取文字檔
import numpy as np myAry = np.loadtxt('drive/MyDrive/scores.csv', delimiter=',') print(myAry)
[[109. 87. 100. 86. 50. ] [ 86. 66. 68.97 33.2 55. ] [ 82. 51. 72.87 57. 70. ] [ 90. 81. 100. 100. 100. ] [ 80. 45. 39.66 0. 0. ] [ 83. 36. 74.14 20. 10. ] [ 84. 51. 67.24 25. 0. ] [ 75. 72. 89.66 43. 40. ] [ 76. 63. 96.55 40. 0. ] [ 80. 75. 100. 32.8 50. ] [ 85. 96. 89.48 83.7 30. ]]
- save
將處理完的陣列回存成csv檔:
import numpy as np myAry = np.loadtxt('drive/MyDrive/scores.csv', delimiter=',') print(myAry) np.savetxt('drive/MyDrive/newScores.csv', myAry)
- load
- 不同格式
- Bineary Format
- save(), load()
import numpy as np a = np.arange(0, 12).reshape(3,4) print(a) np.save('a', a) # 讀入 b = np.load('a.npy') print(b)
[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]]
- savez()
我們可以儲存多個陣列在一個zip的檔案中,使用np.savez就可以了!
import numpy as np aAry = [1,2,3,4,5,6] bAry = [7,8,9,10,11,12] #save np.savez('ab.npz', a = aAry, b = bAry) #load myZip = np.load('ab.npz') print(myZip['a']) print(myZip['b'])
[1 2 3 4 5 6] [ 7 8 9 10 11 12]
- save(), load()
- 讀取混合格式的文字資料
如果資料中某些欄位的資料格式是字串 (string),處理起來相當麻煩,改用 numpy.genfromtxt 會比較簡單。29
Importing data with genfromtxt
目標文字檔(csv)如下:
1: cat cs-scores2.csv
學號,平時成績,打字成績,作業成績,期中考,期末考 201811101,109.00,87,100.00,86.00,50.00 201811102,86.00,66,68.97,33.20,55.00 201811103,82.00,51,72.87,57.00,70.00 201811104,90.00,81,100.00,100.00,100.00 201811105,80.00,45,39.66,0.00,0.00 201811106,83.00,36,74.14,20.00,10.00 201811107,84.00,51,67.24,25.00,0.00 201811108,75.00,72,89.66,43.00,40.00 201811109,76.00,63,96.55,40.00,0.00 201811110,80.00,75,100.00,32.80,50.00 201811111,85.00,96,89.48,83.70,30.00
Pyton 3在讀取文字時,dtype應設為U(Unicode),否則會在讀到的字首多出b30
import numpy as np # Python3 is working with Unicode. # I had the same issue when using loadtxt with dtype='S'. But using dtype='U as Unicode string in both numpy.loadtxt or numpy.genfromtxt, it will give output without b data = np.genfromtxt("cs-scores2.csv", delimiter=',', dtype=[('id', 'U10'), ('cls', float), ('typing', float), ('hw', float), ('mid', float), ('finl', float)], skip_header=1, encoding='UTF-8') print(data)
[('201811101', 109., 87., 100. , 86. , 50.) ('201811102', 86., 66., 68.97, 33.2, 55.) ('201811103', 82., 51., 72.87, 57. , 70.) ('201811104', 90., 81., 100. , 100. , 100.) ('201811105', 80., 45., 39.66, 0. , 0.) ('201811106', 83., 36., 74.14, 20. , 10.) ('201811107', 84., 51., 67.24, 25. , 0.) ('201811108', 75., 72., 89.66, 43. , 40.) ('201811109', 76., 63., 96.55, 40. , 0.) ('201811110', 80., 75., 100. , 32.8, 50.) ('201811111', 85., 96., 89.48, 83.7, 30.)] - 讀取一個欄位資料
在 numpy.genfromtxt() 中, 使用 usecols 參數指定取出的欄位編號, 即可取出特定欄位
import numpy as np classScore = np.genfromtxt("cs-scores2.csv", delimiter=',', skip_header=1, dtype=float, usecols=(2,), unpack=True, encoding='UTF-8') print(classScore)
[87. 66. 51. 81. 45. 36. 51. 72. 63. 75. 96.]
6.12. 基礎練習
資料來源: [轉]numpy 100道練習題
- 創建一個長度為10的零向量,並把第五個值賦值為1
- 創建一個5x5的矩陣,值域為1,3,5,7…,49
- 創建一個3x3x3的隨機數組
- example
[[[0.65641795 0.9853537 0.64412663] [0.52595413 0.17452888 0.44484008] [0.69218998 0.37001109 0.94711544]] [[0.80803134 0.93661621 0.61489936] [0.19635856 0.98132637 0.36713757] [0.89105201 0.31710182 0.12375642]] [[0.42944896 0.24734458 0.97763098] [0.96900319 0.64789449 0.42795362] [0.72306476 0.07604109 0.13012237]]]
- example
- 創建一個8x8的國際象棋棋盤矩陣(黑塊為0,白塊為1)
- 對5x5的隨機矩陣進行歸一化(Normalization)
(提示: (x - min) / (max - min))
- example
[[93.91707 7.2497428 80.03357931 67.36581629 11.58313519] [12.57003038 38.52508111 83.86899353 27.63880275 25.8237963 ] [18.68409281 64.40845991 41.08228881 62.87822465 52.23296308] [90.53354354 78.60844019 46.98888235 29.757448 38.46802081] [71.53666934 63.90804362 64.40403658 29.34575551 82.74560132]] [[1. 0. 0.8398071 0.69364172 0.0500003 ] [0.06138747 0.36086654 0.88406154 0.23525659 0.21431437] [0.1319338 0.65951863 0.39037256 0.64186221 0.5190332 ] [0.96095961 0.82336331 0.45852504 0.25970231 0.36020815] [0.74176658 0.65374464 0.65946759 0.25495205 0.87109942]]
- example
- 如何找出兩個數組公共的元素?
(提示: np.intersect1d)
- 如何用一個生成10個整數的函數來構建數組
(提示: np.fromiter)
- 對一個小數組進行求和有沒有辦法比np.sum更快?
(提示: np.add.reduce)
- 如何判斷兩隨機數組相等
(提示: np.allclose, np.array_equal)
- 創建一個大小為10的隨機向量並且將該向量中最大的值替換為0
(提示: argmax)
- 思考形狀為(100, 2)的隨機向量,求出點與點之間的距離
(提示: np.atleast_2d, T, np.sqrt)
- example
[[0.24 0.44] [0.87 0.84] [0.87 0.63] [0.56 0.11] [0.67 0.47]] [[0. 0.74 0.66 0.46 0.43] [0.74 0. 0.21 0.79 0.42] [0.66 0.21 0. 0.61 0.26] [0.46 0.79 0.61 0. 0.37] [0.43 0.42 0.26 0.37 0. ]] [[0. 0.45 0.29 0.77 0.78] [0.45 0. 0.68 1.04 0.76] [0.29 0.68 0. 0.93 1.05] [0.77 1.04 0.93 0. 0.58] [0.78 0.76 1.05 0.58 0. ]]
- example
- 如何在二維數組的隨機位置放置p個元素?(提示: np.put, np.random.choice)
- 如何對數組通過第n列進行排序? (提示: argsort)
- 從數組中找出與給定值最接近的值(提示: np.abs, argmin, flat)
- 創建一個具有name屬性的數組類(提示: class method)
- 考慮一個維度(5,5,3)的數組,如何將其與一個(5,5)的數組相乘?(提示: array[:, :, None])
- 如何對一個數組中任意兩行做交換?(提示: array[[]] = array[[]])
- 思考描述10個三角形(共享頂點)的一組10個三元組,找到組成所有三角形的唯一線段集(提示: repeat, np.roll, np.sort, view, np.unique)
- 如何找出數組中出現頻率最高的值?(提示: np.bincount, argmax)
- 從一個5x5的矩陣中提取出連續的3x3區塊**(提示: stride_tricks.as_strided)
- example
[[2 5 1 4 6] [2 6 7 2 4] [3 7 7 4 6] [8 1 4 0 3] [1 1 7 7 4]] [[[[2 5 1] [2 6 7] [3 7 7]] [[5 1 4] [6 7 2] [7 7 4]] [[1 4 6] [7 2 4] [7 4 6]]] [[[2 6 7] [3 7 7] [8 1 4]] [[6 7 2] [7 7 4] [1 4 0]] [[7 2 4] [7 4 6] [4 0 3]]] [[[3 7 7] [8 1 4] [1 1 7]] [[7 7 4] [1 4 0] [1 7 7]] [[7 4 6] [4 0 3] [7 7 4]]]]
- example
- 對於一個16x16的數組,如何得到一個區域的和(區域大小為4x4)? (提示: np.add.reduceat)
- 如何找到一個數組的第n個最大值?(提示: np.argsort | np.argpartition)
6.13. 資料的正規化(Normalization)及標準化(Standardization)
當我們在比較分析兩組數據資料時,可能會遭遇因單位的不同(例如:身高與體重),或數字大小的代表性不同(例如:粉專1萬人與滿足感0.8),造成各自變化的程度不一,進而影響統計分析的結果;為解決此類的問題,我們可利用資料的正規化(Normalization)與標準化(Standardization),藉由將原始資料轉換成無量綱(Dimensionless)的純量後,來進行數據的比較及分析。31
- Normalization
原始資料的數據按比例縮放於 [0, 1] 區間中,且不改變其原本分佈。舉例來說,若我們現有兩組數據資料,分別表示 500 項商品的銷售量 Sample 1 及銷售額 Sample 2,如下圖所示,很明顯地,此兩組資料的單位不同,且數字上有著懸殊的差異,分別透過資料正規化後,兩組資料將同時轉換成純量縮放於 [0,1] 區間中,如下右圖所示;這樣的資料轉換,能排除資料單位的限制,提供我們一個相同的基準來進行後續比較分析。

Figure 6: Normalization
- 實作
最小值最大值正規化的用意,是將資料等比例縮放到 [0, 1] 區間中,可利用下列公式進行轉換:
\[X_{nom} = \frac{X-X_{min}}{X_{max}-X{min}} \in [0,1]\]
其中 Xmax 與 Xmin 分別為資料中的最小值與最大值。此種方法有一點需我們特別注意,即若原始資料有新的數據加入,有可能導致最小值 Xmin 及最大值 Xmax 的改變,則這時候我們需再重新定義公式中的 Xmin 及 Xmax。另外,若將轉換公式修改成下列:
\[X_{nom} = \frac{X-\mu}{X_{max}-X{min}} \in [-1,1]\]
其中\[\mu\]為資料的平均值,則資料將縮放到 [-1, 1] 區間中且平均值 = 0,我們稱這為平均值正規化(Mean Normalization)。
在實務操作最小值最大值正規化時,我們可使用 Scikit-learn 套件的MinMaxScaler 物件執行。
- 實作
- Standardization
會將所有特徵數據縮放成平均為0、平方差為1。資料的標準化(Standardization)可運用在機器學習演算法中,它能帶給模型下面兩個好處:
- 提升模型的收斂速度
在建構機器學習模型時,我們會利用梯度下降法(Gradient Descent)來計算成本函數(Cost Function)的最佳解;假設我們現有兩個特徵值 x1 in [0,1] 與 x2 in [0,10000],則在 x1-x2 平面上成本函數的等高線會呈窄長型,導致需較多的迭代步驟,另外也可能導致無法收斂的情況發生。因此,若將資料標準化,則能減少梯度下降法的收斂時間。 - 提高模型的精準度
將特徵值 x1 及 x2 餵入一些需計算樣本彼此的距離(例如:歐氏距離)分類器演算法中,則 x2 的影響很可能將遠大於 x1,若實際上 x1 的指標意義及重要性高於 x2,這將導致我們分析的結果失真。因此,資料的標準化是有必要的,可讓每個特徵值對結果做出相近程度的貢獻。
- 實作
\[Z = \frac{X-\mu}{\delta} \sim N(0, 1)\]
經 Z分數標準化後,資料將符合標準常態分佈(Standard Normal Distribution),轉換後的平均值=0、標準差=1,且用標準分數或稱 Z分數(Z-Score)來作為單位。Z分數標準化適用於分佈大致對稱的資料,因為在非常不對稱的分佈中,標準差的意義並不明確,此時若標準化資料,可能會對結果做出錯誤的解讀,另外,當我們未知資料的最大值與最小值,或存在超出觀察範圍的離群值時,可透過 Z分數標準化來降低離群值對整個模型的影響。
在實務操作時,我們可先分別計算資料的平均值及標準差,再代入上述公式完成標準化,另一種方法,我們可使用Scikit-learn 套件的preprocessing 模組來執行資料標準化。
- 提升模型的收斂速度
6.14. [作業2]
- 模擬一個37人/7科的全班月考成績(隨機生成)
- 將所有原始分數轉換為Z分數
7. Matplotlib
7.1. Matplotlib 簡介
- 官網: https://matplotlib.org/3.2.1/index.html#
- Matplotlib 是利用 Python 所實作的繪圖套件,其中包含兩個最重要的模組: pylab和pyplot。
- pylab 已經幾乎實作了在學術界最常用的套件 — Matlab 所支援的繪圖功能,或者可以說 pylab 其實就是 Matlab 的 Python 版本;
- pyplot 是把 pylab 再加上 Numpy,讓使用者在使用 pyplot 時,可以直接呼叫 Numpy 的函式做計算後再以圖型的方式呈現。32
- 為 Python 最多人使用的 2D 繪圖工具
- 優點:圖形美觀、類型多、相容於 Matlab
- 官方社群網站 https://matplotlib.org/
- 維基百科 https://zh.wikipedia.org/wiki/Matplotlib
7.2. Matplotlib 基本語法
- 基本語法
- import matplotlib.pyplot as plt
plt.plot()函式為 matplotlib.pyplot 模組畫線條方法,其語法如下
plt.plot( [x座標資料,] y座標資料 [, 參數1, 參數2, ...] )
- plt.plot()官網說明
- import matplotlib.pyplot as plt
- 範例:簡單的 sin 圖形
- np.sin( )函式為 Numpy 模組求正弦值
- plt.show( )函式用來顯示圖形
- 請到官網看看除了sin()還有哪些function可以玩,畫出幾個函式曲線
import matplotlib.pyplot as plt import numpy as np x = np.arange(-3, 3, 0.1) plt.clf() plt.plot(x, np.sin(x)) # 若要存圖,要先存檔再顯示 # for jupyter plt.savefig('images/SimpleSin.png', dpi=300) # plt.show() # for jupyter

Figure 7: 簡單的 sin 圖形
- np.sin( )函式為 Numpy 模組求正弦值
- plot 官方語法
plot()函式控制輸出主要有以下兩類參數:
- fmt 字串
- kwarg 參數
基本語法如下:
- plot([x], y, [fmt], *, data=None, **kwargs)
- plot([x], y, [fmt], [x2], y2, [fmt2], …, **kwargs)
- fmt 字串
7.3. fmt控制參數
- 可選引數 fmt 是定義基本格式 (如顏色、標記和 linestyle) 的簡便方法
- plot(): fmt 字串
| 字元 | 顏色 | 字元 | 標記 | 字元 | 線條 |
|---|---|---|---|---|---|
| ’b’ | 藍 | ’.’ | 點 | ’–’(兩個-) | 虛線 |
| ’g’ | 綠 | ’o’ | 圓圈 | ’-’ | 實線 |
| ’r’ | 紅 | ’v’ | 三角形(下) | ’-.’ | -.-.-.-. |
| ’c’ | 青 | ’^’ | 三角形(上) | ||
| ’m’ | 洋紅 | ’<’ | 三角形(左) | ||
| ’y’ | 黃 | ’>’ | 三角形(右) | ||
| ’k’ | 黑 | ’s’ | 正方形 | ||
| ’w’ | 白 | ’p’ | 五邊形 | ||
| ’*’ | * | ||||
| ’+’ | + | ||||
| ’x’ | x | ||||
| ’d’ | 鑽石 |
- Demo #1
import matplotlib.pyplot as plt import numpy as np x = np.arange(-3, 3, 0.1) plt.clf() plt.plot(x, np.sin(x), 'r+') # for web-based # plt.show() # saving figure plt.savefig('images/SimpleSin2.png', dpi=300)

Figure 8: 簡單的 sin 圖形
- Demo #2
import matplotlib.pyplot as plt import numpy as np x = np.arange(-3, 3, 0.1) plt.clf() plt.plot(x, np.cos(x), 'c-.') # for web-based # plt.show() # saving figure plt.savefig('images/SimpleCos.png', dpi=300)

Figure 9: 簡單的 cos圖形
- Demo: 多組資料
import matplotlib.pyplot as plt import numpy as np x1 = np.arange(-3, +3, 0.1) y1 = np.sin(x1) y2 = np.cos(x1) #plt.plot(x1, y1, 'r^--', x1, y2, 'go-') plt.plot(x1, y1, 'r^--', x1, y2, 'go-') plt.savefig('mline.png', dpi=300)

Figure 10: 多組圖形
7.4. kwgarg 參數
- kwarg 為 Line2D 屬性:color, linestyle, marker, label, linewidth, ….
- kwargs 用於指定諸如線條標籤 (用於自動圖例)、線寬、抗鋸齒、標記面顏色等屬性
- 若 fmt 和 kwarg 設定衝突時,以 kwarg 為主
- kwargs 用於指定諸如線條標籤 (用於自動圖例)、線寬、抗鋸齒、標記面顏色等屬性
- color
- 單字,如 g:color = ’lime’
- 字母,如 g:color = ’k’
- 色碼,如 g:color = ’#FF0000’
- RGB 值(0~g1 之間),如:color = (1, 0, 0)
字元 顏色 ’b’ 藍色 ’g’ 綠色 ’r’ 紅 ’c’ 青色 ’m’ 品紅 ’y’ 黃色 ’k’ 黑 ’w’ 白色 - 單字,如 g:color = ’lime’
- marker
字元 描述 ’.’ 點標記 ’,’ 畫素標記 ’o’ 圓圈標記 ’v’ triangle_down 標記 ’^’ triangle_up 標記 ’<’ triangle_left 標記 ’>’ triangle_right 標記 ’1’ tri_down 標記 ’2’ tri_up 標記 ’3’ tri_left 標記 ’4’ tri_right 標記 ’s’ 方形標記 ’p’ 五角大樓標記 ’*’ 星形標記 ’h’ hexagon1 標記 ’H’ hexagon2 標記 ’+’ 加號標記 ’x’ x 標記 ’D’ 鑽石標記 ’d’ thin_diamond 標記 ’|’ 圴標記 ’_’ 修身標記 - 其他參數
- x / y 座標範圍:plt.xlim(起始值, 終止值) / plt.ylim(起, 止)
- 圖表標題:plt.title(字串)
- x / y 座標標題:plt.xlabel(字串) / plt.ylabel(字串)
- 顯示 kwarg 參數裡的 label:plt.legend()
- x / y 座標範圍:plt.xlim(起始值, 終止值) / plt.ylim(起, 止)
- kwarg 示範
import matplotlib.pyplot as plt import numpy as np x = np.arange(-3, 3, 0.5) plt.clf() plt.plot(x, np.cos(x), color='c', linestyle='--', marker='p') plt.xlim(-4, 4) plt.xlabel('This is x label') plt.title("This is Title", fontsize=16) plt.savefig('images/kwarg.png', dpi=300)

Figure 11: kwarg參數控制
7.5. 在Spyder中使用中文
- 解決方案
# 解決中文問題 plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 步驟一(替換系統中的字型,這裡用的是Mac OSX系統) plt.rcParams['axes.unicode_minus'] = False # 步驟二(解決座標軸負數的負號顯示問題)
- 實際示例

Figure 12: 簡單的折線圖及圖例
7.6. 在colab中使用中文
- 在ipynb最上方先加入下列cell
# Colab 進行matplotlib繪圖時顯示繁體中文 # 下載台北思源黑體並命名taipei_sans_tc_beta.ttf,移至指定路徑 !wget -O TaipeiSansTCBeta-Regular.ttf https://drive.google.com/uc?id=1eGAsTN1HBpJAkeVM57_C7ccp7hbgSz3_&export=download import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib.font_manager import fontManager # 改style要在改font之前 # plt.style.use('seaborn') fontManager.addfont('TaipeiSansTCBeta-Regular.ttf') mpl.rc('font', family='Taipei Sans TC Beta')
7.7. Legend
import matplotlib.pyplot as plt import numpy as np x = np.arange(-3, 3, 0.1) plt.clf() plt.plot(x, np.sin(x)) plt.plot(x, np.cos(x)) #加入圖例 plt.legend(["sin", "cos"], loc="upper left") # 若要存圖,要先存檔再顯示 # for jupyter plt.savefig('images/sincoslegend.png', dpi=300)

Figure 13: Caption
7.8. Line Chart
- Demo #1
import matplotlib.pyplot as plt import numpy as np x1 = [1, 2, 3, 4, 5, 6] y1 = [1, 2, 3, 4, 5, 6] x2 = np.arange(0, 3, 0.3) x3 = np.arange(8).reshape(2, 4) print(x3) print(x3+3) plt.plot(y1, 'c-p', x2, x2**2, 'ms:', x3, x3+3, '-*') plt.savefig('line2.png', dpi=300)
[[0 1 2 3] [4 5 6 7]] [[ 3 4 5 6] [ 7 8 9 10]]

Figure 14: 簡單的折線圖形 2
- Demo #2
import matplotlib.pyplot as plt import numpy as np def angdeg(x): return 1.2*x+3 x = np.arange(0, 21, 1) plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 步驟一(替換系統中的字型,這裡用的是Mac OSX系統) plt.rcParams['axes.unicode_minus'] = False # 步驟二(解決座標軸負數的負號顯示問題) ax = plt.figure().gca() plt.plot(x, angdeg(x), color='r', linestyle='--', linewidth=3) plt.xlabel('本月闖禍次數', fontsize=16) plt.ylabel('媽媽生氣程度', fontsize=16) plt.xlim(0,21) ax.set_xticks(x) plt.savefig('images/angrymam-1.png', dpi=300)

Figure 15: Angry Mam
- Demo #3
import matplotlib.pyplot as plt import numpy as np def angdeg(x): return 500+(x**4)/100 x = np.arange(0, 21, 1) #plt.cla() plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 步驟一(替換系統中的字型,這裡用的是Mac OSX系統) plt.rcParams['axes.unicode_minus'] = False # 步驟二(解決座標軸負數的負號顯示問題) ax = plt.figure().gca() plt.plot(x, angdeg(x), color='r', linestyle='--', linewidth=3) plt.xlabel('本月闖禍次數', fontsize=16) plt.ylabel('媽媽生氣程度', fontsize=16) plt.xlim(0,21) plt.ylim(0,2201) ax.set_xticks(x) plt.savefig('images/angrymam-2.png', dpi=300)

Figure 16: Angry Mam
- Demo #4
import matplotlib.pyplot as plt import numpy as np import pandas as pd def angdeg(x): return x**4 def l2c(x): if x == True: return 'r' else: return 'g' np.random.seed(9527) x = np.random.randint(21, size=20) y = np.random.randint(21, size=20) z = x + y < 20 colors = list(map(lambda x: 'g' if x == True else 'r', z)) #colors = list(map(l2c, z)) labels = list(map(lambda x: '挨揍' if x == True else '平安', z)) plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 步驟一(替換系統中的字型,這裡用的是Mac OSX系統) plt.rcParams['axes.unicode_minus'] = False # 步驟二(解決座標軸負數的負號顯示問題) ax = plt.figure().gca() #plt.scatter(x, y, c = colors) rc = [i for i in range(len(colors)) if colors[i] == 'r'] gc = [i for i in range(len(colors)) if colors[i] == 'g'] plt.scatter(x[rc], y[rc], c = 'r') plt.scatter(x[gc], y[gc], c = 'g') plt.xlabel('因闖禍受傷程度', fontsize=16) plt.ylabel('因闖禍導致損失金額', fontsize=16) ax.set_xticks(range(21)) ax.set_yticks(range(21)) plt.legend(['挨揍','平安']) plt.plot(x, 20-x, linewidth=3) plt.savefig('images/angrymam-3.png', dpi=300)

Figure 17: Angry Mam
- Demo #5
import matplotlib.pyplot as plt import numpy as np import pandas as pd def angdeg(x): return x**4 def l2c(x): if x == True: return 'r' else: return 'g' def mycurv(x): return 5 + (2 ** (19-float(x)))/6000 np.random.seed(9527) x = np.random.randint(21, size=20) y = np.random.randint(21, size=20) y1 = list(map(mycurv, x)) print('y:',y) print('y1:',y1) z = y > y1 print('z:', z) colors = list(map(lambda x: 'g' if x == True else 'r', z)) colors = list(map(l2c, z)) #labels = list(map(lambda x: '挨揍' if x == True else '平安', z)) plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 步驟一(替換系統中的字型,這裡用的是Mac OSX系統) plt.rcParams['axes.unicode_minus'] = False # 步驟二(解決座標軸負數的負號顯示問題) ax = plt.figure().gca() # ##plt.scatter(x, y, c = colors) rc = [i for i in range(len(colors)) if colors[i] == 'r'] gc = [i for i in range(len(colors)) if colors[i] == 'g'] plt.scatter(x[rc], y[rc], c = 'r') plt.scatter(x[gc], y[gc], c = 'g') plt.xlabel('因闖禍受傷程度', fontsize=16) plt.ylabel('因闖禍導致金錢損失', fontsize=16) ax.set_xticks(x) plt.legend(['挨揍','平安']) x1=np.arange(21,0,-1, dtype=float) print(x1) plt.plot(x1, 5 + (2 ** (19-x1))/6000, linewidth=3) plt.xlim(0,21) plt.ylim(0,21) ax.set_xticks(range(21)) ax.set_yticks(range(21)) plt.savefig('images/angrymam-4.png', dpi=300)
y: [15 5 1 1 16 12 6 3 14 15 6 17 2 12 0 11 6 7 9 13] y1: [48.690666666666665, 5.341333333333333, 5.001333333333333, 5.001333333333333, 5.021333333333334, 5.341333333333333, 5.001333333333333, 48.690666666666665, 5.000083333333333, 5.042666666666666, 5.1706666666666665, 26.845333333333333, 5.001333333333333, 5.010666666666666, 5.021333333333334, 92.38133333333333, 48.690666666666665, 7.730666666666666, 6.365333333333333, 26.845333333333333] z: [False False False False True True True False True True True False False True False False False False True False] [21. 20. 19. 18. 17. 16. 15. 14. 13. 12. 11. 10. 9. 8. 7. 6. 5. 4. 3. 2. 1.]

Figure 18: Angry Mam
- 練習1: 繪製折線圖
- 利用 list 繪製
- x1 = [1, 2, 3, 4, 5, 6]
- y1 = [1, 2, 3, 4, 5, 6]
- x1 = [1, 2, 3, 4, 5, 6]
- 執行 plt.plot(y1) 和 plt.plot(x1, y1) 有何差別??
- 利用 numpy 繪製
- x2 = np.arange(0, 3, 0.01)
- x2 = np.arange(0, 3, 0.01)
- 執行 plt.plot(x2, x2**2)看看畫出什麼圖形??
- 利用 list 繪製
- 課堂作業: 圖表美化
- 將7.8.6的圖表加上標記、線條標籤,換線條顏色、線條樣式
請將下列資料繪成折線圖(男女折線不同顏色、樣式,加標記)
初婚年齡 2006 2011 2014 2015 2016 男 30.7 31.8 32.1 32.2 32.4 女 27.8 29.4 29.9 30.0 30.0 - 圖表標題: Age of first marriage
- 座標軸標題: x ⇒ Year、y ⇒ Age
- 線條標籤: 男 ⇒ Male、女 ⇒ Female
結果示例
- 圖表標題: Age of first marriage

- 將7.8.6的圖表加上標記、線條標籤,換線條顏色、線條樣式
7.9. Bar chart
- 語法
plt.bar( x座標資料, y座標資料 [, 參數1, 參數2, ...] ) plt.barh( x座標資料, y座標資料 [, 參數1, 參數2, ...] )
- DEMO
import matplotlib.pyplot as plt import numpy as np objects = ('Python', 'C++', 'Java', 'Perl', 'Scala', 'Lisp') y_pos = np.arange(len(objects)) performance = [10,8,6,4,2,1] plt.barh(y_pos, performance, align='center', color = ['b','g','r','c','m','y'], alpha=0.5) plt.yticks(y_pos, objects) plt.xlabel('Usage') plt.title('Programming language usage') #plt.show() plt.savefig('barChart.png', dpi=300)

Figure 19: barChart Demo
7.10. Pie chart
- 語法:
plt.pie( 比例列表 [, 參數 1, 參數 2, …] )
- 參數:
- colors:各子圖顏色,多以 list 表示
- labels:各子圖標籤,多以 list 表示
- explode:各子圖分離突出比例,0.1 代表分離 10%,多以 list 表示
- autopct:顯示各子圖比例值,格式為%x.y%%
- startangle:繪製起始角度,預設為 0 (與三角函數角度相同)
- 若要以正圓形繪製,請再加上 plt.axis(’equal’)
- legend location
Location String Location Code ’best’ 0 ’upper right’ 1 ’upper left’ 2 ’lower left’ 3 ’lower right’ 4 ’right’ 5 ’center left’ 6 ’center right’ 7 ’lower center’ 8 ’upper center’ 9 ’center’ 10 - colors:各子圖顏色,多以 list 表示
- 範例
import matplotlib.pyplot as plt import numpy as np parts = [35.35, 23, 26.65, 15] labels = ['Harrison', 'Vanessa', 'James', 'Ruby'] colors = ['red', 'lightblue', 'purple', 'yellow'] explodes = [0.1, 0, 0, 0.1] plt.pie(parts, colors = colors, labels = labels, explode = explodes, autopct = '%3.2f%%') plt.axis('equal') plt.legend(loc='upper left') #plt.show() plt.savefig('simplePie.png', dpi=300)

Figure 20: 簡單的 pie chart
7.12. 文字註解: plt.text()
- 語法
plt.text( x相對座標, y相對座標 , 文字字串 [, 其它參數] )- 參考資料
- 範例
import matplotlib.pyplot as plt x = [1, 2, 3, 4, 5, 6, 7, 8] y = [1, 4, 9, 16, 25, 36, 49, 64] plt.plot(x, y, 'r--') for x, y in zip(x, y): plt.text(x-0.2, y+0.6, '(%d, %d)' %(x, y)) #plt.show() plt.savefig('simpleText.png', dpi = 300)

Figure 21: 簡單的文字註解
7.13. 子圖表: plt.subplot()
matplotlib下, 一個 Figure 對象可以包含多個子圖(Axes), 可以使用 subplot() 快速繪製, 其調用形式如下 :
- subplot語法
subplot(numRows, numCols, plotNum)
- 圖表的整個繪圖區域被分成 numRows 行和 numCols 列
- 然後按照從左到右,從上到下的順序對每個子區域進行編號,左上的子區域的編號為1
- plotNum 參數指定創建的 Axes 對象所在的區域
- 如果 numRows, numCols 和 plotNum 這三個數都小於 10 的話, 可以把它們縮寫為一個整數, 例如 subplot(323) 和 subplot(3,2,3) 是相同的.
- 圖表的整個繪圖區域被分成 numRows 行和 numCols 列
- subplots_adjust語法
- subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
- left = 0.125 # 子圖(subplot)距畫板(figure)左邊的距離
- right = 0.9 # 右邊
- bottom = 0.1 # 底部
- top = 0.9 # 頂部
- wspace = 0.2 # 子圖水平間距
- hspace = 0.2 # 子圖垂直間距
- subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
- 範例33
import numpy as np import matplotlib.pyplot as plt from matplotlib.ticker import NullFormatter # useful for `logit` scale # Fixing random state for reproducibility np.random.seed(19680801) # make up some data in the interval ]0, 1[ y = np.random.normal(loc=0.5, scale=0.4, size=1000) y = y[(y > 0) & (y < 1)] y.sort() x = np.arange(len(y)) # plot with various axes scales plt.figure() # linear plt.subplot(221) plt.plot(x, y) plt.yscale('linear') plt.title('linear') plt.grid(True) # log plt.subplot(222) plt.plot(x, y) plt.yscale('log') plt.title('log') plt.grid(True) # symmetric log plt.subplot(223) plt.plot(x, y - y.mean()) plt.yscale('symlog', linthreshy=0.01) plt.title('symlog') plt.grid(True) # logit plt.subplot(224) plt.plot(x, y) plt.yscale('logit') plt.title('logit') plt.grid(True) # Format the minor tick labels of the y-axis into empty strings with # `NullFormatter`, to avoid cumbering the axis with too many labels. plt.gca().yaxis.set_minor_formatter(NullFormatter()) # Adjust the subplot layout, because the logit one may take more space # than usual, due to y-tick labels like "1 - 10^{-3}" plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25, wspace=0.35) plt.savefig('simpleSubplot.png', dpi = 300)

Figure 22: 簡單的子圖表
- 進階設定
7.14. 實作練習
- Line: 輸入 a, b, c,畫出曲線圖:y=\(ax^2+bx+c\),參考結果如下:

Figure 23: 方程式曲線圖
- Bar: 將前一章的練習題所產生 30*5 的二維隨機陣列,將五科科目的平均製成長條圖,此五科科目設定為:國文、英文、數學、理化、史地。參考結果如下:

Figure 24: 分數長條圖
- Scatter: 找出國文與英文兩科成績,畫出兩科成績的散佈圖(Scatter),觀察學生在這兩科的得分是否有正相關。參考結果如下:

Figure 25: 兩科成績的關係圖
- 請自行上網搜尋學習資源 ,下載這個檔案,畫出以下圖形(提示: Google關鍵字 matplotlib, boxplot)

Figure 26: Caption
8. Pandas
8.1. 教學影片
8.2. Excel/CSV: Pandas 模組
- CSV
- 為純文字檔,以逗號分隔值(Comma-Separated Values,CSV,有時也稱為字元分隔值,因為分隔字元也可以不是逗號)。
- 純文字意味著該檔案是一個字元序列,不含必須像二進位制數字那樣被解讀的資料。
- CSV 檔案由任意數目的記錄組成,記錄間以某種換行符分隔;每條記錄由欄位組成,欄位間的分隔符是其它字元或字串,最常見的是逗號。
- 為純文字檔,以逗號分隔值(Comma-Separated Values,CSV,有時也稱為字元分隔值,因為分隔字元也可以不是逗號)。
- Pandas
- Pandas 為建構於 NumPy 之上的套件,為 python 的一個數據分析模組,適合處理表格資料或異質資料。
- 於 2009 年底開源出來,提供高效能、簡易使用的資料格式(Data Frame,為 R 語言主要資料格式),可以讓使用者可以快速操作及分析資料。
- Pandas 強化了資料處理的方便性也能與處理網頁資料與資料庫資料等,有點類似於Office 的 Excel,能更加方便的進行運算、分析等34。
- Pandas 主要特色有35:
- 在異質數據的讀取、轉換和處理上,都讓分析人員更容易處理,例如:從列欄試算表中找到想要的值。
- Pandas 提供兩種主要的資料結構,Series 與 DataFrame。Series 顧名思義就是用來處理時間序列相關的資料(如感測器資料等),主要為建立索引的一維陣列。DataFrame 則是用來處理結構化(Table like)的資料,有列索引與欄標籤的二維資料集,例如關聯式資料庫、CSV 等等。
- 透過載入至 Pandas 的資料結構物件後,可以透過結構化物件所提供的方法,來快速地進行資料的前處理,如資料補值,空值去除或取代等。
- 更多的輸入來源及輸出整合性,例如:可以從資料庫讀取資料進入 Dataframe,也可將處理完的資料存回資料庫。
- 在異質數據的讀取、轉換和處理上,都讓分析人員更容易處理,例如:從列欄試算表中找到想要的值。
- Pandas 為建構於 NumPy 之上的套件,為 python 的一個數據分析模組,適合處理表格資料或異質資料。
8.3. Pandas 資料結構
8.4. Series
- 建立: 資料類型可為 array, dictionary
- 由 array 建立 Series
import pandas as pd cars = ["SAAB", "Audi", "BMW", "BENZ", "Toyota", "Nissan", "Lexus"] print("資料型別:", type(cars)) carsToSeries = pd.Series(cars) print("資料型別:", type(carsToSeries)) print(carsToSeries.shape) print(carsToSeries) print("carsToSeries[1]: ", carsToSeries[1])
資料型別: <class 'list'> 資料型別: <class 'pandas.core.series.Series'> (7,) 0 SAAB 1 Audi 2 BMW 3 BENZ 4 Toyota 5 Nissan 6 Lexus dtype: object Audi
- 由 array 建立 Series
- 資料篩選
- 由dict來建立pandas.Series()
就是把dict的key當成index
import pandas as pd dict = { "city": "Kaohsiung", "speedCamera1": "13213", "speedCamera2": "3242", "speedCamera3": "134343", "speedCamera4": "4312", "speedCamera5": "533" } dictToSeries = pd.Series(dict, index = dict.keys()) # 排序與原 dict 相同 print(dictToSeries[0]) print("=====") print(dictToSeries['speedCamera1']) print("=====") print(dictToSeries[[0, 2, 4]]) print("=====") print(dictToSeries[['city', 'speedCamera1', 'speedCamera3']]) print("=====") print(dictToSeries[:2]) print("=====") print(dictToSeries[4:]) # 查詢 print("====會發現有一個NaN=====") search=['speedCamera1', 'speedCamera6'] print(pd.Series(dictToSeries, index=search))
Kaohsiung ===== 13213 ===== city Kaohsiung speedCamera2 3242 speedCamera4 4312 dtype: object ===== city Kaohsiung speedCamera1 13213 speedCamera3 134343 dtype: object ===== city Kaohsiung speedCamera1 13213 dtype: object ===== speedCamera4 4312 speedCamera5 533 dtype: object ====會發現有一個NaN===== speedCamera1 13213 speedCamera6 NaN dtype: object
- 由dict來建立pandas.Series()
8.5. DataFrame
- DataFrame 建立
可利用 Dictionary 或是 Array 來建立,並使用 DataFrame 的方法來操作資料查看、資料篩選、資料切片、資料排序等運算。
- 由 Dictionary 建立
import pandas as pd hobby = ["Movies", "Sports", "Coding", "Fishing", "Dancing", "cooking"] count = [46, 8, 12, 12, 6, 58] dict = {"hobby": hobby, "count": count } print(dict) df = pd.DataFrame(dict) print(df)
{'hobby': ['Movies', 'Sports', 'Coding', 'Fishing', 'Dancing', 'cooking'], 'count': [46, 8, 12, 12, 6, 58]} hobby count 0 Movies 46 1 Sports 8 2 Coding 12 3 Fishing 12 4 Dancing 6 5 cooking 58 - 由 Array 建立
import pandas as pd ary = [["Movies", 46],["Sports", 8], ["Coding", 12], ["Fishing",12], ["Dancing",6], ["cooking",8]] print(ary) ary2df = pd.DataFrame(ary, columns = ["hobby", "count"]) # 單純的二維矩陣沒有欄位名稱,要外加指定欄標籤名稱 print(ary2df)
[['Movies', 46], ['Sports', 8], ['Coding', 12], ['Fishing', 12], ['Dancing', 6], ['cooking', 8]] hobby count 0 Movies 46 1 Sports 8 2 Coding 12 3 Fishing 12 4 Dancing 6 5 cooking 8 - 讀入外部資料檔來建立 Dataframe
- 本機CSV(由本機端的PyCharm執行,不能在Google colab上跑)
- 可以從異質資料來源讀取檔案(如 CSV 檔)內容,並將資料放入 DataFrame 中,進行資料查看、資料篩選、資料切片等運算。
- 如果資料檔和程式放在同一目錄(資料夾)
- 如果資料檔放在本機在其他地方
- 絕對路徑
- 相對路徑
- 絕對路徑
- 下載範例檔:scores.csv
import pandas as pd # 讀取csv df = pd.read_csv("/Users/student/Downloads/scores.csv") #這裡的路徑要自行更改 print("===全部資料===") print(df)
===全部資料=== id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 3 201910904 93 64 86.84 44.0 25.0 4 201910905 93 56 42.11 0.0 20.0 .. ... ... ... ... ... ... 207 201911726 106 80 102.63 96.5 103.0 208 201911727 82 60 102.63 84.0 100.0 209 201911728 83 48 102.63 83.0 75.0 210 201911729 87 84 105.26 112.3 103.0 211 201911730 96 72 102.63 91.0 100.0 [212 rows x 6 columns] - 可以從異質資料來源讀取檔案(如 CSV 檔)內容,並將資料放入 DataFrame 中,進行資料查看、資料篩選、資料切片等運算。
- 線上CSV
import pandas as pd docURL = 'https://letranger.github.io/PythonCourse/scores.csv' #直接指定檔案的URL df = pd.read_csv(docURL) print('=====dataFrame的維度') print(df.shape) print('=====dataFrame的數值分佈概況') print(df.describe()) print('=====dataFrame的前5筆記錄') print(df.head(5))
=====dataFrame的維度 (212, 6) =====dataFrame的數值分佈概況 id classparti typing homework midexam finalexam count 2.120000e+02 212.000000 212.00000 212.000000 211.000000 212.000000 mean 2.019113e+08 92.386792 82.09434 89.394764 62.863033 49.458491 std 2.933047e+02 13.798874 23.06041 19.471763 34.104173 34.831187 min 2.019109e+08 14.000000 0.00000 10.750000 0.000000 0.000000 25% 2.019110e+08 87.000000 68.00000 83.880000 35.000000 20.000000 50% 2.019111e+08 94.000000 80.00000 97.370000 67.000000 45.000000 75% 2.019116e+08 101.000000 96.00000 102.630000 95.000000 76.000000 max 2.019117e+08 120.000000 120.00000 107.890000 113.500000 115.000000 =====dataFrame的前5筆記錄 id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 3 201910904 93 64 86.84 44.0 25.0 4 201910905 93 56 42.11 0.0 20.0
- 本機CSV(由本機端的PyCharm執行,不能在Google colab上跑)
8.6. DataFrame 的資料瀏覽
- 基本函數
- shape
- describe()
- head()
- tail()
- columns
- index
- info()
import pandas as pd docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) print("====df.shape====") print(df.shape) # 回傳列數與欄數 print("====df.describe()====") print(df.describe()) # 回傳描述性統計 print("====df.head(3)====") print(df.head(3)) # 回傳前三筆觀測值 print("====df.tail(3)====") print(df.tail(3)) # 回傳後三筆觀測值 print("====df.columns====") print(df.columns) # 回傳欄位名稱 print("====df.columns====") print(df.index) # 回傳 index print("====df.info====") print(df.info) # 回傳資料內容
====df.shape==== (212, 6) ====df.describe()==== id classparti typing homework midexam finalexam count 2.120000e+02 212.000000 212.00000 212.000000 211.000000 212.000000 mean 2.019113e+08 92.386792 82.09434 89.394764 62.863033 49.458491 std 2.933047e+02 13.798874 23.06041 19.471763 34.104173 34.831187 min 2.019109e+08 14.000000 0.00000 10.750000 0.000000 0.000000 25% 2.019110e+08 87.000000 68.00000 83.880000 35.000000 20.000000 50% 2.019111e+08 94.000000 80.00000 97.370000 67.000000 45.000000 75% 2.019116e+08 101.000000 96.00000 102.630000 95.000000 76.000000 max 2.019117e+08 120.000000 120.00000 107.890000 113.500000 115.000000 ====df.head(3)==== id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 ====df.tail(3)==== id classparti typing homework midexam finalexam 209 201911728 83 48 102.63 83.0 75.0 210 201911729 87 84 105.26 112.3 103.0 211 201911730 96 72 102.63 91.0 100.0 ====df.columns==== Index(['id', 'classparti', 'typing', 'homework', 'midexam', 'finalexam'], dtype='object') ====df.columns==== RangeIndex(start=0, stop=212, step=1) ====df.info==== <bound method DataFrame.info of id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 3 201910904 93 64 86.84 44.0 25.0 4 201910905 93 56 42.11 0.0 20.0 .. ... ... ... ... ... ... 207 201911726 106 80 102.63 96.5 103.0 208 201911727 82 60 102.63 84.0 100.0 209 201911728 83 48 102.63 83.0 75.0 210 201911729 87 84 105.26 112.3 103.0 211 201911730 96 72 102.63 91.0 100.0 [212 rows x 6 columns]> - shape
- DataFrame 資料排序
- sort_index(): 依欄位名稱或index值來排。The axis along which to sort. The value 0 identifies the rows, and 1 identifies the columns.
- sort_values(): 依欄位的值來排序(較常用)
- sort 後的結果為 複本 ,不改變原本的資料
import pandas as pd docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) print("====Original====") print(df.head()) print("===.sort_index()====") print(df.sort_index(axis = 1, ascending = True)) print("===.sort_index()====") print(df.sort_index(axis = 0, ascending = False)) print("===.sort_values()====") print(df.sort_values(by = 'finalexam'))
====Original==== id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 3 201910904 93 64 86.84 44.0 25.0 4 201910905 93 56 42.11 0.0 20.0 ===.sort_index()==== classparti finalexam homework id midexam typing 0 100 16.0 92.11 201910901 48.0 72 1 96 40.0 93.42 201910902 60.0 56 2 96 28.0 102.63 201910903 45.0 76 3 93 25.0 86.84 201910904 44.0 64 4 93 20.0 42.11 201910905 0.0 56 .. ... ... ... ... ... ... 207 106 103.0 102.63 201911726 96.5 80 208 82 100.0 102.63 201911727 84.0 60 209 83 75.0 102.63 201911728 83.0 48 210 87 103.0 105.26 201911729 112.3 84 211 96 100.0 102.63 201911730 91.0 72 [212 rows x 6 columns] ===.sort_index()==== id classparti typing homework midexam finalexam 211 201911730 96 72 102.63 91.0 100.0 210 201911729 87 84 105.26 112.3 103.0 209 201911728 83 48 102.63 83.0 75.0 208 201911727 82 60 102.63 84.0 100.0 207 201911726 106 80 102.63 96.5 103.0 .. ... ... ... ... ... ... 4 201910905 93 56 42.11 0.0 20.0 3 201910904 93 64 86.84 44.0 25.0 2 201910903 96 76 102.63 45.0 28.0 1 201910902 96 56 93.42 60.0 40.0 0 201910901 100 72 92.11 48.0 16.0 [212 rows x 6 columns] ===.sort_values()==== id classparti typing homework midexam finalexam 81 201911108 95 100 66.89 0.0 0.0 36 201910937 89 84 97.37 4.0 0.0 139 201911330 95 72 51.32 30.0 0.0 45 201911009 71 64 99.78 36.2 0.0 49 201911013 88 76 70.61 15.0 0.0 .. ... ... ... ... ... ... 196 201911715 106 120 107.89 102.4 105.0 195 201911714 102 44 107.89 110.0 110.0 88 201911115 120 88 105.26 113.5 111.0 143 201911334 100 120 81.58 104.8 113.0 98 201911125 119 80 107.89 113.2 115.0 [212 rows x 6 columns] - sort_index(): 依欄位名稱或index值來排。The axis along which to sort. The value 0 identifies the rows, and 1 identifies the columns.
- DataFrame 處理遺漏值
- dropna()
- fillna()
- isnull()
- notnull()
- 下載scores-null.csv
import pandas as pd docURL = 'https://letranger.github.io/PythonCourse/scores-null.csv' df = pd.read_csv(docURL) #df = pd.read_csv('scores-null.csv') print("====原始資料====") print(df) # 刪除有遺失值的記錄 print("====刪除有遺失值的記錄====") dropValue = df.dropna() print(dropValue) fill0 = df.fillna(0) print("====遺失值填零====") print(fill0) fillv = df.fillna({"classparti":999, "typing":0, "finalExam":"NULL"}) print("====遺失值填特定值====") print(fillv)
====原始資料==== id classparti typing homework finalExam 0 201910901 100.0 72.0 92.11 48.0 1 201910902 96.0 56.0 93.42 60.0 2 201910903 96.0 76.0 102.63 NaN 3 201910904 NaN 64.0 86.84 44.0 4 201910905 93.0 56.0 42.11 0.0 5 201910906 101.0 108.0 100.00 NaN 6 201910907 101.0 NaN 92.11 55.0 7 201910908 94.0 68.0 105.26 61.0 8 201910909 NaN 64.0 44.74 20.0 9 201910910 93.0 120.0 97.37 16.0 ====刪除有遺失值的記錄==== id classparti typing homework finalExam 0 201910901 100.0 72.0 92.11 48.0 1 201910902 96.0 56.0 93.42 60.0 4 201910905 93.0 56.0 42.11 0.0 7 201910908 94.0 68.0 105.26 61.0 9 201910910 93.0 120.0 97.37 16.0 ====遺失值填零==== id classparti typing homework finalExam 0 201910901 100.0 72.0 92.11 48.0 1 201910902 96.0 56.0 93.42 60.0 2 201910903 96.0 76.0 102.63 0.0 3 201910904 0.0 64.0 86.84 44.0 4 201910905 93.0 56.0 42.11 0.0 5 201910906 101.0 108.0 100.00 0.0 6 201910907 101.0 0.0 92.11 55.0 7 201910908 94.0 68.0 105.26 61.0 8 201910909 0.0 64.0 44.74 20.0 9 201910910 93.0 120.0 97.37 16.0 ====遺失值填特定值==== id classparti typing homework finalExam 0 201910901 100.0 72.0 92.11 48.0 1 201910902 96.0 56.0 93.42 60.0 2 201910903 96.0 76.0 102.63 NULL 3 201910904 999.0 64.0 86.84 44.0 4 201910905 93.0 56.0 42.11 0.0 5 201910906 101.0 108.0 100.00 NULL 6 201910907 101.0 0.0 92.11 55.0 7 201910908 94.0 68.0 105.26 61.0 8 201910909 999.0 64.0 44.74 20.0 9 201910910 93.0 120.0 97.37 16.0 - dropna()
8.7. Dataframe 的選取與過濾
- 資料選取: Select column(s) 36
df[['col1', 'col2']]df.col1
import pandas as pd docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) print(df.head(3)) print(df[['id', 'typing']].head(3)) print(df.id, df.homework)
id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 id typing 0 201910901 72 1 201910902 56 2 201910903 76 0 201910901 1 201910902 2 201910903 3 201910904 4 201910905 ... 207 201911726 208 201911727 209 201911728 210 201911729 211 201911730 Name: id, Length: 212, dtype: int64 0 92.11 1 93.42 2 102.63 3 86.84 4 42.11 ... 207 102.63 208 102.63 209 102.63 210 105.26 211 102.63 Name: homework, Length: 212, dtype: float64 - 資料選取: Select using index (row)
df[1:20]
import pandas as pd docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) print(df[1:3]) print(df[df.homework<30]) # 合併row and column print(df.id[df.homework<30]) print(df[['id', 'homework']][1:3])
id classparti typing homework midexam finalexam 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 id classparti typing homework midexam finalexam 10 201910911 62 44 20.39 4.0 0.0 18 201910919 14 56 25.92 0.0 0.0 23 201910924 75 48 10.75 0.0 0.0 124 201911315 71 120 15.35 0.0 0.0 140 201911331 93 120 25.88 0.0 16.0 10 201910911 18 201910919 23 201910924 124 201911315 140 201911331 Name: id, dtype: int64 id homework 1 201910902 93.42 2 201910903 102.63 - 條件式選取資料
- 語法
df[(condition)]df[(condition 1) & (condition 2) ]
- 範例
import pandas as pd docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) print(df[(df.midexam >= 100) & (df.finalexam >= 100)])
id classparti typing homework midexam finalexam 11 201910912 87 92 92.11 110.8 103.75 70 201911034 104 92 102.63 100.8 101.00 88 201911115 120 88 105.26 113.5 111.00 98 201911125 119 80 107.89 113.2 115.00 102 201911129 106 52 102.63 103.6 100.00 127 201911318 108 80 102.63 104.8 100.00 138 201911329 105 48 107.89 100.0 100.00 143 201911334 100 120 81.58 104.8 113.00 153 201911608 88 0 102.63 100.0 100.00 169 201911624 82 100 102.63 101.5 105.00 182 201911701 103 108 92.63 100.0 100.00 189 201911708 106 108 102.63 107.2 101.00 190 201911709 108 88 107.89 104.8 105.00 191 201911710 102 100 102.63 100.0 105.00 193 201911712 105 96 101.58 107.5 100.00 195 201911714 102 44 107.89 110.0 110.00 196 201911715 106 120 107.89 102.4 105.00 198 201911717 80 120 107.89 106.0 100.00 201 201911721 109 76 100.00 100.0 100.00 203 201911722 105 120 102.63 101.5 105.00 210 201911729 87 84 105.26 112.3 103.00
- 語法
8.8. Dataframe 進階條件過濾
- loc, iloc, between 37
- loc: 基於行標籤和列標籤(x_label、y_label)進行索引,以 column 名做為 index
- iloc: 基於行索引和列索引(index,columns) 都是從 0 開始,以數字做為 index
- between: 檢查區間值,
Series.between(self, left, right, inclusive=True)
- 範例 1
import pandas as pd docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) print(df.head(3)) print("========") print(df.loc[df.homework<25, ['id', 'homework']]) print("========") print(df.loc[2,'homework'])
id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 ======== id homework 10 201910911 20.39 23 201910924 10.75 124 201911315 15.35 ======== 102.63 - 範例 2
# 載入函式庫 import pandas as pd # 讀取csv docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) # 輸出前3筆 print(df.iloc[:3]) # 輸出前3筆的第2,3欄 print(df.iloc[:2, 1:3]) print(df['midexam'].between(50, 60)) midFilter = df['midexam'].between(50, 55) print(df[midFilter]) #也可以直接寫成 df[ df['midexam'].between(50, 55)], 和NumPy的語法類似
Pandas version: 0.25.1 id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 classparti typing 0 100 72 1 96 56 102.63 0 False 1 True 2 False 3 False 4 False ... 207 False 208 False 209 False 210 False 211 False Name: midexam, Length: 212, dtype: bool id classparti typing homework midexam finalexam 6 201910907 101 120 92.11 55.0 20.0 65 201911029 93 60 82.00 50.0 16.0 72 201911036 88 80 102.63 53.0 76.0 92 201911119 82 104 89.47 50.0 20.0 94 201911121 95 100 88.51 52.0 72.0 109 201911136 91 100 81.58 51.0 4.0 171 201911626 98 72 97.37 50.0 75.0
- loc: 基於行標籤和列標籤(x_label、y_label)進行索引,以 column 名做為 index
8.9. 進階分析
- 由其他 column 產生新的 column(重要、實用)
- 全部指定
- 條件指定
- 全部指定
- 資料分組: groupby
- 範例
# 載入函式庫 import pandas as pd # 讀取csv docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) # 計算總分' df['Final'] = df.classparti*.1 + df.typing*.1 + df.homework*.2 + df.midexam*.3 + df.finalexam*.3 # PASS/FAIL df.loc[df.Final < 60, 'PASS'] = False df.loc[df.Final >= 60, 'PASS'] = True # 新增一個打字速度的欄位 df.loc[df.typing < 30, 'TypingSpeed' ] = 'LOW' df.loc[df.typing.between(30, 60), 'TypingSpeed' ] = 'MID' df.loc[df.typing > 60, 'TypingSpeed' ] = 'HIGH' print(df.head(3)) # 依打字速度分組,看不同組別的學期總成績分佈 print('---依打字速度分組---') print(df.groupby('TypingSpeed')['Final'].mean()) print(df.groupby('PASS')['typing' ,'homework'].mean()) # 罝換row/column print('---置換row/column(轉90度)---') print(df.groupby('PASS')['typing' ,'homework'].mean().T)
id classparti typing homework midexam finalexam 0 201910901 100 72 92.11 48.0 16.0 1 201910902 96 56 93.42 60.0 40.0 2 201910903 96 76 102.63 45.0 28.0 id classparti typing ... Final PASS TypingSpeed 0 201910901 100 72 ... 54.822 False HIGH 1 201910902 96 56 ... 63.884 True MID 2 201910903 96 76 ... 59.626 False HIGH [3 rows x 9 columns] ---依打字速度分組--- TypingSpeed HIGH 69.977451 LOW 68.517333 MID 64.086303 Name: Final, dtype: float64 typing homework PASS False 77.567568 76.409054 True 84.525547 96.350730 ---置換row/column(轉90度)--- PASS False True typing 77.567568 84.525547 homework 76.409054 96.350730 - 資料檔下載: scores.csv
8.10. 資料視覺化
- Bar chart
# 載入函式庫 import pandas as pd # 讀取csv docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) # 計算總分' df['Final'] = df.classparti*.1 + df.typing*.1 + df.homework*.2 + df.midexam*.3 + df.finalexam*.3 # create new column according to typing speed df.loc[df.typing < 30, 'TypingSpeed' ] = 'Level-1' df.loc[df.typing.between(30, 60), 'TypingSpeed' ] = 'Level-2' df.loc[df.typing > 60, 'TypingSpeed' ] = 'Level-3' x = df.groupby('TypingSpeed')['homework','classparti'].mean() print(x) # 關於圖表的屬性設定方式 fig = df.groupby('TypingSpeed')['homework','classparti'].mean().plot(kind='bar', title="XXX", rot=0, legend=True) fig.set_xlabel("Typing Speed") fig.set_ylabel("score") # savefig = fig.get_figure() # 如果不存圖檔就不用設定這行 # 關於圖表的屬性設定方式 fig1 = df.groupby('TypingSpeed')['homework','classparti'].mean().T.plot(kind='bar', title="XXX", rot=0, legend=True) fig1.set_xlabel("Subject") fig1.set_ylabel("score") # 如果不存圖檔就不用執行以下程式 #savefig1 = fig1.get_figure() #savefig.savefig('images/pandasPlot1.png', bbox_inches='tight') #savefig1.savefig('images/pandasPlot10.png', bbox_inches='tight')
homework classparti TypingSpeed Level-1 96.053333 87.666667 Level-2 85.273939 88.696970 Level-3 90.053920 93.159091
Figure 27: Pandas plot bar chart

Figure 28: Pandas plot bar chart
- Pie chart
- DEMO
# 載入函式庫 import pandas as pd # 讀取csv docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) # 計算總分' df['Final'] = df.classparti*.1 + df.typing*.1 + df.homework*.2 + df.midexam*.3 + df.finalexam*.3 # create new column according to typing speed df.loc[df.typing < 60, 'TypingSpeed' ] = 'Level-1' df.loc[df.typing.between(60, 80), 'TypingSpeed' ] = 'Level-2' df.loc[df.typing > 80, 'TypingSpeed' ] = 'Level-3' print(df.groupby('TypingSpeed').count()) df.groupby('TypingSpeed')['TypingSpeed'].count().plot(kind='pie', title="XXX", rot=0, legend=True) # 如果不存圖檔就不用執行以下程式 # fig = df.groupby('TypingSpeed')['TypingSpeed'].count().plot(kind='pie', title="XXX", rot=0, legend=True) # savefig = fig.get_figure() # savefig.savefig('images/pandasPlot2.png', bbox_inches='tight')
id classparti typing homework midexam finalexam Final
TypingSpeed
Level-1 25 25 25 25 25 25 25
Level-2 86 86 86 86 86 86 86
Level-3 101 101 101 101 100 101 100

Figure 29: Pandas plot pie chart
- 如何避免圖例擋住圖表
- DEMO
- Scatter chart
# 載入函式庫 import pandas as pd # 讀取csv docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) df[['midexam','finalexam']].plot(kind='scatter', x=1, y=0) # 如果不存圖檔就不用執行以下程式 #fig = df[['midexam','finalexam']].plot(kind='scatter', x=1, y=0) #savefig = fig.get_figure() #savefig.savefig('images/PandasPlot3.png')

Figure 30: Pandas plot scatter chart
- Haxbin chart
# 載入函式庫 import pandas as pd # 讀取csv docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) df[['midexam','finalexam']].plot(kind='hexbin', x=1, y=0, gridsize=25) #fig = df[['midexam','finalexam']].plot(kind='hexbin', x=1, y=0, gridsize=25) #savefig = fig.get_figure() #savefig.savefig('images/PandasPlot6.png')

Figure 31: Pandas plot hexbin chart
- Regression line chart
# 載入函式庫 import pandas as pd import seaborn as sns # 讀取csv docURL = 'https://letranger.github.io/PythonCourse/scores.csv' df = pd.read_csv(docURL) sns.regplot(x="midexam", y='finalexam', data=df); #fig = sns.regplot(x="midexam", y='finalexam', data=df); #savefig = fig.get_figure() #savefig.savefig('images/PandasPlot4.png')

Figure 32: Pandas plot scatter chart
8.11. 實作練習
- 403 期中考得分統計
- 原始數據

Figure 33: CSV 內容
- cs109score.csv為 T 市某校的資訊科線上期中考系統所匯出的成績檔,該次考試共計 10 題,
其中 A~F 為基本題,B1~B4 為加分題,前 5 題(A,B,C,D,E)為基本題,每題 100 分,後 4 題
為加分題(Bonus1~Bonus4),每題 25 分,本份考卷總分:600;總得分除以 5 即為期中考得分,此次期中考總分 120 分。 - 任務 1: [直接輸出結果]將有缺失值的部份以 0 分取代。
- 任務 2: [直接輸出結果]求出每個人的原始總分(即所有分數),新增的欄位名稱為TOTAL。
- 任務 3: [直接輸出結果]求出每個人的實際得分(原始總分TOTAL除以 5)。
- 任務 4: [直接輸出結果]由學號欄中提出班級資料, 新增一欄班級欄(欄位名稱CLAS),班級為學號的第 5~7 碼,如 201910107,
表示該生為 101 班。 任務 5: 求出各題的平均得分,以直條長條圖顯示。如下圖:

Figure 34: task5
任務 6: 以班級為分組依據,畫出各班平均得分,以橫向長條圖顯示。如下圖:

Figure 35: task6
任務 7: 求 119 班各題平均得分,以 pie chart 顯示。如下圖:

Figure 36: task7
任務 8: 求所有受試者第 B 及 B1 兩題的 scatter plot 以及 regression line。如下圖:

Figure 37: task8
- 原始數據
- 進階練習
一dataframe如下
import numpy as np import pandas as pd scores = {'Math': [900, 50, 70, 80], 'English': [60, 30, 90, 50], 'History': [33, 75, np.NaN, np.NaN]} df = pd.DataFrame(scores, index=['Simon', 'Allen', 'Jimmy', 'Vanessa'])
試解決以下問題:
- 列出Math分數>100資料
- 將Siman之Math改為90分
- 計算History遺漏值筆數
- 以History平均值取代History中遺漏值
- 新增“平均”欄位,計算四人各科平均
- 新增“名次”欄位,以四人平均分數為排序依據
- 列出平均不及格者
- 列出數學最高分者姓名
- 列出Math分數>100資料
9. 網路資料解析與爬蟲
9.1. JSON
- 網路資料分析的兩種類型
- 可直接下載的靜態結構化資料,如 CSV、JSON、XML
- 直接分析網站的線上內容(HTML): 爬蟲(Web Crawler)
- 可直接下載的靜態結構化資料,如 CSV、JSON、XML
- JSON
- What is JSON?
- JSON(JavaScript Object Notation,JavaScript 物件表示法)是個以純文字來描述資料的簡單結構,在 JSON 中可以透過特定的格式去儲存任何資料(字串,數字,陣列,物件),也可以透過物件或陣列來傳送較複雜的資料。38
- JSON 常用於網站上的資料呈現、傳輸 (例如將資料從伺服器送至用戶端,以利顯示網頁)。
- 一旦建立了 JSON 資料,就可以非常簡單的跟其他程式溝通或交換資料,因為 JSON 就只是純文字格式。
- JSON(JavaScript Object Notation,JavaScript 物件表示法)是個以純文字來描述資料的簡單結構,在 JSON 中可以透過特定的格式去儲存任何資料(字串,數字,陣列,物件),也可以透過物件或陣列來傳送較複雜的資料。38
- JSON 的優點
- 相容性高
- 格式容易瞭解,閱讀及修改方便
- 支援許多資料格式 (number,string,booleans,nulls,array,associative array)
- 許多程式都支援函式庫讀取或修改 JSON 資料
- NoSQL Database
- 相容性高
- JSON 結構
- 物件: {}
- 陣列: []
- 物件: {}
- JSON 支援的資料格式
- 字串 (string),要以雙引號括起來:
- {“name”: “Mike”}
- {“name”: “Mike”}
- 數值 (number)
- {“age”: 25}
- {“age”: 25}
- 布林值 (boolean)
- {“pass”: True}
- {“pass”: True}
- 空值 (null)
- {“middlename”: null}
- {“middlename”: null}
- 物件 (object)
- 陣列 (array)
- 字串 (string),要以雙引號括起來:
- JSON 物件
- 格式
- key 只能是字串,也一定要加上雙引號
{ "key1": value1, "key2": value2, ...... "keyN": valueN }- 範例 1
{ "id": 1, "name": "Jamees, Yen", "age": 19, "gender": "M", "hobby": ["music", "programming"] }- 範例 2
{ "id": 382192 "name": "Jamees, Yen", "age": 19, "gender": "M", "exams": [ { "title": "期中考", "chinese": 85, "math": 98, "englihs": 92 }, { "title": "期末考", "chinese": 81, "math": 92, "englihs": 97 } ], "hobby": ["music", "programming"] } - 格式
- JSON轉PANDAS dataFrame
from pandas.io.json import json_normalize df = json_normalize( list of dict ) print(df.head(3))
- What is JSON?
- JSON實作範例
- 實作 1: 國際主要國家貨幣每月匯率概況
- 下載 JSON
# -*- coding: utf-8 -*- import requests json_url = 'https://quality.data.gov.tw/dq_download_json.php?nid=11339&md5_url=f2fdbc21603c55b11aead08c84184b8f' response = requests.get(json_url) print(response) jsonRes = response.json() print(type(jsonRes)) import json print(jsonRes[:1]) print(json.dumps(jsonRes[:2], indent = 4, ensure_ascii=False)) print('日期', ":", '美元/新台幣') for item in jsonRes: print(item['日期'], ":", item['美元/新台幣'])
<Response [200]> <class 'list'> [{'日期': '20201005', '美元/新台幣': '29.02', '人民幣/新台幣': '4.29882', '歐元/美元': '1.17405', '美元/日幣': '105.645', '英鎊/美元': '1.2947', '澳幣/美元': '0.71775', '美元/港幣': '7.75005', '美元/人民幣': '6.7507', '美元/南非幣': '16.4019', '紐幣/美元': '0.66435'}] [ { "日期": "20201005", "美元/新台幣": "29.02", "人民幣/新台幣": "4.29882", "歐元/美元": "1.17405", "美元/日幣": "105.645", "英鎊/美元": "1.2947", "澳幣/美元": "0.71775", "美元/港幣": "7.75005", "美元/人民幣": "6.7507", "美元/南非幣": "16.4019", "紐幣/美元": "0.66435" }, { "日期": "20201006", "美元/新台幣": "28.96", "人民幣/新台幣": "4.300705", "歐元/美元": "1.17735", "美元/日幣": "105.555", "英鎊/美元": "1.297", "澳幣/美元": "0.7156", "美元/港幣": "7.75005", "美元/人民幣": "6.7338", "美元/南非幣": "16.63735", "紐幣/美元": "0.66365" } ] 日期 : 美元/新台幣 20201005 : 29.02 20201006 : 28.96 20201007 : 28.965 20201008 : 28.966 20201012 : 28.909 20201013 : 28.92 20201014 : 28.952 20201015 : 28.96 20201016 : 28.979 20201019 : 28.95 20201020 : 28.932 20201021 : 28.892 20201022 : 28.903 20201023 : 28.917 20201026 : 28.902 20201027 : 28.872 20201028 : 28.906 20201029 : 28.914 20201030 : 28.925 20201102 : 28.909 20201103 : 28.92 20201104 : 29.006 20201105 : 28.874 20201106 : 28.876 20201109 : 28.825 20201110 : 28.856 20201111 : 28.83 20201112 : 28.86 20201113 : 28.847 20201116 : 28.81 20201117 : 28.815 20201118 : 28.758 20201119 : 28.818 20201120 : 28.82 20201123 : 28.803 20201124 : 28.831 20201125 : 28.816 20201126 : 28.811
- 下載 JSON
- 實作 2: 澎湖生活博物館每月參觀人次統計資料
#coding:utf-8 import requests import pandas as pd from datetime import datetime json_url = 'http://opendataap2.penghu.gov.tw/resource/files/2020-01-12/eaa641fc3af66277e60b13201ca11232.json' response = requests.get(json_url) jsonRes = response.json() year = [str(int(i['年度'])+1911) for i in jsonRes] month = [i['月份'] for i in jsonRes] visitor = [int(i['人數']) for i in jsonRes] dates = [x+'/'+y for x, y in zip(year, month)] from datetime import datetime dates = [datetime.strptime(x, '%Y/%m').date() for x in dates] import matplotlib.pyplot as plt plt.rcParams['font.family'] = 'cwTeXFangSong' #這裡要改成colab的中文化設定 plt.clf() plt.barh(dates, visitor) plt.xticks(rotation=0, fontsize=10) plt.yticks(rotation=0, fontsize=6) plt.xlabel('人數') plt.ylabel('日期') plt.title('澎湖生活博物館每月參觀人次') plt.savefig('images/jsonBar.png', dpi=300, bbox_inches='tight')

Figure 38: 參觀人數
- 實作 3: 學校甄選公告#1
import requests import json json_url = 'http://www.kh.edu.tw/json/bulletin/employ/datagrid?page=1&rows=20' response = requests.get(json_url) print(response) # 方法1 jsonRes1 = response.json() print("========\n", type(jsonRes1)) # jsonRes2 = json.loads(response.text) print("========\n", type(jsonRes2)) print("========\n", jsonRes2['rows'][:2]) # 先將JSON的資料轉為PANDAS for item in jsonRes2['rows']: #將attributes裡的k,v移出來 item['subject'] = item['attributes']['subjects'] item['url'] = item['attributes']['url'] #將attribute刪掉 del item['attributes'] from pandas.io.json import json_normalize import matplotlib.pyplot as plt df = json_normalize(jsonRes2['rows'][:5]) print(df.head(3))
<Response [200]> ======== <class 'dict'> ======== <class 'dict'> ======== [{'author': '正興國中', 'attributes': {'subjects': '國文', 'url': 'https://employ.kh.edu.tw/Html/2023/3/三民區111學年度正興國中第6號第1次公告簡章.html', 'target': '_blank'}, 'title': '111學年度正興國中第6號第1次公告', 'pubDate': '112-03-09'}, {'author': '文山國小', 'attributes': {'subjects': '專任輔導代理育嬰', 'url': 'https://employ.kh.edu.tw/Html/2023/3/鳳山區111學年度文山國小第10號第2次公告簡章.html', 'target': '_blank'}, 'title': '111學年度文山國小第10號第2次公告', 'pubDate': '112-03-09'}] author ... url 0 正興國中 ... https://employ.kh.edu.tw/Html/2023/3/三民區111學年度... 1 文山國小 ... https://employ.kh.edu.tw/Html/2023/3/鳳山區111學年度... 2 竹圍國小 ... https://employ.kh.edu.tw/Html/2023/3/岡山區111學年度... [3 rows x 5 columns] - 實作 4: 學校甄選公告
import requests import json response = requests.get('http://www.kh.edu.tw/json/bulletin/employ/datagrid?page=1&rows=20') jsonRes = response.json() #print(jsonRes['rows']) for item in jsonRes['rows']: print(item['author'], ":", item['title'],"/", item['pubDate']) # sortJR = jsonRes['rows'] # print(type(sortJR)) # #sortJR.sort(key=lambda x: x['author'], reverse=False) #for item in sortJR: # print(item['author'], ":", item['title'],"/", item['pubDate'])
明誠高中 : 109學年度明誠高中第1號第1次公告 / 109-06-01 八卦國小 : 109學年度八卦國小第1號第1次公告 / 109-06-01 新莊國小 : 109學年度新莊國小第1號第1次公告 / 109-06-01 楠梓國中 : 108學年度楠梓國中第9號第3次公告 / 109-06-02 燕巢國中 : 108學年度燕巢國中第14號第11次公告 / 109-05-29 前鎮國中 : 108學年度前鎮國中第10號第1次公告 / 109-05-25 仁武特殊教育學校 : 108學年度仁武特殊教育學校第7號第5次公告 / 109-05-12 大社國小 : 108學年度大社國小第7號第3次公告 / 109-05-11 明誠高中 : 108學年度明誠高中第3號第3次公告第1次修正 / 109-05-04 鼓山高中 : 108學年度鼓山高中第11號第3次公告 / 109-05-04 南成國小 : 108學年度南成國小第3號第3次公告 / 109-05-01 青山國小 : 108學年度青山國小第8號第1次公告 / 109-04-30 明宗國小 : 108學年度明宗國小第5號第1次公告 / 109-04-30 大社國中 : 108學年度大社國中第5號第3次公告 / 109-04-29 小港國小 : 108學年度小港國小第15號第3次公告 / 109-04-21 小港國小 : 108學年度小港國小第13號第2次公告 / 109-04-18 鼓山國小 : 108學年度鼓山國小第6號第3次公告 / 109-04-16 新民國小 : 108學年度新民國小第9號第3次公告 / 109-04-14 林園高中 : 108學年度林園高中第18號第3次公告 / 109-04-14 大社國中 : 108學年度大社國中第4號第3次公告 / 109-04-13
- 實作 1: 國際主要國家貨幣每月匯率概況
9.2. JSON課堂練習
- [注意]如果JSON的資料下載發生錯誤,可以試著將URL由’https://’改為’http://’
- 由高雄市政府資料開放平台找到[高雄市各級學校甄選公告],線上讀取其線上 JSON 資料,輸出校名及出缺科別。
- 由政府資料開放平台找到高雄市電動機車充電站名稱及充電站地址,線上讀取 JSON 檔,列出所有高雄市機車充電站之[計費方式]以及[充電站所在位址]。
請上網查詢台南市各區WIFI熱點數量,依行政區統計,繪製類似以下圖形,儘可能加上統計圖表所需元素並加以美化

Figure 39: Caption
9.3. JSON進階閱讀
9.4. HTML
- What is HTML
- 政府資料開放平台
- 超文本標記語言(HyperText Markup Language)是一種用於建立網頁的標準標記語言。HTML 是一種基礎技術,常與 CSS、JavaScript 一起被眾多網站用於設計網頁、網頁應用程式以及行動應用程式的使用者介面。39
- HTML 元素是構建網站的基石。HTML 允許嵌入圖像與物件,並且可以用於建立互動式表單,它被用來結構化資訊——例如標題、段落和列表等等,也可用來在一定程度上描述文件的外觀和語意。39
- HTML 的語言形式為<>包圍的 HTML 元素(如<html>),瀏覽器使用 HTML 標籤和指
令碼來詮釋網頁內容,但不會將它們顯示在頁面上。 39 - 網頁就是由各式標籤 (tag) 所組成的階層式文件。
- 政府資料開放平台
- HTML 範例
- HTML code
<html> <head> <title>我是網頁標題</title> <style> .large { color:blue; text-align: center; } </style> </head> <body> <h1 class="large">我是變色且置中的抬頭</h1> <p id="p1">我是段落一</p> <p id="p2" style="">我是段落二</p> <div><a href='http://blog.castman.net' style="font-size:200%;">我是放大的超連結</a></div> </body> </html>
- RESULTS
我是網頁標題 我是變色且置中的抬頭
我是段落一
我是段落二
- HTML tree

Figure 40: HTML tree
- HTML 文件內不同的標籤 (例如 <title>, <h1>, <p>, <a>, <div>)
- 不同的標籤有著不同的語義,表示建構網頁用的不同元件,且標籤可以有各種屬性 (例如 id, class, style 等通用屬性, 或 href 等專屬屬性),
- <div>是division這個單字取前面三個字母來表示,division是區分的意思,div標籤主要的功能就是在形成一個個的區塊,方便網頁排版美化40。例如:
<div style="background-color:grey;"> <p>今天是第7天的介紹,div範例程式</p> <p>今天是第7天的介紹,div範例程式</p> </div>
其結果為:
今天是第7天的介紹,div範例程式
今天是第7天的介紹,div範例程式
- 我們可以用標籤 + 屬性去定位資料所在的區塊並取得資料。41
- HTML code
9.5. 網路爬蟲
- 爬蟲精神:將程式模仿成一般 user
- 原則
- 要抓網路資料,就要先仔細觀察網頁內容
- 要儘量欺騙網站自己是 browser
- 要抓網路資料,就要先仔細觀察網頁內容
- 實作範例: 取得 HTML 資料
- 第一版: 以urllib模組來抓取HTML資料
urllib.request 是一個用來從 URLs (Uniform Resource Locators)取得資料的 Python 模組。它提供一個了非常簡單的介面能接受多種不同的協議(protocol, 如 http, ftp), urlopen 函數。
# 抓取PTT電影版header import urllib.request as req url = "https://www.ptt.cc/bbs/Movie/index.html" with req.urlopen(url) as response: data = response.read().decode("utf-8") print(data)
結果得到如下錯誤訊息:
urllib.error.HTTPError: HTTP Error 403: Forbidden
被拒絕原因:這隻程式的行為不像一般使用者,被網站伺服器拒絕。403 Forbidden 是 HTTP 協議中的一個 HTTP 狀態碼(Status Code)。可以簡單的理解為沒有權限訪問此站,服務器收到請求但拒絕提供服務42。
- 第二版:加入 headers
import urllib.request as req url = "https://www.ptt.cc/bbs/Movie/index.html" # 幫request加上一個header request = req.Request(url, headers = { "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0" }) with req.urlopen(request) as response: data = response.read().decode("utf-8") print('所取得的資料型態:',type(data)) print('===得到的部份HTML內容===\n', data[2651:3500])
所取得的資料型態: <class 'str'> ===得到的部份HTML內容=== <div class="r-ent"> <div class="nrec"></div> <div class="title"> <a href="/bbs/movie/M.1678501889.A.BD7.html">[新聞] 迪士尼、網飛都搶合作 台灣女力吳采頤</a> </div> <div class="meta"> <div class="author">filmwalker</div> <div class="article-menu"> <div class="trigger">⋯</div> <div class="dropdown"> <div class="item"><a href="/bbs/movie/search?q=thread%3A%5B%E6%96%B0%E8%81%9E%5D+%E8%BF%AA%E5%A3%AB%E5%B0%BC%E3%80%81%E7%B6%B2%E9%A3%9B%E9%83%BD%E6%90%B6%E5%90%88%E4%BD%9C%E3%80%80%E5%8F%B0%E7%81%A3%E5%A5%B3%E5%8A%9B%E5%90%B3%E9%87%87%E9%A0%A4">搜尋同標題文章</a></div> <div class="item"><a href="/bbs/movie/search?q=author%3Afilmwalker">搜尋看板內 filmwalker 的文章</a></div> </div> </div> <div class="date"> 3/11</div> <div class="mark"></div> </div> </div>
可以發現抓取下來的結果都是字串型態,不太容易進一步擷取所需資訊。例如,當我們想抓取所有看板中的標題,只使用字串所提供的一些函式就很難完成工作。
- 第一版: 以urllib模組來抓取HTML資料
- 原則
- BeautifulSoup
- BeautifulSoup4 簡介
- BeautifulSoup4 和 lxml 一樣,Beautiful Soup 也是一個 HTML/XML 的解析器,
- 主要的功能是解析和提取 HTML/XML 資料。
- BeautifulSoup4 和 lxml 一樣,Beautiful Soup 也是一個 HTML/XML 的解析器,
- 實作:解析 HTML 資料內容
- 安裝解析 HTML 所需套件(安裝套件名稱後面有 4)
pip install beautifulsoup4
- 第三版: 解析 HTML
import urllib.request as req url = "https://www.ptt.cc/bbs/Movie/index.html" # 幫request加上一個header request = req.Request(url, headers = { "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0" }) with req.urlopen(request) as response: data = response.read().decode("utf-8") import bs4 root = bs4.BeautifulSoup(data,"html.parser") # 依序執行丁列程式 print(type(root)) #取得的資料型態變成是bs4的物件,而非字串 print(type(root.prettify())) #可以先美化輸出,方便查看HTML結構 print(root.prettify()[:100])
<class 'bs4.BeautifulSoup'> <class 'str'> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta content="width=device-width, initia
<class 'bs4.BeautifulSoup'> <class 'str'>
- 安裝解析 HTML 所需套件(安裝套件名稱後面有 4)
- Beautifulsoup 解析器
現在我們可以進一步來研究bs4的玩法
- 在上面的語句中,我們使用了一個 html.parser。 這是一個解析器,在構造BeautifulSoup 物件的時候,需要用到解析器。BeautifulSoup 支援 python 內建的解析器和少數第三方解析器。43:

Figure 41: Parsers 比較
- 一般來說,對於速度或效能要求不太高的話,也可以使用 html5lib 來進行解析;如果較在乎效能則建議使用 lxml 來進行解析。
如果要換用html5lib,則要先安裝:
1: pip install html5lib接下來更換parser:
import urllib.request as req url = "https://www.ptt.cc/bbs/Movie/index.html" # 幫request加上一個header request = req.Request(url, headers = { "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0" }) with req.urlopen(request) as response: data = response.read().decode("utf-8") import bs4 root = bs4.BeautifulSoup(data,"html5lib") # 依序執行丁列程式 print(type(root)) #取得的資料型態變成是bs4的物件,而非字串 print(type(root.prettify())) #可以先美化輸出,方便查看HTML結構 print(root.prettify()[:100])
<class 'bs4.BeautifulSoup'> <class 'str'> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta content="width=device-width, initia
- 在上面的語句中,我們使用了一個 html.parser。 這是一個解析器,在構造BeautifulSoup 物件的時候,需要用到解析器。BeautifulSoup 支援 python 內建的解析器和少數第三方解析器。43:
- BeautifulSoup4 四大物件
BeautifulSoup4 將複雜 HTML 文件轉換成一個複雜的樹形結構,每個節點都是 Python 物件,所有物件可以歸納為 4 種:
- Tag: HTML 中的一個個標籤,例如:<title>, <head>, <div>, <link>, <a>…,Tag 下擁有許多屬性和方法,和前端類似,例如 a 標籤一定會有它的 href 屬性,某些屬性是某些標籤所獨有的。
- NavigableString: 標籤內部的文字: .string
- BeautifulSoup: BeautifulSoup 物件就是通過解析網頁所得到的物件,我們的 soup 即是 BeautifulSoup 物件. 可以把它當作 Tag 物件,是一個特殊的 Tag,我們可以分別獲取它的型別,名稱,以及屬性
- Comment: 物件是網頁中的註釋及特殊字串,當你提取網頁中的註釋的時候,它會自動幫你生成 Comment 物件
- Tag: HTML 中的一個個標籤,例如:<title>, <head>, <div>, <link>, <a>…,Tag 下擁有許多屬性和方法,和前端類似,例如 a 標籤一定會有它的 href 屬性,某些屬性是某些標籤所獨有的。
- BeautifulSoup的方法44
方法 說明 select() 以 CSS 選擇器的方式尋找指定的 tag。 find_all() 以所在的 tag 位置,尋找內容裡所有指定的 tag。 find() 以所在的 tag 位置,尋找第一個找到的 tag。 find_parents() 以所在的 tag 位置,尋找父層所有指定的 tag 或第一個找到的 tag。 find_parent() find_next_siblings() 以所在的 tag 位置,尋找同一層後方所有指定的 tag 或第一個找到的 tag。 find_next_sibling() find_previous_siblings() 以所在的 tag 位置,尋找同一層前方所有指定的 tag 或第一個找到的 tag。 find_previous_sibling() find_all_next() 以所在的 tag 位置,尋找後方內容裡所有指定的 tag 或第一個找到的 tag。 find_next() find_all_previous() 所在的 tag 位置,尋找前方內容裡所有指定的 tag 或第一個找到的 tag。 find_previous() 下方的程式碼,使用 Beautiful Soup 取得範例網頁中指定 tag 的內容。
- 第四版:擷取所需資訊
import urllib.request as req url = "https://www.ptt.cc/bbs/Food/index.html" # 幫request加上一個header request = req.Request(url, headers = { "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0" }) with req.urlopen(request) as response: data = response.read().decode("utf-8") import bs4 root = bs4.BeautifulSoup(data,"html.parser") #找到所有class為title的div titles = root.find_all("div", class_="title") for t in titles: print(t.a)
<a href="/bbs/Food/M.1678360881.A.B45.html">[食記] 桃園聚餐餐廳 霸氣帶骨肉早午餐 甜福號</a> <a href="/bbs/Food/M.1678363764.A.1B5.html">[食記] 桃園蘆竹區。日日春土雞城</a> <a href="/bbs/Food/M.1678363771.A.C75.html">[食記] 金牌咖哩炒麵 基隆暖暖 香辣鍋氣小卷炒麵</a> <a href="/bbs/Food/M.1678366722.A.79F.html">[食記] 宜蘭壯圍 大仁哥蔗香脆皮桶仔雞旗艦店</a> <a href="/bbs/Food/M.1678368325.A.03E.html">[食記] 新竹 Luau pizza~爆紅網美餐廳超美炮仗花</a> <a href="/bbs/Food/M.1678369306.A.359.html">[食記] 中壢 江家羊肉 二訪</a> <a href="/bbs/Food/M.1355673582.A.5F7.html">[公告] Food板 板規 V3.91</a> <a href="/bbs/Food/M.1190944426.A.E6C.html">[公告] 發文請在標題加上地區及提供地址電話。^^</a> <a href="/bbs/Food/M.1128132666.A.0FD.html">[公告] 文章被刪除者請洽精華區的資源回收桶</a> <a href="/bbs/Food/M.1496532469.A.C36.html">[公告] 新增板規22:發文禁附延伸閱讀連結</a>
- BeautifulSoup4 簡介
- 實作2
import urllib.request as req url = "https://www.ptt.cc/bbs/Tainan/index.html" # 幫 request 加上一個 header request = req.Request(url, headers = { "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0" }) with req.urlopen(request) as response: data = response.read().decode("utf-8") import bs4 root = bs4.BeautifulSoup(data,"html.parser") #print(root.prettify()) titles = root.find_all("div", class_="title") print(titles[3].a['href']) print(titles[3].text) #pubs = root.find_all("li", class_="publish") #for pub, title in zip(pubs[:5], titles[:5]): # print(pub.span.string,title.a.string)
/bbs/Tainan/M.1678370734.A.97D.html [交易] 永康 深呼吸 健身房 會籍
9.6. 實作練習
- Google 美食達人「壽司羊」,以爬蟲程式列出其部落格(在痞客邦)最近五篇文章的標題以及文章月份。
Jun 【高雄食記】初一車輪燒紅豆餅/只要 25 元就可以吃到一大塊的巧克力戚風蛋糕做成的雙重巧克力車輪餅,會牽絲的起司蛋也很好吃((附菜單 Jun 【高雄美食】長代堂食品行/好吃的長崎蜂蜜蛋糕,底下那一層才是配上無糖紅茶的隱藏版超級美味甜點,千萬不要丟掉了!!! Jun 【冷凍宅配】蝦大俠室內有機養殖 白蝦 草蝦/不出門簡單煮,在家就可以吃到美味的蝦子,不管是烤蝦,水煮蝦都很方便快速。 Jun 【台北美食】地中海牛排館 歐華酒店/超高 CP 值的用餐優惠方案,濕式熟成 50 天肋眼牛排+1000 元送高級雙人房住宿一晚((附菜單 Jun 【高雄食記】玖雞炸物/堅持使用新鮮雞肉,每天到鳳農市場採購,吃得到雞肉鮮味鹽酥雞小店,墨魚黑輪也很好吃,脫油機不油膩((附菜單
10. GUI/Web-based
10.1. Gradio
讓只有command line I/O的python程式搖身一變成為web service。
- 安裝套件
1: pip install gradio - Hello world45
- fn :被 UI 裝飾的函式
- inputs :輸入元件。如 “text” 、 “image” 、 “audio” 等
- outputs :輸出元件。如 “text” 、 “image” 、 “label” 等
Gradio 支援 20 多種不同的元件型別,其中大部分都可以作為輸入/輸出元件,詳見官網文件gradio Docs
import gradio as gr def greet(name): return "Hello " + name + "!!" demo = gr.Interface(fn=greet, inputs="text", outputs="text") demo.launch()
- fn :被 UI 裝飾的函式
- 數字 I/O
import gradio as gr def BMI(h, w): h /= 100 return f'BMI值: {w/(h*h)}' ui = gr.Interface( fn=BMI, inputs=[gr.Slider(100, 240, label="身高(cm)"), gr.Slider(40, 200, label="體重(kg)")], outputs=["text"] ) ui.launch(share=True)
- 文字 I/O
import gradio as gr def greet(name): return "Hello " + name + "!" demo = gr.Interface( fn=greet, inputs=gr.Textbox(lines=2, placeholder="Name Here..."), outputs="text", ) demo.launch()
- 多資料 I/O
import gradio as gr def greet(name, is_morning, temperature): salutation = "Good morning" if is_morning else "Good evening" greeting = f"{salutation} {name}. It is {temperature} degrees today" celsius = (temperature - 32) * 5 / 9 return greeting, round(celsius, 2) demo = gr.Interface( fn=greet, inputs=["text", "checkbox", gr.Slider(0, 100)], outputs=["text", "number"], ) demo.launch()
- 圖形 I/O
import gradio as gr import matplotlib.pyplot as plt import numpy as np def curve(a, b, c): x = np.arange(-3, 3, 0.3) y = a*x**2 + b*x + c fig = plt.figure() plt.plot(x, y) print(x) print(y) return fig inputs = [gr.Slider(0, 10, 5), gr.Slider(0, 10, 5), gr.Slider(0, 10, 5)] outputs = gr.Plot() demo = gr.Interface( fn=curve, inputs=inputs, outputs=outputs, cache_examples=True,) demo.launch()
- Button
import gradio as gr def greet(name): return "Hello " + name + "!" with gr.Blocks() as demo: name = gr.Textbox(label="Name") output = gr.Textbox(label="Output Box") greet_btn = gr.Button("Greet") greet_btn.click(fn=greet, inputs=name, outputs=output) demo.launch()
- Radio
import gradio as gr def greet(gender, name): if gender == "Male": return "Hello, Mr. " + name + "!" else: return "Hello, Ms. " + name + "!" with gr.Blocks() as demo: input = [gr.Radio(["Male", "Female"]), gr.Textbox(label="Name)] output = gr.Textbox(label="Output Box") greet_btn = gr.Button("Greet") greet_btn.click(fn=greet, inputs=input, outputs=output) demo.launch()
10.2. Flask
- Button
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): title = "<title>Advanced Materials of Python</title>" h1 = "<h1>Python based web</h1>" p1 = "<p>這是用Python開發的網站</p>" return title+h1+p1 if __name__ == "__main__": app.run()

Figure 42: Flask Web Site
- Input form46
.gif)
Figure 43: 標題
- Python
# Flask網站前後端互動 09 - 超連結與圖片 # 載入Flask、Request、render_template from flask import Flask, request, render_template # 建立 Application 物件,設定靜態檔案的路徑處理 # http://127.0.0.1:5000/head.png 為圖片路徑 app = Flask(__name__, static_folder="public", static_url_path="/") # 處理路徑 / 的對應函市 @app.route("/") def main(): return render_template("main.html") @app.route("/page") def page(): #從main.html頁面中讀取姓名,存在name中 name = request.args.get("nameis") #將name的資料轉給pate.html中的變數namepage return render_template("page.html", namepage=name) # 啟動Server app.run()
- templates
- 建立templates資料夾
- 以下兩個html檔要放在templates資料夾中
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>這是標題</title> </head> <body> <h3>網頁的主畫面</h3> <form action="/page"> 名字:<input type="text" name="nameis"> <button>點擊送出</button> </form> </body> </html>
- page.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>page1</title> </head> <body> <h1>我的名字叫 {{namepage}}</h1> </body> </html>
- 建立templates資料夾
- Python
10.3. tkinter
- 安裝
- DEMO #1
# -*- coding: utf-8 -*- import tkinter as tk from tkinter import messagebox # 按完button要做什麼 def test(): tk.messagebox.showinfo('測試', 'Hi') # main window root = tk.Tk() root.title('my window') root.geometry('200x150') root.configure(background='white') myentry = tk.Entry(root) myentry.pack() # Button myButton = tk.Button(root, text='Button', command=test) myButton.pack() # Label resultLabel = tk.Label(root, text='這是Label') resultLabel.pack() root.mainloop()
- DEMO #2
# -*- coding: utf-8 -*- import tkinter as tk from tkinter import messagebox def button_event(): #print(var.get()) if var.get() == '': tk.messagebox.showerror('message', '未輸入答案') elif var.get() == '2': tk.messagebox.showinfo('message', '答對了!') else: tk.messagebox.showerror('message', '答錯') root = tk.Tk() root.title('my window') # label mylabel = tk.Label(root, text='1+1=') mylabel.grid(row=0, column=0) var = tk.StringVar() myentry = tk.Entry(root, textvariable=var) myentry.grid(row=0, column=1) mybutton = tk.Button(root, text='完成', command=button_event) mybutton.grid(row=1, column=1) root.mainloop()
10.4. PySimpleGUI
11. TODO Multiprocessing
11.1. Futher Reading

Figure 44: Multiprocessing v.s. Multithreading





