File size: 4,234 Bytes
271e6aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""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()