"""Lesson 21:用同一份公開資料練習排名、文字圖與跨年比較。""" import csv from pathlib import Path DATA_FILE = Path("data/students_by_county.csv") # 先執行一次,再依自己的問題修改這幾個條件。 COUNTY = "新北市" BEFORE_YEAR = "104" AFTER_YEAR = "114" FIELD = "國小[人]" TOP_N = 5 # 以下工具已備好,可以先使用,再查詢函式裡的寫法。 def load_rows(path=DATA_FILE): """將 CSV 每列讀成 dict;兼容有 BOM 的 UTF-8 檔案。""" with open(path, encoding="utf-8-sig", newline="") as file: return list(csv.DictReader(file)) def parse_count(value): """數字文字轉成整數;空白、破折號與無效值回傳 None。""" cleaned = (value or "").strip().replace(",", "") if not cleaned or cleaned == "-": return None try: return int(cleaned) except ValueError: return None def get_count(rows, school_year, county, field): """查詢指定學年度、縣市與教育階段的人數;找不到回傳 None。""" for row in rows: if row.get("學年度", "").strip() != str(school_year): continue if row.get("縣市別", "").strip() == county: return parse_count(row.get(field)) return None def count_by_county(rows, school_year, field): """同一學年度的人數排名;未知值不當成 0。""" totals = {} for row in rows: if row.get("學年度", "").strip() != str(school_year): continue county = row.get("縣市別", "").strip() count = parse_count(row.get(field)) if county and count is not None: totals[county] = count return sorted(totals.items(), key=lambda item: item[1], reverse=True) def print_text_chart(results, limit=5): """使用相同尺度畫文字長條,並保留可核對的人數。""" visible = results[:max(0, limit)] if not visible: print("沒有符合條件的資料,請檢查學年度、欄位名稱與顯示筆數。") return largest = max(value for _, value in visible) print("縣市 人數(人) | 比例長條") for county, value in visible: bar_length = round(value / largest * 24) if largest else 0 print(f"{county:<6} {value:>8,} | {'█' * bar_length}") print("長條從 0 開始,最長為 24 格;仍以旁邊的實際數字核對。") def main(): rows = load_rows() print("=== Lesson 21:比較與圖表 ===") print(f"讀入 {len(rows)} 列資料") if not rows: print("CSV 沒有資料,請檢查檔案。") return print(f"欄位:{', '.join(rows[0])}") # 第一部分:先觀察現成的排名圖,再換成自己的條件。 results = count_by_county(rows, AFTER_YEAR, FIELD) print(f"\n{AFTER_YEAR} 學年度各縣市 {FIELD}(前 {TOP_N} 名)") print_text_chart(results, TOP_N) print("來源:教育部統計處|各級學校縣市別學生人數(課堂 CSV)") # 第二部分:兩個原始數字都查到以後,才做相減。 before = get_count(rows, BEFORE_YEAR, COUNTY, FIELD) after = get_count(rows, AFTER_YEAR, COUNTY, FIELD) print(f"\n{COUNTY} {FIELD}:{BEFORE_YEAR} → {AFTER_YEAR} 學年度") if before is None or after is None: print("找不到完整的兩筆人數,請核對年份、縣市與欄位;未知值不能當成 0。") return print(f"{BEFORE_YEAR} 學年度:{before:,} 人") print(f"{AFTER_YEAR} 學年度:{after:,} 人") # TODO:以「後來的人數」減去「原來的人數」,取代下面的 None。 change = None if change is None: print("請完成 change 的相減算式,再執行一次。") return print(f"增減人數:{change:+,} 人") if change > 0: print(f"在這兩個學年度之間增加 {change:,} 人。") elif change < 0: print(f"在這兩個學年度之間減少 {abs(change):,} 人。") else: print("這兩個學年度的人數相同。") print("只比較兩個端點,還不能說中間每一年都增加或減少,也不能單靠此表判斷原因。") if __name__ == "__main__": main()