---
# System prepended metadata

title: Python程式設計入門-課堂筆記

---

# Python程式設計入門-課堂筆記 
10/17-11/14上課內容和問chatGPT得到的內容整理

10/17 科學計算
---
處理各種數學和科學任務，從數據分析到模擬和視覺化

1.數據分析：

使用Pandas庫處理和分析數據：
```python=
import pandas as pd

# 讀取數據文件
data = pd.read_csv('data.csv')

# 簡單統計摘要
print(data.describe())

# 選擇特定列
print(data['column_name'])

# 繪製直方圖
data['column_name'].hist()
```

2.線性代數：

使用NumPy執行矩陣運算

```python=
import numpy as np

# 創建矩陣
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# 矩陣相加
C = A + B

# 矩陣乘法
D = np.dot(A, B)

print(C)
print(D)
```
3.科學計算：

使用SciPy解決科學計算問題，例如求解微分方程

```python=
from scipy.integrate import odeint

# 定義微分方程
def model(y, t):
    return -2 * y

# 初始條件
y0 = 1

# 求解微分方程
t = np.linspace(0, 5, 100)
y = odeint(model, y0, t)

print(y)
```
4.視覺化：

使用Matplotlib繪製圖表
```python=
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)

plt.plot(x, y)
plt.xlabel('X軸')
plt.ylabel('Y軸')
plt.title('正弦波')
plt.show()
```

例子：計算複利
```python=
# 輸入資訊
P = 1000  # 假設本金為1000元
r = 0.03  # 年利率為3%
n = 365   # 每天複利一次
t = 1     # 1年

# 計算每日利息
daily_interest = P * (r/n)

# 計算本利和
A = P
for _ in range(n*t):
    A += daily_interest
    A = round(A)  # 每天結束後四捨五入到整數位

# 輸出結果
print("一年後的本利和為:", A)
```
有無四捨五入對複利的影響
```python=
# 輸入資訊
P = 1000  # 假設本金為1000元
r = 0.03  # 年利率為3%
n = 365   # 每天複利一次
t = 1     # 1年

# 計算每日利息和本利和（有四捨五入）
A_with_rounding = P
for _ in range(n*t):
    daily_interest = A_with_rounding * (r/n)
    A_with_rounding += daily_interest
    A_with_rounding = round(A_with_rounding)

# 計算每日利息和本利和（無四捨五入）
A_without_rounding = P
for _ in range(n*t):
    daily_interest = A_without_rounding * (r/n)
    A_without_rounding += daily_interest

# 輸出結果
print("有四捨五入的本利和為:", A_with_rounding)
print("無四捨五入的本利和為:", A_without_rounding)

# 比較兩者差異
difference = A_with_rounding - A_without_rounding
print("有四捨五入與無四捨五入的差異為:", difference)
```
計算本金對複利的影響
```python=
import matplotlib.pyplot as plt

# 計算每個本金 P 對應的差異
P_values = list(range(1000, 100001, 1000))
differences = []

for P in P_values:
    A_with_rounding = P
    A_without_rounding = P
    for _ in range(n*t):
        daily_interest = A_with_rounding * (r/n)
        A_with_rounding += daily_interest
        A_with_rounding = round(A_with_rounding)

        daily_interest_without_rounding = A_without_rounding * (r/n)
        A_without_rounding += daily_interest_without_rounding

    difference = A_with_rounding - A_without_rounding
    differences.append(difference)

# 繪製圖表
plt.figure(figsize=(10, 6))
plt.plot(P_values, differences)
plt.xlabel("本金 P")
plt.ylabel("四捨五入和無四捨五入的差異")
plt.title("本金 P 對差異的影響")
plt.grid(True)
plt.show()
```
![截圖 2023-11-20 下午9.46.50](https://hackmd.io/_uploads/rJCIQyFET.png)



---


10/24 網路爬蟲
---
先了解網頁HTML結構
```python=
<!DOCTYPE html>
<html>
<head>
    <title>網頁標題</title>
</head>
<body>
    <h1>歡迎來到我的網站</h1>
    <p>這是一個示例網頁。</p>
    <a href="https://www.example.com">訪問示例網站</a>
</body>
</html>
```
網路爬蟲第一步：向網頁請求HTML內容
```python=
import requests

# 目標網頁的URL
url = "https://www.example.com"

# 發送GET請求
response = requests.get(url)

# 檢查是否成功取得響應
if response.status_code == 200:
    # 獲取網頁的HTML內容
    html_content = response.text
    # 在這裡你可以開始解析HTML，提取所需的資訊
else:
    print("無法取得網頁，狀態碼：", response.status_code)
```
例如：提取新聞資訊
```python=
import requests
from bs4 import BeautifulSoup

URL = 'https://www.bbc.com/news/world'

# 向網頁請求HTML
response = requests.get(URL)  # 注意變數名稱的大小寫

# 檢查是否成功取得響應
if response.status_code == 200:
    # 獲取網頁的HTML內容
    html_content = response.text
    # 在這裡你可以開始解析HTML，提取所需的資訊
    soup = BeautifulSoup(html_content, "html.parser")

    # 使用正確的CSS選擇器來找到新聞標題
    news_titles = soup.find_all("h3", {"class": "gs-c-promo-heading__title"})

    for news_title in news_titles:
        # 印出新聞標題文本
        print(news_title.text)
else:
    print("無法取得網頁，狀態碼：", response.status_code)
```
提取成大網頁特定內容
```python=
import requests
from bs4 import BeautifulSoup

# 目標網址
URL = 'https://www.ncku.edu.tw/'

# 向網頁請求HTML
response = requests.get(URL)

# 檢查是否成功取得響應
# 一個網頁伺服器成功處理一個請求時，它會返回一個狀態碼，通常是一個三位數的數字，用來表示請求的結果。200 是 HTTP 狀態碼中代表成功的代碼。
if response.status_code == 200:

    # 獲取網頁的HTML內容
    html_content = response.text

    # 在這裡你可以開始解析HTML，提取所需的資訊
    # html_content 是包含網頁 HTML 內容的字串，html.parser" 是解析器的選項，它告訴 BeautifulSoup 使用 HTML 解析器來解析 html_content。
    soup = BeautifulSoup(html_content, "html.parser")

    # 先大範圍搜索，這個部分包含整個網頁中的成大快訊全部訊息
    informations = soup.find_all('div', class_='mouter')

    # 針對成大快訊裡面的訊息去找每篇內容的標題
    for information in informations:
      title = information.find_all('h2', class_='mt-title')
      # 如果這個標題是成大快訊，代表這部分的內容是我們要找的
      # title[0]代表我們要得到第一個匹配元素的文本
      if title and title[0].text == '成大快訊':
          # 所以我們可以更進一步找我們要的內容
          contents = information.find_all('div', class_='d-item v-it col-sm-3')
          # 講每篇內容輸出，用strip使不會空格
          for content in contents:
            print(content.text.strip())
```


---

10/31 圖像處理
---
各種圖像處理
1.將兩張圖合併
```python=
def concatenate_images(image1, image2):    # 要把兩張圖, 左右合併在一起的函式
    total_width = image1.width + image2.width  # 因為要左右合併在一起, 所以圖片的寬度
    max_height = max(image1.height, image2.height) # 兩張圖可能不一樣高, 取比較高的圖片高度

    new_image = Image.new('RGB', (total_width, max_height)) 
    new_image.paste(image1, (0, 0)) # 貼第一張圖
    new_image.paste(image2, (image1.width, 0)) # 貼第二張圖

    return new_image
```
2.旋轉縮放
```python=
# 旋轉圖片 (角度)
rotated_image = image.rotate(45)  # 逆時針旋轉45度
display(rotated_image)

# 縮放圖片
resized_image = image.resize((200, 200))
display(resized_image)
```
3.調整亮度
```python=
from PIL import ImageEnhance

# 調整亮度
enhancer = ImageEnhance.Brightness(image)
brightened_image = enhancer.enhance(1.5)  # 1.0表示原始亮度
display(brightened_image)

# 調整對比度
enhancer = ImageEnhance.Contrast(image)
high_contrast_image = enhancer.enhance(2.0)  # 1.0表示原始對比度
display(high_contrast_image)

```
4.變灰色
```python=
gray_image = image.convert("L") 

combined_image = concatenate_images(image, gray_image)
plt.imshow(combined_image)
plt.axis('off')  # 不顯示座標軸
plt.show()
#
```
5.使用濾鏡
```python=
# 使用模糊滤镜进行模糊处理
from PIL import ImageFilter # 一開始不能跑, 會有imagefilter的name error, 所以我才加上去的

blurred_image = image.filter(ImageFilter.GaussianBlur(radius=100))

combined_image = concatenate_images(image, blurred_image)
plt.imshow(combined_image)
plt.axis('off')  # 不顯示座標軸
plt.show()
```
6.特殊處理
```python=
# 获取图片的宽度和高度
width, height = image.size

# 创建一个新的空白图片，与原始图片大小相同
negative_image = Image.new("RGB", (width, height))

# 遍历每个像素并取反RGB值
for x in range(width):
    for y in range(height):
        pixel = image.getpixel((x, y))  # 得到像素的值 tuple (R,G,B)
        inverted_pixel = tuple(255 - value for value in pixel)
        # 用for-loop 產生tuple中的每一個值
        negative_image.putpixel((x, y), inverted_pixel) #把像素的(RGB)放回圖片中


combined_image = concatenate_images(image, negative_image)
plt.imshow(combined_image)
plt.axis('off')  # 不顯示座標軸
plt.show()
```

---


11/7 遊戲設計
---
我自己在預習時利用chatGPT寫的圈圈叉叉小遊戲
```python=
# 用list來幫九宮格儲存遊戲資料，list裡面的資料是有順序的，所以用for迴圈代表第0到8個數據
board = [' ' for _ in range(9)]

# 定義一個函數來顯示九宮格圈圈叉叉遊戲情況，board[數字]代表這個list裡面第幾個數據
def display_board(board):
    
    # 按照九宮格形式print出來
    print(board[0],board[1],board[2])
    print(board[3],board[4],board[5])
    print(board[6],board[7],board[8])

# 定義另一個函數來檢查遊戲勝利條件
def check_game(board):
    
    # 用一個list存放勝利組合，勝利組合是一個元組()，裡面的三個數字分別代表的是可以連成一條線的三個位置
    win_combinations = [(0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)]
    
    # 用for迴圈將borad去做每個勝利組合位置的判斷，意思就是第一個迴圈用(0,1,2)這三個位置去判斷有沒有成一條線，再來用(3,4,5)這三個位置去判斷，以此回圈
    for combination in win_combinations:
        
        # 0,1,2分別代表勝利組合()裡的第0,1,2個數字，如果這三個位置都相同，且不等於空格' '，代表連成一線
        if board[combination[0]] == board[combination[1]] == board[combination[2]] != ' ':
            
            # now 是一個變數，代表現在的玩家是誰，所以如果連成一線勝利，就return勝利玩家
            return now
    
    # 如果board上沒有空格，但還沒有判斷出誰勝利，代表平手    
    if ' ' not in board:
        return 'tie'
    
    else:
        return False

# 第一個玩家是User1
now = 'User1'

# 主遊戲回圈
while True:
  
  # 如果變數now等於User1時，換User1玩
  if now == 'User1':
      # 輸入position，減一是因為資料是從第0個開始，但位置是從1開始，所以輸入的數字要-1才能匹配到對的資料位置
      print('User one turn to input now')
      position = eval(input('Select input position 1~9 =' )) -1
      
      # 防呆，如果輸入的值不在對應九宮格內，還有把輸入的position放到對應九宮格內發現這個位置不是空格，代表已經被輸入過了
      if position<0 or position>8 or board[position] != ' ':
          print('User one turn to input now')
          print('該位置已經被輸入，請重新輸入')
          position = eval(input('Select input position 1~9 =' ))-1
      # 輸入position到九宮格內，就是把對應的資料從空個' '改成'o'
      board[position] = 'o'
      # 然後把遊戲情況顯示出來
      display_board(board)
      # 檢查勝利條件
      result = check_game(board)
      # result = now 代表有人贏了
      if result == now:
        print(now,'win')
        # game over要記得把回圈break
        break
      # result = tie 代表平手
      elif result == 'tie':
        print('Tie')
        break    
      # 最後要切換玩家，把now變數改為User2
      now = 'User2'
  # 換User2玩
  else:
    # 以下和User2的code一樣
    print('User two turn to input now')  
    position = eval(input('Select input position 1~9 =' )) -1
    if position<0 or position>8 or board[position] != ' ':
      print('該位置已經被輸入，請重新輸入')
      print('User two turn to input now')
      position = eval(input('Select input position 1~9 =' ))-1
    board[position] = 'x'
    display_board(board)
    if result == now:
      print(now,'win')
      break
    elif result == 'tie':
      print('Tie')
      break    
    now = 'User1'
```
可改進的地方：
可以新增棋盤和把初始值設為該位置的數字，再遊戲進行時再把該位置的變數改為o或x，來讓遊戲更完整

---

11/14 機器學習
---
利用data去建構model，來讓機器能利用這個model來辨識特徵

例子：Tensor_Flow_PlayGround
這個範例是要做一個類似tensor flow playground的類神經網路分類器。

其中我們產生兩個分類

與原點距離R0到R1，Positive (藍色點)
與原點距離是R2到R3的，Negative (橘色點)
每一類各產生 𝑁 個點供訓練用， 也各產生 𝑁𝑝 個點以標記出確定Positive的範圍

 **生成數據點**
```python=
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from sklearn.model_selection import train_test_split

N = 200  # 原始點的數量
Np = 1200  # 額外點的數量

#
def generate_circle_points(r_min, r_max, num_points):
    angles = np.random.uniform(0, 2 * np.pi, num_points)
    radii = np.sqrt(np.random.uniform(r_min**2, r_max**2, num_points)) # 為什麼要這樣才會均勻, 請問數學老師
    x = radii * np.cos(angles)
    y = radii * np.sin(angles)
    return x, y

# 生成數據點
R0 = 0
R1 = 2
R2 = 1
R3 = 3
x_o, y_o = generate_circle_points(R0, R1, N)
x_x, y_x = generate_circle_points(R2, R3, N)

# 繪製散點圖
plt.figure(figsize=(8, 8))
plt.scatter(x_o, y_o, c='blue', marker='o', label='Positive O')
plt.scatter(x_x, y_x, c='orange', marker='o', label='Negative O')

plt.xlabel('X')
plt.ylabel('Y')
plt.title('Scatter Plot with Different Classes')
plt.legend()
plt.grid(True)
plt.axis('equal')
plt.show()
```
![截圖 2023-11-21 上午3.02.40](https://hackmd.io/_uploads/Sk4D6QK4T.png)

**Training function**
```python=
import matplotlib.pyplot as plt
import numpy as np

def plot_predictions(X_train, y_train, X_test, y_test, model):
    # 使用模型對訓練集進行預測
    y_pred_train_continuous = model.predict(X_train)
    y_pred_train = np.where(y_pred_train_continuous.flatten() >= 0, 1, -1)

    # 使用模型對測試集進行預測
    y_pred_test_continuous = model.predict(X_test)
    y_pred_test = np.where(y_pred_test_continuous.flatten() >= 0, 1, -1)
    plt.figure(figsize=(8, 8))

    # 訓練集 - 預測正確
    correct_train = (y_train == y_pred_train)
    oc = (y_train == 1) & correct_train
    xc = (y_train == -1) & correct_train
    plt.scatter(X_train[oc, 0], X_train[oc, 1], c='blue', marker='o', label='Train Correct (Positive)')
    plt.scatter(X_train[xc, 0], X_train[xc, 1], c='orange', marker='o', label='Train Correct (Negative)')

    # 訓練集 - 預測錯誤
    incorrect_train = (y_train != y_pred_train)
    oi = (y_train == 1) & incorrect_train
    xi = (y_train == -1) & incorrect_train
    plt.scatter(X_train[oi, 0], X_train[oi, 1], c='blue', marker='x', label='Train Incorrect (Positive)')
    plt.scatter(X_train[xi, 0], X_train[xi, 1], c='orange', marker='x', label='Train Incorrect (Negative)')

    # 測試集 - 預測正確
    correct_test = (y_test == y_pred_test)
    oc = (y_test == 1) & correct_test
    xc = (y_test == -1) & correct_test
    plt.scatter(X_test[oc, 0], X_test[oc, 1], c='cyan', marker='o', label='Test Correct (Positive)')
    plt.scatter(X_test[xc, 0], X_test[xc, 1], c='pink', marker='o', label='Test Correct (Negative)')

    # 測試集 - 預測錯誤
    incorrect_test = (y_test != y_pred_test)
    oi = (y_test == 1) & incorrect_test
    xi = (y_test == -1) & incorrect_test
    plt.scatter(X_test[oi, 0], X_test[oi, 1], c='cyan', marker='x', label='Test Incorrect (Positive)')
    plt.scatter(X_test[xi, 0], X_test[xi, 1], c='pink', marker='x', label='Test Incorrect (Negative)')

    plt.xlabel('X Coordinate')
    plt.ylabel('Y Coordinate')
    plt.title('Model Predictions')
    plt.legend()
    plt.grid(True)
    plt.show()
```
**整理、分割數據，創建神經網絡model**
```python=
# 整合並標記數據點
X = np.vstack((np.column_stack((x_o, y_o)), np.column_stack((x_x, y_x))))
y = np.array([1] * N + [-1] * N)  # 1代表o，-1代表x

# 分割數據集, 把 N 中的 20% 做為測試
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 創建神經網路模型
N1 = 3
model = Sequential()
model.add(Dense(N1, input_dim=2, activation='tanh'))  # 第一層有N1個神經元，使用tanh激活函數
model.add(Dense(2, input_dim=2, activation='tanh'))  # 第二層有2個神經元
model.add(Dense(1, activation='tanh'))  # 第三層有1個神經元

# 編譯模型
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])
```
**Training並記錄loss function**
```python=
# 訓練模型並記錄訓練過程
history = model.fit(X_train, y_train, epochs=1000, batch_size=5, verbose=0, validation_data=(X_test, y_test))

# 獲取損失函數值
loss_values = history.history['loss']

# 繪製損失函數圖
plt.figure(figsize=(8, 4))
plt.plot(loss_values, label='Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Loss Function During Training')
plt.legend()
plt.grid(True)
plt.show()

plot_predictions(X_train, y_train, X_test, y_test, model)


# 獲取第一層的權重和偏置
weights, biases = model.layers[0].get_weights()

# 生成更多隨機點進行預測
x_a, y_a = generate_circle_points(R0, R3, Np)
additional_X = np.column_stack((x_a, y_a))
y_pred_additional = model.predict(additional_X)
y_pred_additional = np.where(y_pred_additional.flatten() >= 0, 1, -1)

# 繪製測試數據和預測結果
plt.figure(figsize=(8, 8))
plt.scatter(x_o, y_o, c='blue', marker='o', label='Class Positive')
plt.scatter(x_x, y_x, c='orange', marker='o', label='Class Negative')
plt.scatter(additional_X[y_pred_additional == 1, 0], additional_X[y_pred_additional == 1, 1], c='blue', marker='o', label='Predicted Positive')
plt.scatter(additional_X[y_pred_additional == -1, 0], additional_X[y_pred_additional == -1, 1], c='orange', marker='o', label='Predicted Negative')

# 繪製第一層的N1條直線
x_values = np.linspace(-2, 2, 100)
y_values = np.linspace(-2, 2, 100)
for i in range(N1):
    a, b = weights[:, i]
    c = biases[i]
    if abs(a) > abs(b):
        x_line = (-b * y_values - c) / a
        plt.plot(x_line, y_values, linestyle='--', label=f'Neuron {i+1} Boundary')
    else:
        y_line = (-a * x_values - c) / b
        plt.plot(x_values, y_line, linestyle='--', label=f'Neuron {i+1} Boundary')

plt.xlabel('X')
plt.ylabel('Y')
plt.title('Additional Points Classification with Neuron Boundaries')
plt.legend()
plt.grid(True)
plt.axis('equal')
plt.show()
```
![截圖 2023-11-21 上午3.07.22](https://hackmd.io/_uploads/rkStC7FEa.png)
![截圖 2023-11-21 上午3.08.16](https://hackmd.io/_uploads/r1d207tEp.png)
![截圖 2023-11-21 上午3.08.43](https://hackmd.io/_uploads/SJTa07FNp.png)
**計算準確率**
```python=
#loss, accuracy = model.evaluate(X_train, y_train)
#print('train loss: ', loss)
#print('train accuracy: ', accuracy)

y_pred_train_continuous = model.predict(X_train)
y_pred_train = np.where(y_pred_train_continuous.flatten() >= 0, 1, -1)

TP = sum((y_pred_train==1) & (y_train == 1))
TN = sum((y_pred_train==-1) & (y_train == -1))
FP = sum((y_pred_train==1) & (y_train == -1))
FN = sum((y_pred_train==-1) & (y_train == 1))
print("In training set:")
TPR = TP/(TP+FN)
TNR = TN/(TN+FP)
FPR = 1-TNR
FNR = 1-TPR
PPV = TP/(TP+FP)
NPV = TN/(TN+FN)
F1 = 2*PPV*TPR/(PPV+TPR)
ACC = (TP+TN)/(TP+TN+FP+FN)
print("TP=",TP,"TN=",TN,"FP=",FP,"FN=",FN)
print("Accuracy=",ACC)
print("TPR(Sensitivity, Recall)=",TPR,"TNR(Specificity)=",TNR)
```
![截圖 2023-11-21 上午3.10.24](https://hackmd.io/_uploads/ryi4k4YN6.png)

**實際情況**
```python=
# 模型预测为 1（类别 o）的点
o_predicted = additional_X[y_pred_additional == 1]

# 模型预测为 -1（类别 x）的点
x_predicted = additional_X[y_pred_additional == -1]

# 计算预测为类别 o 的点中，实际上也属于类别 o 的比例（x^2 + y^2 < 1）
o_correct = np.sum(np.sum(o_predicted**2, axis=1) < R1**2)
o_total = o_predicted.shape[0]
o_correct_ratio = o_correct / o_total if o_total > 0 else 0

# 计算预测为类别 o 的点中，实际上属于类别 x 的比例（x^2 + y^2 > 1）
o_incorrect = np.sum(np.sum(o_predicted**2, axis=1) > R2**2)
o_incorrect_ratio = o_incorrect / o_total if o_total > 0 else 0

# 计算预测为类别 x 的点中，实际上也属于类别 x 的比例（x^2 + y^2 > 2^2）
x_correct = np.sum(np.sum(x_predicted**2, axis=1) > R2**2)
x_total = x_predicted.shape[0]
x_correct_ratio = x_correct / x_total if x_total > 0 else 0

# 计算预测为类别 x 的点中，实际上属于类别 o 的比例（x^2 + y^2 < 2^2）
x_incorrect = np.sum(np.sum(x_predicted**2, axis=1) < R1**2)
x_incorrect_ratio = x_incorrect / x_total if x_total > 0 else 0

print("預測為 o 的點中，屬於類別 o 的比例: {:.4f}".format(o_correct_ratio))
print("預測為 o 的點中，屬於類別 x 的比例: {:.4f}".format(o_incorrect_ratio))
print("預測為 x 的點中，屬於類別 x 的比例: {:.4f}".format(x_correct_ratio))
print("預測為 x 的點中，屬於類別 o 的比例: {:.4f}".format(x_incorrect_ratio))
```
![截圖 2023-11-21 上午3.12.32](https://hackmd.io/_uploads/ry-2yEKV6.png)


