本日主題:選一個題目,動手做出自動化小作品 預計時間:2 小時(動手日) 對應主教材:第 6.3 節
一、今日學習目標
- [ ] 選定一個自動化題目
- [ ] 完成程式主要功能
- [ ] 學會完整的報表處理流程(含 merge、pivot_table)
二、選一個題目(擇一)
題目 A:每日報表彙總(推薦,最實用)
讀取一份(或多份)Excel/CSV,依某欄位彙總,輸出報表。
import pandas as pd
from datetime import datetime
# ---- 設定區 ----
INPUT_FILE = "daily_data.xlsx"
OUTPUT_DIR = "./"
GROUP_COL = "部門"
# ---- 主程式 ----
df = pd.read_excel(INPUT_FILE)
# 基本彙總
summary = df.groupby(GROUP_COL).agg(
總數量=("數量", "sum"),
總金額=("金額", "sum"),
平均金額=("金額", "mean"),
筆數=("金額", "count"),
).reset_index()
# 加上佔比欄位
summary["金額佔比(%)"] = (summary["總金額"] / summary["總金額"].sum() * 100).round(1)
# 排序
summary = summary.sort_values("總金額", ascending=False)
# 輸出
stamp = datetime.now().strftime("%Y%m%d")
output_path = f"{OUTPUT_DIR}彙總報表_{stamp}.xlsx"
summary.to_excel(output_path, index=False)
print(f"報表已產生:{output_path},共 {len(summary)} 個{GROUP_COL}")
進階版:多欄位交叉分析
# 用 pivot_table 做「部門 × 月份」交叉分析
df["月份"] = pd.to_datetime(df["日期"]).dt.strftime("%Y-%m")
pivot = df.pivot_table(
values="金額",
index="部門",
columns="月份",
aggfunc="sum",
fill_value=0,
margins=True, # 加總計列
margins_name="合計"
)
pivot.to_excel(f"交叉分析_{stamp}.xlsx")
print("交叉分析表已產生")
進階版:合併員工資料再彙總
# 報表只有員工編號,需要 merge 員工姓名
employees = pd.read_excel("employees.xlsx") # 有:員工編號, 姓名, 部門
sales = pd.read_excel("sales.xlsx") # 有:員工編號, 日期, 金額
# 合併
merged = pd.merge(sales, employees, on="員工編號", how="left")
# 處理缺值(有些員工可能已離職,查不到)
merged["姓名"] = merged["姓名"].fillna("未知員工")
merged["部門"] = merged["部門"].fillna("未分類")
# 依部門彙總
dept_summary = merged.groupby("部門").agg(
總金額=("金額", "sum"),
人數=("員工編號", "nunique"),
).reset_index()
dept_summary.to_excel("部門業績.xlsx", index=False)
題目 B:批次整理檔案
把一堆檔案依規則重新命名/分類。
import os
import shutil
folder = "./scans"
for i, fn in enumerate(sorted(os.listdir(folder)), start=1):
if fn.lower().endswith(".pdf"):
new = f"維修單_{i:03d}.pdf"
os.rename(os.path.join(folder, fn), os.path.join(folder, new))
print("整理完成")
進階版:依檔案類型分資料夾
import os
import shutil
source = "./downloads"
type_map = {
".pdf": "PDF文件",
".xlsx": "Excel檔案",
".xls": "Excel檔案",
".docx": "Word文件",
".pptx": "簡報檔案",
".jpg": "圖片",
".png": "圖片",
".csv": "資料檔",
}
for fn in os.listdir(source):
filepath = os.path.join(source, fn)
if not os.path.isfile(filepath):
continue
ext = os.path.splitext(fn)[1].lower()
folder_name = type_map.get(ext, "其他")
dest_folder = os.path.join(source, folder_name)
os.makedirs(dest_folder, exist_ok=True)
shutil.move(filepath, os.path.join(dest_folder, fn))
print("檔案分類完成!")
題目 C:多檔合併
把資料夾內多個 Excel 合併成一個。
import pandas as pd
import glob
files = glob.glob("./reports/*.xlsx")
dfs = [pd.read_excel(f) for f in files]
all_df = pd.concat(dfs, ignore_index=True)
# 去重複
all_df = all_df.drop_duplicates()
# 排序
all_df = all_df.sort_values("日期")
all_df.to_excel("合併結果.xlsx", index=False)
print(f"合併了 {len(files)} 個檔案,共 {len(all_df)} 筆資料")
進階版:合併時記錄來源檔名
import pandas as pd
import glob
import os
files = glob.glob("./reports/*.xlsx")
dfs = []
for f in files:
df = pd.read_excel(f)
df["來源檔案"] = os.path.basename(f)
dfs.append(df)
all_df = pd.concat(dfs, ignore_index=True)
all_df.to_excel("合併結果_含來源.xlsx", index=False)
print(f"合併了 {len(files)} 個檔案")
題目 D:自動寄送報表(進階挑戰)
結合題目 A + smtplib,產生報表後自動寄信通知。
import pandas as pd
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from datetime import datetime
# 1. 產生報表
df = pd.read_excel("daily_data.xlsx")
summary = df.groupby("部門").agg(
總金額=("金額", "sum"),
).reset_index()
stamp = datetime.now().strftime("%Y%m%d")
report_file = f"彙總報表_{stamp}.xlsx"
summary.to_excel(report_file, index=False)
print(f"報表產生完成:{report_file}")
# 2. 寄信
msg = MIMEMultipart()
msg["From"] = "it@company.com"
msg["To"] = "manager@company.com"
msg["Subject"] = f"每日彙總報表 — {stamp}"
body = f"主管您好,\n\n今日彙總報表已產生,共 {len(summary)} 個部門。\n請參閱附件。\n\nIT 部門"
msg.attach(MIMEText(body, "plain", "utf-8"))
with open(report_file, "rb") as f:
part = MIMEBase("application", "octet-stream")
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header("Content-Disposition", f"attachment; filename={report_file}")
msg.attach(part)
with smtplib.SMTP("smtp.company.com", 587) as server:
server.starttls()
server.login("it@company.com", "password")
server.send_message(msg)
print("報表已寄出!")
三、今日實作任務(產出)
- [ ] 選定題目(建議 A)
- [ ] 準備測試資料(自己做幾個 Excel/檔案)
- [ ] 把程式跑通,能產出正確結果
- [ ] 記錄:你解決的問題是什麼、原本要花多久
- [ ] (進階)嘗試加入 merge 或 pivot_table
遇到錯誤是正常的:把錯誤訊息看懂、上網查、修正——這正是工作中寫程式的日常。
四、今日練習
Q1. pandas 的 pd.merge() 中,how="left" 代表什麼意思?
Q2. 用 pivot_table 做「產品 × 月份」的金額加總,怎麼寫?
Q3. 用 os 模組建立一個不存在的資料夾(如果已存在也不報錯),怎麼寫?
參考解答
**A1.** `how="left"` 代表 LEFT JOIN,保留左邊 DataFrame 的所有資料列,右邊沒有匹配到的欄位填 NaN。等同 SQL 的 `LEFT JOIN`。 **A2.**pivot = df.pivot_table(
values="金額",
index="產品",
columns="月份",
aggfunc="sum",
fill_value=0
)
**A3.**
import os
os.makedirs("./output/2024", exist_ok=True)
`exist_ok=True` 代表資料夾已存在時不會報錯。
五、常見誤解
| 誤解 | 正確觀念 |
|---|---|
| 「程式第一次就該跑對」 | 除錯是常態,工程師大量時間花在 debug |
| 「作品要很複雜才能拿出來講」 | 一個 20 行的腳本只要真的解決問題、有量化成果,就是好作品 |
| 「測試資料隨便做就好」 | 測試資料要包含邊界狀況(空值、重複、格式不一致)才能驗證程式的穩健性 |
六、今日檢核
- [ ] 我選定了題目並備好測試資料
- [ ] 我的程式能正確跑出結果
- [ ] 我清楚這個作品解決什麼問題
- [ ] 我記錄了「原本手動花多久 vs 自動化後花多久」
七、延伸閱讀
⬅️ 上一天:Day 3 | 🏠 本週總覽 | ➡️ 下一天:Day 5 — 自動化作品(下)