本日主題:用 Python 處理 Excel/CSV 資料 預計時間:2 小時(動手日) 對應主教材:第 6.3 節
一、今日學習目標
- [ ] 安裝 Python 與 pandas
- [ ] 了解 Python 虛擬環境(venv)
- [ ] 會用 pandas 讀寫 Excel/CSV
- [ ] 會做基本的彙總(groupby、pivot_table)
- [ ] 會用 merge 合併資料、fillna 處理缺值、apply 自訂運算
- [ ] 認識 os/shutil 基本檔案操作
二、教材內容
2.1 安裝
- 到 python.org 安裝 Python(勾選 Add to PATH)。
- 安裝 pandas 與 Excel 支援:
pip install pandas openpyxl
2.2 Python 虛擬環境(venv)
為什麼需要虛擬環境?
不同專案可能用到不同版本的套件。虛擬環境讓每個專案有自己獨立的套件空間,不會互相干擾。
建立與使用
# 建立虛擬環境(在專案資料夾內執行)
python -m venv myenv
# 啟動虛擬環境
# Windows:
myenv\Scripts\activate
# macOS/Linux:
source myenv/bin/activate
# 啟動後,終端機會出現 (myenv) 前綴
# 安裝套件(只裝在這個虛擬環境裡)
pip install pandas openpyxl
# 匯出套件清單(方便別人重建環境)
pip freeze > requirements.txt
# 別人要重建環境時
pip install -r requirements.txt
# 離開虛擬環境
deactivate
面試加分小知識:提到你會用 venv 管理專案環境,代表你有「工程化」的觀念,而不只是會寫腳本。
2.3 Python 最基礎語法(夠用就好)
# 變數與印出
name = "王小明"
print("你好", name)
# 字串格式化(f-string,Python 3.6+)
age = 25
print(f"{name} 今年 {age} 歲")
# 清單(list)
fruits = ["蘋果", "香蕉", "橘子"]
fruits.append("葡萄")
print(len(fruits)) # 4
# 字典(dict)
server = {"name": "DC01", "ip": "192.168.1.10", "role": "AD"}
print(server["ip"])
# 迴圈
for i in range(3):
print(i) # 0 1 2
for fruit in fruits:
print(fruit)
# 條件
salary = 52000
if salary > 50000:
print("高於五萬")
elif salary > 40000:
print("四萬到五萬之間")
else:
print("四萬以下")
# 函式
def add(a, b):
return a + b
print(add(2, 3)) # 5
# 例外處理
try:
result = 10 / 0
except ZeroDivisionError:
print("不能除以零!")
2.4 pandas 讀寫資料
import pandas as pd
# 讀檔
df = pd.read_excel("data.xlsx") # 或 pd.read_csv("data.csv")
# 讀取特定工作表
df = pd.read_excel("data.xlsx", sheet_name="Sheet2")
# 看資料
print(df.head()) # 前 5 列
print(df.tail(3)) # 後 3 列
print(df.columns) # 欄位名
print(df.shape) # (列數, 欄數)
print(df.dtypes) # 每個欄位的資料型別
print(df.describe()) # 數值欄位的統計摘要
print(df.info()) # 資料表整體資訊
# 篩選
it = df[df["部門"] == "IT"]
high_salary = df[df["月薪"] > 50000]
# 多條件篩選
senior_it = df[(df["部門"] == "IT") & (df["年資"] > 3)]
# 新增欄位
df["年薪"] = df["月薪"] * 12
# 排序
df_sorted = df.sort_values("月薪", ascending=False)
# 去除重複
df_unique = df.drop_duplicates(subset=["員工編號"])
# 寫出
df.to_excel("output.xlsx", index=False)
df.to_csv("output.csv", index=False, encoding="utf-8-sig")
2.5 彙總(groupby)
# 依部門加總數量
summary = df.groupby("部門")["數量"].sum().reset_index()
# 多個統計
summary = df.groupby("部門").agg(
總數量=("數量", "sum"),
平均金額=("金額", "mean"),
最高金額=("金額", "max"),
筆數=("金額", "count"),
).reset_index()
pandas 的
groupby概念跟 SQL 的GROUP BY一樣,上週學過會很好懂。
2.6 pivot_table(樞紐分析表)
# 類似 Excel 的樞紐分析表
pivot = df.pivot_table(
values="金額", # 要彙總的數值
index="部門", # 列標籤
columns="月份", # 欄標籤
aggfunc="sum", # 彙總方式
fill_value=0 # 空值填 0
)
print(pivot)
pivot.to_excel("樞紐分析.xlsx")
常見誤解:「pivot_table 跟 groupby 一樣」—— 不完全一樣。pivot_table 可以產生二維的交叉表(像 Excel 的樞紐分析),groupby 產生的是一維的分組彙總。要做「部門 × 月份」的交叉分析,pivot_table 更直覺。
2.7 merge 合併資料(類似 SQL JOIN)
# 假設有兩張表
# employees: 員工編號, 姓名, 部門代碼
# departments: 部門代碼, 部門名稱
merged = pd.merge(
employees,
departments,
on="部門代碼", # 共同的 key 欄位
how="left" # left / right / inner / outer
)
# 如果 key 欄位名稱不同
merged = pd.merge(
employees,
departments,
left_on="dept_id",
right_on="department_id",
how="inner"
)
| how 參數 | 對應 SQL | 說明 |
|---|---|---|
inner |
INNER JOIN | 只保留兩邊都有的 |
left |
LEFT JOIN | 保留左表全部 |
right |
RIGHT JOIN | 保留右表全部 |
outer |
FULL OUTER JOIN | 兩邊都保留 |
2.8 fillna 處理缺值
# 查看缺值
print(df.isnull().sum())
# 用固定值填補
df["備註"] = df["備註"].fillna("無")
# 用前一個值填補(適合時間序列)
df["溫度"] = df["溫度"].fillna(method="ffill")
# 用平均值填補
df["薪水"] = df["薪水"].fillna(df["薪水"].mean())
# 直接刪除有缺值的列
df_clean = df.dropna()
# 刪除特定欄位有缺值的列
df_clean = df.dropna(subset=["姓名", "部門"])
2.9 apply 自訂運算
# 對單一欄位套用函式
df["薪資等級"] = df["月薪"].apply(lambda x: "高" if x > 60000 else "一般")
# 自訂函式(複雜邏輯用)
def classify_salary(salary):
if salary >= 80000:
return "A級"
elif salary >= 60000:
return "B級"
elif salary >= 40000:
return "C級"
else:
return "D級"
df["薪資等級"] = df["月薪"].apply(classify_salary)
# 對整列(row)操作
df["全名"] = df.apply(lambda row: row["姓"] + row["名"], axis=1)
2.10 os / shutil 檔案操作
import os
import shutil
# ---- os 模組 ----
# 列出資料夾內容
files = os.listdir("./reports")
print(files)
# 檢查檔案/資料夾是否存在
print(os.path.exists("data.xlsx")) # True/False
print(os.path.isfile("data.xlsx")) # 是否為檔案
print(os.path.isdir("./reports")) # 是否為資料夾
# 取得檔案資訊
size = os.path.getsize("data.xlsx") # 檔案大小(bytes)
print(f"檔案大小:{size / 1024:.1f} KB")
# 建立資料夾
os.makedirs("./output/2024", exist_ok=True)
# 重新命名
os.rename("old_name.txt", "new_name.txt")
# 刪除檔案
os.remove("temp.txt")
# 取得當前工作目錄
print(os.getcwd())
# 組合路徑(跨平台安全)
path = os.path.join("reports", "2024", "jan.xlsx")
# ---- shutil 模組 ----
# 複製檔案
shutil.copy2("source.xlsx", "backup/source_backup.xlsx")
# 複製整個資料夾
shutil.copytree("./reports", "./reports_backup")
# 搬移檔案
shutil.move("temp.xlsx", "./archive/temp.xlsx")
# 刪除整個資料夾(小心使用!)
shutil.rmtree("./old_reports")
2.11 實用範例:自動依日期整理檔案
import os
import shutil
from datetime import datetime
source = "./downloads"
for filename in os.listdir(source):
filepath = os.path.join(source, filename)
if os.path.isfile(filepath):
# 取得檔案修改日期
mtime = os.path.getmtime(filepath)
date_folder = datetime.fromtimestamp(mtime).strftime("%Y-%m")
# 建立日期資料夾
dest_folder = os.path.join("./archive", date_folder)
os.makedirs(dest_folder, exist_ok=True)
# 搬移檔案
shutil.move(filepath, os.path.join(dest_folder, filename))
print("檔案整理完成!")
三、今日實作任務(動手)
- 建立一個 Python 虛擬環境,安裝 pandas 與 openpyxl。
- 自己用 Excel 做一個小檔(如:日期、產品、數量、金額,20 列)。
- 用 pandas 讀進來。
- 依「產品」彙總總數量與總金額。
- 用 pivot_table 做「產品 × 月份」的交叉分析。
- 輸出成新的 Excel。
四、今日練習
Q1. 用 pandas 讀一個 CSV 檔的指令?
Q2. 依「部門」算每組「薪水」平均,怎麼寫?
Q3. pandas 的 groupby 對應 SQL 的什麼語法?
Q4. 如何用 pandas 合併兩張表(類似 SQL 的 LEFT JOIN)?
Q5. 資料中有缺值(NaN),想用 0 填補,怎麼寫?
Q6. 想對「月薪」欄位做自訂分類(>60000 標「高」,否則標「一般」),怎麼寫?
參考解答
# A1
df = pd.read_csv("data.csv")
# A2
df.groupby("部門")["薪水"].mean()
# 或完整版本:
avg_salary = df.groupby("部門")["薪水"].mean().reset_index()
avg_salary.columns = ["部門", "平均薪水"]
# A4
merged = pd.merge(left_df, right_df, on="共同欄位", how="left")
# A5
df["欄位名"] = df["欄位名"].fillna(0)
# 或全部欄位一起填:
df = df.fillna(0)
# A6
df["薪資等級"] = df["月薪"].apply(lambda x: "高" if x > 60000 else "一般")
**A3**:對應 SQL 的 `GROUP BY` + 聚合函數。例如 `df.groupby("部門")["薪水"].mean()` 等同於 `SELECT 部門, AVG(薪水) FROM table GROUP BY 部門`。
五、常見誤解
| 誤解 | 正確觀念 |
|---|---|
| 「pandas 只能處理小資料」 | pandas 處理幾十萬列通常沒問題;超過百萬列才需考慮 Dask 等工具 |
| 「merge 跟 concat 一樣」 | merge 是依 key 欄位合併(類似 JOIN);concat 是上下堆疊或左右拼接 |
| 「fillna 會改變原始資料」 | 預設不會(回傳新的 DataFrame),除非加 inplace=True |
| 「venv 很麻煩可以不用」 | 養成好習慣很重要,避免套件版本衝突導致專案無法執行 |
六、今日檢核
- [ ] 我裝好了 Python 與 pandas
- [ ] 我建立過 Python 虛擬環境
- [ ] 我會用 pandas 讀寫 Excel/CSV
- [ ] 我會用 groupby 做彙總
- [ ] 我會用 pivot_table 做交叉分析
- [ ] 我會用 merge 合併兩張表
- [ ] 我知道 os/shutil 的基本檔案操作
七、延伸閱讀
- pandas 官方文件 — 10 Minutes to pandas
- Real Python — pandas merge, join, concat
- Python venv 官方文件
- os 模組官方文件
- shutil 模組官方文件
⬅️ 上一天:Day 1 | 🏠 本週總覽 | ➡️ 下一天:Day 3 — 自動化判斷思維