steveyehtw's picture
Publish standalone Lesson 21 starter for students continuing from Lesson 19
271e6aa
Raw History Blame Contribute Delete
4.23 kB
"""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()