# 2024.5.12 上課講義
## 模組 Module & 套件 Package
[**Python函式庫**](https://docs.python.org/zh-tw/3/library/index.html)
### 模組 Module
一個`Python`檔案就類似於一個模組,當一次需要撰寫大量內容或長時間維護時,有時會將部分定義程式碼分檔案撰寫,而在主模組(main)中就可以透過`import`來引入模組。
>別名可以按照自己的習慣去命名,目的是撰寫程式碼時方便輸入
```python=
import datetime
import datetime as dt
from random import randint
```
:::warning
**檢視模組是否引入成功**
```python=
print( datetime.__file__ )
```
>file 前後為雙底線
:::
### 套件 Package
若一個模組形似一個檔案,則套件就類似於一個資料夾,包含許多模組,在主模組(main)中可以透過`from`來引入套件中的模組。
```python=
from matplotlib import pyplot
from matplotlib import pyplot as plt
```
:::warning
**import all 的寫法**
可以一次性引入所有在該套件下的模組
```
from 套件名稱 import *
```
:::
---
## Dates 模組
模組可以自行定義但`Python`也有很多已經撰寫好的模組可以引用,但在一般電腦內的直譯器內模組需另外下載,不過Colab不需要另外下載,需要注意的是在正規測驗、競賽中(如APCS)是無法適用的
### 引入模組
```python=
import datetime
```
### 顯示現在時間
```python=
x = datetime.datetime.now()
y = datetime.datetime.today()
```
1. 顯示的是**GMT標準時間**也就是英國倫敦格林威治天文臺當下的時間,並非主機所位在地點之當地時間
2. 回傳的資料包含年份 year、月份 month、日期 day、小時 hour、分鐘 minute、秒 second、微秒 microsecond
3. 可以透過`.+需要的資料`來進一步取得時間
>對應的英文:年份 year、月份 month、日期 day、小時 hour、分鐘 minute、秒 second、微秒 microsecond
```python=
print(x.year)
```
:::warning
**屬性 attributes vs. 函式 methods**
<Font color="#CE0000">**屬性**</Font>是一個**物件本身的特性**
而<Font color="#CE0000">**函式**</Font>則是**作用在該物件的動作**
:::
### 建立時間點
```python=
x = datetime.datetime(2024, 4, 12)
```
回傳:2024-04-12 00\:00\:00
>沒有給定的會預設為0
### 轉換當地時間
```python=
time_del = datetime.timedelta(hours = -8)
now = datetime.datetime.now()
loc = now - time_del
now_datetime_format = now.strftime("%Y/%m/%d %H:%M:%S")
loc_datetime_format = loc.strftime("%Y/%m/%d %H:%M:%S")
print(now_datetime_format, loc_datetime_format, sep='\n')
```
:::spoiler 符號
| 符號 | 意義 | 範例 |
|:----:|:-------------------------------:|:--------------------------:|
| %a | 星期幾的簡稱 | Mon, Tue... |
| %A | 星期幾 | Monday, Tuesday... |
| %w | 星期幾(0->星期日) | 0, 1, 2..., 6 |
| %d | 日期 | 01, 02, 03..., 31 |
| %b | 月份縮寫 | Jan, Feb... |
| %B | 月份 | January, February... |
| %m | 月份 | 01, 02..., 12 |
| %y | 年份(後2位) | 00, 01... |
| %Y | 年份 | 0001, 0002..., 2024... |
| %H | 小時(24小時制) | 00, 01..., 23 |
| %I | 小時(12小時制) | 00, 01..., 12 |
| %M | 分鐘 | 00, 01..., 59 |
| %S | 秒 | 00, 01..., 59 |
| %f | 微秒 | 000000, 000001..., 999999 |
| %U | 周序號(從0開始, 以周日為開始) | 00, 01..., 53 |
| %W | 周序號(從0開始, 以周一為開始) | 00, 01..., 53 |
| %c | 日期+時間 | Tue Apr 16 14\:00\:00 2024 |
| %x | 日期 | 04/16/24 |
| %X | 時間 | 14\:00\:00 |
[**其他符號**](https://docs.python.org/zh-tw/3/library/datetime.html#strftime-and-strptime-behavior)
:::
---
## Math 模組
`Python`本身就有內建的數學函式,`Math`模組算是一個擴充,有更多跟數學相關的函式
```python=
import math
```
### 取最近整數
>類似於[**高斯符號**](https://zh.wikipedia.org/zh-tw/%E5%8F%96%E6%95%B4%E5%87%BD%E6%95%B0) []
```python=
print(math.ceil(13.873096))
print(math.floor(13.873096))
```
1. ceil是取大於該數且最相近之整數
2. floor是取小於該數且最相近之整數
### π
```python=
print(math.pi)
```
[**其他函式**](https://www.w3schools.com/python/module_math.asp)
---
## 矩陣
### 定義
當一個 $m*n$ 的矩陣,有 $m$ 列、 $n$ 行
>直行橫列
### 零矩陣
可記為 $O_{m*n}$ ,且全部的數字皆為 $0$
$e.g.\ O_{1*3} = \left[
\begin{array}{ccc}
0 &&0 &&0 \\
\end{array}\right]$
$e.g.\ O_{2*4} = \left[
\begin{array}{cccc}
0 &&0 &&0 &&0 \\
0 &&0 &&0 &&0 \\
\end{array}\right]$
### 單位方陣
可記為 $I_n$ ,必為方陣且所有主對角線數字為 $1$ 其餘為 $0$
:::warning
任何矩陣乘以單位方陣皆不會改變原來的值
:::
$e.g.\ I_{2} = \left[
\begin{array}{cc}
1 &&0 \\
0 &&1 \\
\end{array}\right]$
$e.g.\ I_{3} = \left[
\begin{array}{ccc}
1 &&0 &&0 \\
0 &&1 &&0 \\
0 &&0 &&1 \\
\end{array}\right]$
### 矩陣加法、減法
同階矩陣才可相加減,運算時,相對應位置數字進行運算
$e.g.\ A = \left[
\begin{array}{ccc}
1 &&0 &&3 \\
5 &&6 &&0 \\
10 &&0 &&1 \\
\end{array}\right],
B = \left[
\begin{array}{ccc}
1 &&6 &&4 \\
0 &&1 &&-1 \\
7 &&2 &&1 \\
\end{array}\right]$
$A + B = \left[
\begin{array}{ccc}
1+1 &&0+6 &&3+4 \\
5+0 &&6+1 &&0-1 \\
10+7 &&0+2 &&1+1 \\
\end{array}\right]
=\left[
\begin{array}{ccc}
2 &&6 &&7 \\
5 &&7 &&-1 \\
17 &&2 &&2 \\
\end{array}\right]$
### 係數積
若將一個矩陣乘以k倍,則將該矩陣內每一個元素都乘以k倍
$e.g.\ A = \left[
\begin{array}{ccc}
a_{11} &&a_{12} &&a_{13} \\
a_{21} &&a_{22} &&a_{23} \\
a_{31} &&a_{32} &&a_{33} \\
\end{array}\right]$
$A * k = \left[
\begin{array}{ccc}
a_{11}*k &&a_{12}*k &&a_{13}*k \\
a_{21}*k &&a_{22}*k &&a_{23}*k \\
a_{31}*k &&a_{32}*k &&a_{33}*k \\
\end{array}\right]$
### 轉置矩陣
若將一個矩陣轉置,則將該矩陣內的所有行列互換
$e.g.\ A = \left[
\begin{array}{ccc}
1 &&0 &&3 \\
5 &&6 &&0 \\
\end{array}\right]$
$\ A^T = \left[
\begin{array}{cc}
1 &&5 \\
0 &&6 \\
3 &&0 \\
\end{array}\right]$
### 矩陣乘法
必須 **前一個矩陣的行數** 和 **後一個矩陣的列數** 相同才可進行乘法
>$A_{x*y} * B_{y*z} = C_{x*z}$
$e.g.\ A = \left[
\begin{array}{ccc}
a_{11} &&a_{12} &&a_{13} \\
a_{21} &&a_{22} &&a_{23} \\
\end{array}\right],
B = \left[
\begin{array}{cc}
b_{11} &&b_{12} \\
b_{21} &&b_{22} \\
b_{31} &&b_{32} \\
\end{array}\right]$
$A * B = \left[
\begin{array}{ccc}
a_{11}*b_{11}+a_{12}*b_{21}+a_{13}*b_{31} &&a_{11}*b_{12}+a_{12}*b_{22}+a_{13}*b_{32}\\
a_{21}*b_{11}+a_{22}*b_{21}+a_{23}*b_{31} &&a_{21}*b_{12}+a_{22}*b_{22}+a_{23}*b_{32} \\
\end{array}\right]$
### 反矩陣
A的反矩陣可記為 $A^{-1}$ ,2者相乘為 $1$
假設 $A = \left[
\begin{array}{cc}
a &&b \\
c &&d \\
\end{array}\right],det(A) = \begin{vmatrix}
a & b \\
c & d
\end{vmatrix}$
則$A^{-1} =
\cfrac{1}{det(A)} *\left[
\begin{array}{cc}
d &&-b \\
-c &&a \\
\end{array}\right]$
### 解方程式
$3x + 2y = 7$
$4x - 3y = -2$
A為係數矩陣、B為常數矩陣、X為未知數
$A = \left[
\begin{array}{cc}
3 &&2 \\
4 &&-3 \\
\end{array}\right],
B = \left[
\begin{array}{c}
7\\
-2\\
\end{array}\right],
X = \left[
\begin{array}{c}
x\\
y\\
\end{array}\right]$
原式
$A*X = B$
$\Rightarrow A*A^{-1}*X = B*A^{-1}$
$\Rightarrow X = B*A^{-1}$
---
## Numpy 官網
[**Numpy 官網**](https://numpy.org/)
### 為什麼需要 Numpy
Numpy 的架構中,有`ndarray`可以使用,在用法上跟`List`很像,但方便性、執行速度都比`List`快上許多
>ndarray 全名為 n-dimensional array 是一種多維陣列,為 Numpy 獨有的資料型態
### 建立 array
```python=
import numpy as np
```
- 利用`List`、`Tuple`轉換成`array`
```python=
A = np.array([1, 2, 3, 4, 5])
B = np.array((1, 2, 3, 4, 5))
```
:::warning
**ndarray**
其中的`nd`代表這個陣列的維度,可以使用`arr.ndim`來回傳該資料的維度
>其中不能有數字和列表同地位的狀況
ex. 0d
```python=
A = np.array(42)
print(A, type(A), A.ndim)
```
ex. 1d
```python=
A = np.array([42])
print(A, type(A), A.ndim)
```
ex. 2d
```python=
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A, type(A), A.ndim)
```
ex. 3d
```python=
A = np.array([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
print(A, type(A), A.ndim)
```
- `.size`回傳所有元素數量
- `.shape`以`Tuple`的形式返回每個axis的長度
```python=
A = np.array([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
print(A.size)
print(A.shape)
```
:::
- 建立初始化矩陣,利用`Tuple`來包裝想要的矩陣大小
- 利用`np.ones()`來創造全為 $1$ 的矩陣
- 利用`np.zeros()`來創造全為 $0$ 的矩陣
>記得有2個`()`
```python=
A = np.ones((2, 3))
B = np.zeros((1, 4))
print(A)
print(B)
```
- 建立單位矩陣
利用`.eye(n)`,來建立$n*n$的單位矩陣
```python=
A = np.eye(4)
print(A)
```
### indexing
有2種方式,1種和之前一樣,另1種和之前的`List`,有些許不同,利用`[]`依序輸入各維度index,並用`,`隔開
```
[[1, 2, 3],
[4, 5, 6]]
```
以取出第一列第二個數字來示範,
**List**
```python=
A = [[1, 2, 3], [4, 5, 6]]
print(A[0][1])
```
**Array**
```python=
nd_A = np.array(A)
print(nd_A[0, 1])
print(nd_A[0][1])
```
### slicing
和之前的`List`相同,利用`[]`依序輸入開頭及結尾的index並用`:`隔開
```
[start:end:step]
```
>end一樣不包含
```python=
A = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
print(A[0, 1:3])
```
### 加減法
相同大小的2矩陣可利用`+`、`-`來進行各位置的加減法
```python=
A = np.array([[1, 2, 3], [4, 5, 6]])
B = np.array([[8, 9, 1], [-1, 6, 10]])
print(A+B)
print(A-B)
```
### 重塑 reshape
使用`arr.reshape(a, b, c...)`將原本的矩陣重新組合成指定大小的多維矩陣
>a, b, c ... 為各維度中指定個數
:::warning
注意矩陣內的元素**總數**需相同,否則會報錯
>使用 -1 可以讓電腦自行運算
:::
```python=
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
arr = arr.reshape(2, 4)
print(arr)
print(arr.shape)
```
### 疊加 stacking
分別利用`np.vstack((arr1, arr2))`、`np.hstack((arr1, arr2))`將兩 <font color="#CE0000">**Tuple、List、ndarray**</font> 依 **鉛直軸vertical axis 、 水平軸horizontal axis** 疊加,並一律用ndarray回傳值
:::warning
若使用`np.vstack`,則兩陣列**行數**需相同
若使用`np.hstack`,則兩陣列**列數**需相同
**arr1和arr2在傳入時需事先包裝成一個Tuple**
:::
```python=
# vstack 行數相同
arr1 = [[1, 2],
[3, 4],
[5, 6]]
arr2 = [[-1, 2],
[3, 4]]
np.vstack((arr1, arr2))
```
```python=
# hstack 列數相同
arr1 = [[1, 2],
[3, 4],
[5, 6]]
arr2 = [[-1, 2, 4],
[3, 4, 2],
[6, 8, 9]]
np.hstack((arr1, arr2))
```
### 轉置 transpose
$A$ 轉置後的矩陣記做 $A^T$,在程式中利用`arr.T`回傳轉置後的陣列
```python=
arr = np.array([[2, 3, 10], [4, -1, 3]])
print(arr)
print(arr.T)
```
### 迭代函數 arange
利用`np.arange(a, b, c)`可以直接創造出一個符合迭代函數的List
>a 是起點,b 是終點(不包含),c 是間隔
```python=
a = np.arange(10)
print(a)
```
### 線性間距向量 linspace
利用`np.linspace(a, b, c)`,在 a 和 b 之間取出平均的 c 個點
>範圍包含 a、b
```python=
a = np.linspace(1, 7, 3)
print(a)
```
### 矩陣乘法
程式上利用`np.dot(arr1, arr2)`或是`arr1.dot(arr2)`回傳相乘過後的矩陣
>`np.dot(arr1, arr2)`也適用於List
```python=
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[-1, 2], [6, 10]])
print(arr1.dot(arr2))
print(np.dot(arr1, arr2))
```
### 解方程式
:::info
**練習** [**網站**](https://numpy.org/doc/stable/reference/routines.linalg.html)
提示:`linalg`、不利用反矩陣
寫出一個能解出這個二元一次方程式的程式
> $3x+6y=-9$
> $-x-7y=13$
```python=
A = np.array([[3, 6], [-1, -7]])
B = np.array([-9, 13])
print(np.linalg.solve(A, B))
```
提示:`linalg`、利用反矩陣
> $3x+6y=-9$
> $-x-7y=13$
$A*X = B$
$\Rightarrow A*A^{-1}*X = B*A^{-1}$
$\Rightarrow X = B*A^{-1}$
寫出一個能解出這個二元一次方程式的程式
```python=
A = np.array([[3, 6], [-1, -7]])
B = np.array([-9, 13])
# inverse
A_inv = np.linalg.inv(A)
print(np.dot(A_inv, B))
```
:::
## Random
[**Random 官網**](https://docs.python.org/zh-tw/3/library/random.html)
```python=
import random
```
### randint
利用`random.randint(a, b)`隨機產生一個 a ~ b 間的整數
>有包含 b
```python=
random.randint(1, 10)
```
### random
利用`random.random()`隨機產生一個 0 ~ 1 間的實數
>有包含 0 不包含 1
```python=
random.random()
```
### randrange
利用`random.randrange(a, b, c)`以 c 為間隔,隨機產生一個 a ~ b 間的整數
>相當於在`np.arange(a, b, c)`中,隨機取一個數字
```python=
random.randrange(1, 100, 10)
```
### choice
從 A 中隨機抽取一個元素
>A 可以是 String、List、Tuple
```python=
L = [1, 23, 3, 4, 2, 7, 18, 29, 8, 72]
random.choice(L)
```
從 A 中隨機抽取 n 個元素(可能重複)
```python=
L = [1, 23, 3, 4, 2, 7, 18, 29, 8, 72]
random.choices(L, k=3)
```
### sample
從 A 中隨機抽取 n 個元素(不重複)
```python=
L = [1, 23, 3, 4, 2, 7, 18, 29, 8, 72]
random.sample(L, k=6)
```
### shuffle
將 A 中原本的元素順序打亂
```python=
L = [1, 23, 3, 4, 2, 7, 18, 29, 8, 72]
random.shuffle(L)
print(L)
```
:::info
**練習**
請試著用`random`寫出一個終極密碼(1~100)的程式
```python=
import random
seed = random.randint(1, 100)
game = True
ceil = 100
floor = 1
c = 0
while game:
ans = int(input(f"請在{floor}~{ceil}之間猜一個數字"))
if ans > seed:
print("太大囉\n")
ceil = ans
c += 1
continue
elif ans < seed:
print("太小囉\n")
floor = ans
c += 1
continue
else: # ans = seed
c += 1
break
print(f"恭喜遊戲結束,您猜對的正確答案為{seed},一共猜了{c}次")
```
:::
## Matplotlib
[**官方資料**](https://matplotlib.org/stable/index.html)
> `Matplotlib` 為套件,這次我們會教的是裡面的 `pyplot` 這個模組
```python=
import numpy as np
import matplotlib.pyplot as plt
```
:::warning
**補充**
一般在 `Matplotlib.pyplot` 中,所顯示的字型皆為英文,如果要顯示中文需要加上一些步驟
>大部分時候用英文比較保險
1. 取得字型
[**Mac的下載方式**](https://support.apple.com/zh-tw/guide/font-book/fntbk1000/mac)
檔案總管>本機>Windows>Fonts
然後右鍵複製,貼到其他地方(像是桌面),上傳到自己的雲端硬碟
2. 掛接雲端硬碟
方法一:

點左手邊,有一個檔案的符號

點下去之後,按上方列表的第三項 — 掛接雲端硬碟 mount drive

跳出頁面,按藍色的—同意

多出現一個 Drive 資料夾就是掛接成功

>*如果沒有出現可以點第二個的重整 refresh*
>
方法二:
直接執行下方程式
```python=
from google.colab import drive
drive.mount('/content/drive')
```
執行成功即為掛接成功

3. 取得路徑

一樣點左手邊,有一個檔案的符號

點開 Drive > MyDrive 資料夾

找到自己的字型檔案,右鍵複製檔案路徑 coby path

檔案路徑就會自動處存到你的剪貼簿
4. 載入字型
```python=
# 繪圖處理套件
import matplotlib.font_manager as plt_font
# 設定中文字體物件和字型檔案路徑
twfont1 = plt_font.FontProperties(fname = 'file path')
```
<font color="#CE0000">**file path的地方要記得填入自己的檔案路徑**</font>
:::