Spaces:
Running
Running
Commit ·
5fbb440
1
Parent(s): 584a199
Publish four-week data analysis course and revised student slides
Browse files- .gitattributes +1 -0
- README.md +2 -0
- classroom_data/course.json +5 -1
- classroom_data/curriculum/data_analysis_special_course.md +153 -0
- classroom_data/curriculum/slides/Lesson19_資料偵探提出好問題.pptx +3 -0
- classroom_data/curriculum/slides/Lesson20_把資料整理成證據.pptx +3 -0
- classroom_data/curriculum/slides/Lesson21_找出差異圖表教學簡報.pptx +3 -0
- classroom_data/curriculum/slides/Lesson22_資料故事發表會.pptx +3 -0
- classroom_data/curriculum/slides/assets/README.md +6 -0
- classroom_data/curriculum/slides/assets/data-download-page.png +3 -0
- classroom_data/curriculum/slides/assets/telearn-background.png +3 -0
- classroom_data/curriculum/slides/data.zip +3 -0
- classroom_data/curriculum/slides/data/Lesson19_資料偵探提出好問題_學生版修訂.pptx +3 -0
- classroom_data/curriculum/slides/data/Lesson20_先檢查再相信數字_學生版修訂.pptx +3 -0
- classroom_data/curriculum/slides/data/Lesson21_找差異讓圖表說清楚_學生版修訂.pptx +3 -0
- classroom_data/curriculum/slides/data/Lesson22_分享你的資料發現_學生版修訂.pptx +3 -0
- classroom_data/curriculum/slides/previews/Lesson19_資料下載預覽.png +3 -0
- classroom_data/curriculum/tools/build_slides.mjs +131 -0
- classroom_data/curriculum/tools/lesson_content.mjs +128 -0
- classroom_data/templates/week19/DATA_SOURCE.md +18 -0
- classroom_data/templates/week19/REPORT.md +38 -0
- classroom_data/templates/week19/data/students_by_county.csv +243 -0
- classroom_data/templates/week19/main.py +87 -0
- classroom_static/classroom.css +2 -0
- classroom_static/student.html +2 -2
- classroom_static/student.js +21 -1
.gitattributes
CHANGED
|
@@ -43,3 +43,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 43 |
*.avif filter=lfs diff=lfs merge=lfs -text
|
| 44 |
*.apk filter=lfs diff=lfs merge=lfs -text
|
| 45 |
*.JPEG filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 43 |
*.avif filter=lfs diff=lfs merge=lfs -text
|
| 44 |
*.apk filter=lfs diff=lfs merge=lfs -text
|
| 45 |
*.JPEG filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
*.pptx filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -18,6 +18,8 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 18 |
|
| 19 |
內建 2026 Fall Lesson 路線圖。Lesson 2 只匯入一次專案起始套版,Lesson 3~4 共用同一份案件進度;支援多個 Python 檔、ZIP 匯入匯出、即時監看、專案最後正式版與整班發布。
|
| 20 |
|
|
|
|
|
|
|
| 21 |
課程與名單管理:
|
| 22 |
|
| 23 |
- 教師先在控制台建立班級與學生名單,可用逗號一次加入多人。
|
|
|
|
| 18 |
|
| 19 |
內建 2026 Fall Lesson 路線圖。Lesson 2 只匯入一次專案起始套版,Lesson 3~4 共用同一份案件進度;支援多個 Python 檔、ZIP 匯入匯出、即時監看、專案最後正式版與整班發布。
|
| 20 |
|
| 21 |
+
Lesson 19~22 延伸為四週數據分析特別課程,使用公開 CSV、問題選單、資料清理、可重現計算和 Console 文字圖表;帶課流程見 `classroom_data/curriculum/data_analysis_special_course.md`。
|
| 22 |
+
|
| 23 |
課程與名單管理:
|
| 24 |
|
| 25 |
- 教師先在控制台建立班級與學生名單,可用逗號一次加入多人。
|
classroom_data/course.json
CHANGED
|
@@ -16,5 +16,9 @@
|
|
| 16 |
{"id":"week15","lesson":15,"title":"班級數位藝廊","project":"生成藝術玩具箱","output":"系列作品與線上展示","template":false},
|
| 17 |
{"id":"week16","lesson":16,"title":"Director's Cut 重製計畫","project":"精選作品重製","output":"選題與改進承諾","template":false},
|
| 18 |
{"id":"week17","lesson":17,"title":"Release Candidate","project":"精選作品重製","output":"通過驗收的上架版","template":false},
|
| 19 |
-
{"id":"week18","lesson":18,"title":"Demo Day","project":"精選作品重製","output":"線上作品、展示稿與回顧","template":false}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
]}
|
|
|
|
| 16 |
{"id":"week15","lesson":15,"title":"班級數位藝廊","project":"生成藝術玩具箱","output":"系列作品與線上展示","template":false},
|
| 17 |
{"id":"week16","lesson":16,"title":"Director's Cut 重製計畫","project":"精選作品重製","output":"選題與改進承諾","template":false},
|
| 18 |
{"id":"week17","lesson":17,"title":"Release Candidate","project":"精選作品重製","output":"通過驗收的上架版","template":false},
|
| 19 |
+
{"id":"week18","lesson":18,"title":"Demo Day","project":"精選作品重製","output":"線上作品、展示稿與回顧","template":false},
|
| 20 |
+
{"id":"week19","lesson":19,"title":"資料分析 I:公開資料與問題","project":"公開資料分析","output":"從公開資料選定自己的問題,能執行查詢並指出答案在哪個欄位。","workspace_id":"week19","template":true,"guide":{"goal":"從公開資料選定自己的問題,能執行查詢並指出答案在哪個欄位。","source_title":"教育部統計處|各級學校縣市別學生人數","source_url":"https://data.gov.tw/dataset/40121","source_note":"四週使用同一份課堂 CSV:104–114 學年度、22 個縣市、242 筆資料、15 個欄位。檔案已附在 week19 專案。","flow":[{"time":"0–20 分","title":"公開資料","body":"看三筆實際學生人數紀錄,確認年份、縣市與人數欄位。"},{"time":"20–40 分","title":"下載與 CSV","body":"到官方頁面看 CSV 下載位置,確認課堂檔案在 data/students_by_county.csv,讀懂標題列。"},{"time":"40–50 分","title":"選一個問題","body":"各自選定縣市、學年度與教育階段,寫下要比較什麼。"},{"time":"50–60 分","title":"休息","body":"保存目前內容,休息 10 分鐘。"},{"time":"60–80 分","title":"讀取並執行","body":"看 load_rows() 的用途,執行 main.py,核對讀入 242 筆資料與範例人數。"},{"time":"80–95 分","title":"修改條件","body":"改查 114 學年度國中人數,核對前 3 名,再換成自己的條件。"},{"time":"95–105 分","title":"AI 與欄位","body":"請 AI 指出題目所需欄位,到 CSV 檢查它是否存在。"},{"time":"105–120 分","title":"保存個人進度","body":"把題目、來源與一個核對過的數字寫進 REPORT.md,存檔並提交。"}],"checklist":["問題寫明年份、縣市或教育階段","資料名稱與官方網址已記錄","main.py 可以執行","至少在 CSV 找到一筆輸出的人數"],"ai_checkpoint":"把自己的題目和 CSV 欄位名稱交給 AI,請它指出所需欄位,再到原始表格確認。","submission":"問題寫明年份、縣市或教育階段、資料名稱與官方網址已記錄、main.py 可以執行、至少在 CSV 找到一筆輸出的人數","ai_note":"每人各自完成。允許使用外部 AI,保留實際使用與核對紀錄;沒有使用就如實註明。","question_bank":[{"group":"起步題","text":"114 學年度,哪些縣市的國小學生人數最多?"},{"group":"起步題","text":"選一個縣市,104 到 114 學年度的國小學生人數是增加還是減少?"},{"group":"起步題","text":"104 到 114 學年度,哪個縣市的國小學生人數變化最多?"},{"group":"起步題","text":"114 學年度,選一個縣市,國小和國中學生人數差多少?"},{"group":"挑戰題","text":"選一個縣市,哪兩個學年度的國中學生人數最多和最少?"},{"group":"挑戰題","text":"114 學年度,哪個縣市的高中普通科學生人數最多?"},{"group":"挑戰題","text":"104 到 114 學年度,哪個縣市的大專校院校本部學生人數變化比例最大?"},{"group":"挑戰題","text":"114 學年度,哪個縣市的幼兒園與國小學生人數差距最大?"}],"question_rules":["先挑一個數值欄,再加上一個學年度、縣市或教育階段作為比較條件。","問「多少、哪個較多、差多少、如何變化」;不要讓這張表替你猜原因或預測未來。","自訂資料使用公開、彙總的 CSV,留下來源、時間範圍、欄位和單位,不匯入個人資料。"]}},
|
| 21 |
+
{"id":"week20","lesson":20,"title":"資料分析 II:資料整理","project":"公開資料分析","output":"讀懂數字轉換與未知值的處理,實際核對三筆資料。","workspace_id":"week19","template":false,"guide":{"goal":"讀懂數字轉換與未知值的處理,實際核對三筆資料。","source_title":"教育部統計處|各級學校縣市別學生人數","source_url":"https://data.gov.tw/dataset/40121","source_note":"四週使用同一份課堂 CSV:104–114 學年度、22 個縣市、242 筆資料、15 個欄位。檔案已附在 week19 專案。","flow":[{"time":"0–25 分","title":"文字與數字","body":"重跑上週程式,練習文字相加、int()、strip() 與 replace()。"},{"time":"25–45 分","title":"0 與未知","body":"比較 0、空字串與破折號,讀懂 parse_count() 的判斷。"},{"time":"45–50 分","title":"��試輸入","body":"每人測試五種輸入,寫下回傳結果。"},{"time":"50–60 分","title":"休息","body":"保存目前內容,休息 10 分鐘。"},{"time":"60–75 分","title":"筆數與重複","body":"核對 242 筆、15 個欄位,分清不同學年度與真正重複的紀錄。"},{"time":"75–95 分","title":"核對三筆","body":"各自到 CSV 找到三筆查詢結果,記下年份、縣市、欄位和人數。"},{"time":"95–105 分","title":"解釋錯誤","body":"請 AI 解釋 int() 的轉換錯誤,用自己的程式測試一個例子。"},{"time":"105–120 分","title":"整理紀錄","body":"完成資料處理與核對紀錄,重新執行並提交進度;負值檢查為選做題。"}],"checklist":["測試五種字串輸入","沒有把未知值直接改成 0","核對三筆結果並記錄條件","保留原始 CSV,程式可重新執行"],"ai_checkpoint":"請 AI 解釋 int() 遇到空字串的錯誤,自己測試一個例子並記下回傳結果。","submission":"測試五種字串輸入、沒有把未知值直接改成 0、核對三筆結果並記錄條件、保留原始 CSV,程式可重新執行","ai_note":"每人各自完成。允許使用外部 AI,保留實際使用與核對紀錄;沒有使用就如實註明。"}},
|
| 22 |
+
{"id":"week21","lesson":21,"title":"資料分析 III:比較與圖表","project":"公開資料分析","output":"用排名、差值或圖表回答自己的問題,說明計算方式。","workspace_id":"week19","template":false,"guide":{"goal":"用排名、差值或圖表回答自己的問題,說明計算方式。","source_title":"教育部統計處|各級學校縣市別學生人數","source_url":"https://data.gov.tw/dataset/40121","source_note":"四週使用同一份課堂 CSV:104–114 學年度、22 個縣市、242 筆資料、15 個欄位。檔案已附在 week19 專案。","flow":[{"time":"0–20 分","title":"人數增減","body":"用兩個原始數字相減,完成新北市示範與臺中市練習。"},{"time":"20–35 分","title":"百分比與排名","body":"說明變化百分比的分母,檢查人數排名;百分比計算可選做。"},{"time":"35–50 分","title":"長條圖","body":"比較表格、圖表和 Console 文字圖,說明標題、單位、來源與從 0 開始的尺度。"},{"time":"50–60 分","title":"休息","body":"保存目前內容,休息 10 分鐘。"},{"time":"60–75 分","title":"程式計算","body":"用 get_count() 取出兩個學年度的人數,再計算差值。"},{"time":"75–90 分","title":"修改自己的條件","body":"把例題的縣市或欄位改成自己的專題條件,核對結果。"},{"time":"90–100 分","title":"解釋數字","body":"分清總數、比例和原因,改寫 AI 加入未證實原因的句子。"},{"time":"100–120 分","title":"個人圖表","body":"完成一張圖或一組數字比較,寫下方法與說明,重新執行並提交。"}],"checklist":["圖表或比較結果回答自己的問題","有原始數字和計算方式","圖表寫明學年度、對象、單位和來源","沒有把人數變化直接寫成原因"],"ai_checkpoint":"檢查 AI 是否把人數變化寫成原因,找出能從 CSV 核對的部分,改掉缺少證據的說法。","submission":"圖表或比較結果回答自己的問題、有原始數字和計算方式、圖表寫明學年度、對象、單位和來源、沒有把人數變化直接寫成原因","ai_note":"每人各自完成。允許使用外部 AI,保留實際使用與核對紀錄;沒有使用就如實註明。"}},
|
| 23 |
+
{"id":"week22","lesson":22,"title":"資料分析 IV:分析報告","project":"公開資料分析","output":"完成個人報告,重新執行專案,親自說明一組數字與資料範圍。","workspace_id":"week19","template":false,"guide":{"goal":"完成個人報告,重新執行專案,親自說明一組數字與資料範圍。","source_title":"教育部統計處|各級學校縣市別學生人數","source_url":"https://data.gov.tw/dataset/40121","source_note":"四週使用同一份課堂 CSV:104–114 學年度、22 個縣市、242 筆資料、15 個欄位。檔案已附在 week19 專案。","flow":[{"time":"0–15 分","title":"完整報告範例","body":"看一份完整短報告,對照自己的 REPORT.md。"},{"time":"15–30 分","title":"報告各段","body":"寫清問題、來源、方法、數字與解釋,練習修正文句。"},{"time":"30–50 分","title":"重新執行","body":"每人重新打開專案,處理路徑、欄位或條件問題,核對報告數字。"},{"time":"50–60 分","title":"休息","body":"保存目前內容,休息 10 分鐘。"},{"time":"60–65 分","title":"AI 使用紀錄","body":"記錄一件實際使用與核對的事;未使用者如實填寫。"},{"time":"65–85 分","title":"個人報告","body":"各自補完來源、方法、結果和資料還不能回答的事。"},{"time":"85–90 分","title":"準備分享","body":"挑一張圖或一組數字,準備個人說明。"},{"time":"90–115 分","title":"個人分享","body":"每人介紹問題、方法與答案,回答一個問題;依人數調整分享長度。"},{"time":"115–120 分","title":"提交專題","body":"存檔,確認報告和程式一致,提交目前版本。"}],"checklist":["REPORT.md 有問題、來源、方法與結果","程式可重跑,數字與報告一致","資料檔案與 DATA_SOURCE.md 都在","能親自說明數字與一段相關程式","AI 紀錄如實填寫;未使用也可以"],"ai_checkpoint":"檢查 AI 潤飾是否加入原文沒有的原因,保留確定的年份、對象和數字。","submission":"REPORT.md 有問題、來源、方法與結果、程式可重跑,數字與報告一致、資料檔案與 DATA_SOURCE.md 都在、能親自說明數字與一段相關程式、AI 紀錄如實填寫;未使用也可以","ai_note":"每人各自完成。允許使用外部 AI,保留實際使用與核對紀錄;沒有使用就如實註明。"}}
|
| 24 |
]}
|
classroom_data/curriculum/data_analysis_special_course.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 數據分析特別課程|四週帶課說明
|
| 2 |
+
|
| 3 |
+
這四週接在 Lesson 18 後面,使用 Lesson 19–22。學生每人完成自己的題目、程式與報告,四週都使用同一個 week19 工作區。四週結束後可以回到原來的專題,繼續開發原有功能。
|
| 4 |
+
|
| 5 |
+
## 學生投影片
|
| 6 |
+
|
| 7 |
+
每週一份可編輯 PowerPoint。說明、例題、練習與解答分頁,投影時依順序帶課;遇到練習先停下來,學生作答後才翻解答。講者備註包含教學提醒、時間安排與來源。
|
| 8 |
+
|
| 9 |
+
| 週次 | 課題 | 頁數 | 投影片 |
|
| 10 |
+
| --- | --- | ---: | --- |
|
| 11 |
+
| Lesson 19 | 資料分析 I:公開資料與問題 | 24 | [第一週](slides/data/Lesson19_資料偵探提出好問題_學生版修訂.pptx) |
|
| 12 |
+
| Lesson 20 | 資料分析 II:資料整理 | 23 | [第二週](slides/data/Lesson20_先檢查再相信數字_學生版修訂.pptx) |
|
| 13 |
+
| Lesson 21 | 資料分析 III:比較與圖表 | 24 | [第三週](slides/data/Lesson21_找差異讓圖表說清楚_學生版修訂.pptx) |
|
| 14 |
+
| Lesson 22 | 資料分析 IV:分析報告 | 21 | [第四週](slides/data/Lesson22_分享你的資料發現_學生版修訂.pptx) |
|
| 15 |
+
|
| 16 |
+
教師提供的參考稿用於課程版面及教學節奏,沒有沿用電腦科學基礎課的內容。學生頁不放整堂課的流程表,時間安排在本文件與講者備註。
|
| 17 |
+
|
| 18 |
+
## 課前準備
|
| 19 |
+
|
| 20 |
+
- 開啟課堂系統的 week19 專案,確認 main.py、REPORT.md、DATA_SOURCE.md 與 data/students_by_county.csv 都在。
|
| 21 |
+
- 專案只使用 Python 內建模組;課堂圖表先使用 Console 文字長條圖。
|
| 22 |
+
- 示範用 CSV 已附在專案,不需要每位學生現場連網下載。
|
| 23 |
+
- 第一週第 5–6 頁會示範官方來源、CSV 下載按鈕、原始檔名與專案位置。
|
| 24 |
+
- 若學生已用舊版起始程式建立工作區,先備份學生的修改,再補上新版 main.py 裡的 get_count()。不要直接覆蓋學生程式。第三週的跨年比較會用到此函式。
|
| 25 |
+
|
| 26 |
+
## 資料與範例答案
|
| 27 |
+
|
| 28 |
+
資料提供者為教育部統計處,資料集名稱為「各級學校縣市別學生人數」。
|
| 29 |
+
|
| 30 |
+
- [資料集頁面](https://data.gov.tw/dataset/40121)
|
| 31 |
+
- [原始 CSV](https://stats.moe.gov.tw/files/opendata/edu_B_1_4.csv)
|
| 32 |
+
- 課堂檔案:classroom_data/templates/week19/data/students_by_county.csv
|
| 33 |
+
- 本課版本有 242 筆資料、15 個欄位,涵蓋 104–114 學年度與 22 個縣市。
|
| 34 |
+
- 一列代表一個學年度、一個縣市;同列有不同教育階段的人數。
|
| 35 |
+
- 這份附檔沒有空白格,也沒有重複的「學年度+縣市別」組合。第二週的空白、破折號及重複列都是另做的練習例子,不能說成原始資料的錯誤。
|
| 36 |
+
- 資料平台會更新,課堂範例以附檔版本為準。不要把更新後下載的檔案和舊範例答案混用。
|
| 37 |
+
- 使用資料時保留提供機關與網址,依政府資料開放授權條款第 1 版註明來源。
|
| 38 |
+
|
| 39 |
+
| 範例 | 答案 |
|
| 40 |
+
| --- | --- |
|
| 41 |
+
| 114 學年度國小人數前 3 名 | 新北市 194,670;臺中市 161,332;桃園市 132,964 |
|
| 42 |
+
| 114 學年度國中人數前 3 名 | 新北市 96,668;臺中市 82,767;臺北市 68,231 |
|
| 43 |
+
| 新北市國小人數,114 減 104 | 194,670 − 201,970 = −7,300 人 |
|
| 44 |
+
| 新北市國小人數變化率 | −7,300 ÷ 201,970 × 100% ≈ −3.6% |
|
| 45 |
+
| 臺中市國小人數,114 減 104 | 161,332 − 157,272 = 4,060 人 |
|
| 46 |
+
|
| 47 |
+
## 題目選擇
|
| 48 |
+
|
| 49 |
+
全班共用一份示範資料,每位學生自己選問題。基本題完成後可以換縣市、學年度或教育階段;不必每人找不同的資料集。
|
| 50 |
+
|
| 51 |
+
基本題:
|
| 52 |
+
|
| 53 |
+
1. 114 學年度,哪個縣市的國小學生最多?
|
| 54 |
+
2. 選一個縣市,104 和 114 學年度的國小學生相差幾人?
|
| 55 |
+
3. 114 學年度,選一個縣市,國小和國中學生相差幾人?
|
| 56 |
+
4. 換一個教育階段,比較同一學年度的人數排名。
|
| 57 |
+
|
| 58 |
+
選做題:
|
| 59 |
+
|
| 60 |
+
5. 104 到 114 學年度,哪個縣市的國小學生增加最多?
|
| 61 |
+
6. 選一個縣市,比較不同學年度的國中人數,找出最多和最少的一年。
|
| 62 |
+
7. 選一個縣市,計算國小學生人數的變化百分比。
|
| 63 |
+
8. 取同一縣市的多個學年度,檢查期間的變化,避免只憑端點說每年都增減。
|
| 64 |
+
|
| 65 |
+
自訂題目要能指出欄位與比較條件。如果題目問原因,可把它保留為後續問題,先完成這份資料能回答的人數比較。學生要換其他資料集時,確認資料是公開 CSV、有來源、欄位與單位清楚,且能在現有專案讀取;教師同意題目後再換,避免四週都花在找檔案。
|
| 66 |
+
|
| 67 |
+
## 第一週:公開資料與問題(120 分鐘)
|
| 68 |
+
|
| 69 |
+
| 時間 | 投影片 | 帶課安排 |
|
| 70 |
+
| --- | --- | --- |
|
| 71 |
+
| 0–10 分 | 1–2 | 問「哪個縣市的國小學生最多」,請學生說出答案可能從哪裡來。 |
|
| 72 |
+
| 10–20 分 | 3–4 | 用三筆實際紀錄說明學年度、縣市、人數與公開資料。 |
|
| 73 |
+
| 20–40 分 | 5–8 | 示範下載頁、CSV 按鈕、檔名與資料夾,再比較原始文字和表格。 |
|
| 74 |
+
| 40–50 分 | 9–14 | 完成找欄位的例題,各自選一個基本問題。 |
|
| 75 |
+
| 50–60 分 | — | 休息。 |
|
| 76 |
+
| 60–80 分 | 15–19 | 解釋資料能回答的範圍,讀取 CSV,執行起始程式並核對人數。 |
|
| 77 |
+
| 80–95 分 | 20–22 | 修改學年度與教育階段,完成國中人數排名練習。 |
|
| 78 |
+
| 95–105 分 | 23 | 請 AI 指出題目需要的欄位,學生到 CSV 確認。 |
|
| 79 |
+
| 105–120 分 | 24 | 寫下個人題目與來源,保存一個已核對的數字,提交進度。 |
|
| 80 |
+
|
| 81 |
+
來賓分析師可示範自己遇到陌生表格時怎麼確認欄位,以及怎麼把大問題改成能用手上資料計算的問題。不要一開始就要求學生完整理解整份函式。
|
| 82 |
+
|
| 83 |
+
較慢的學生完成一次排名查詢即可。較快的學生可換條件或做同年度的教育階段比較。
|
| 84 |
+
|
| 85 |
+
## 第二週:資料整理(120 分鐘)
|
| 86 |
+
|
| 87 |
+
| 時間 | 投影片 | 帶課安排 |
|
| 88 |
+
| --- | --- | --- |
|
| 89 |
+
| 0–10 分 | 1–2 | 各自重新執行上週程式,說明目前條件。 |
|
| 90 |
+
| 10–25 分 | 3–7 | 比較文字相加與整數相加,示範 int()、strip()、replace() 及轉換錯誤。 |
|
| 91 |
+
| 25–45 分 | 8–11 | 區分 0 與未知,讀懂 parse_count() 的兩部分。 |
|
| 92 |
+
| 45–50 分 | 12 | 學生測試五種輸入。 |
|
| 93 |
+
| 50–60 分 | — | 休息。 |
|
| 94 |
+
| 60–75 分 | 13–18 | 對答案,確認筆數與欄位數,練習辨認重複資料,說明查詢條件。 |
|
| 95 |
+
| 75–95 分 | 19–20 | 老師核對一筆,學生自行核對三筆並記錄。 |
|
| 96 |
+
| 95–105 分 | 21 | 請 AI 解釋一個錯誤,用程式測試它的說法。 |
|
| 97 |
+
| 105–115 分 | 22–23 | 完成整理紀錄;熟練者挑戰負值檢查。 |
|
| 98 |
+
| 115–120 分 | 23 | 重新執行、存檔並提交進度。 |
|
| 99 |
+
|
| 100 |
+
來賓分析師可用自己的資料工作經驗說明:能轉成數字,不一定代表數值合理;資料沒有某個問題,也不需要為了練習而修改原始檔。
|
| 101 |
+
|
| 102 |
+
基本目標是測試函式及核對三筆,負值檢查不列入最低標準。
|
| 103 |
+
|
| 104 |
+
## 第三週:比較與圖表(120 分鐘)
|
| 105 |
+
|
| 106 |
+
| 時間 | 投影片 | 帶課安排 |
|
| 107 |
+
| --- | --- | --- |
|
| 108 |
+
| 0–10 分 | 1–2 | 取回學生各自的問題與已核對數字。 |
|
| 109 |
+
| 10–20 分 | 3–6 | 完成新北市示範與臺中市增減人數練習。 |
|
| 110 |
+
| 20–35 分 | 7–10 | 解釋百分比的分母與人數排名;百分比可只看示範。 |
|
| 111 |
+
| 35–50 分 | 11–14 | 比較表格、長條圖與 Console 文字圖,說明標題、單位與從 0 開始。 |
|
| 112 |
+
| 50–60 分 | — | 休息。 |
|
| 113 |
+
| 60–75 分 | 15–18 | 計算長條長度,示範 get_count() 與跨年相減。 |
|
| 114 |
+
| 75–90 分 | 19 | 用程式重算臺中市的例題,再改成自己的條件。 |
|
| 115 |
+
| 90–100 分 | 20–21 | 區分總數、比例與原因,修改 AI 過度推論的句子。 |
|
| 116 |
+
| 100–115 分 | 22–23 | 各自完成一張圖或一組計算;熟練者查看多個學年度。 |
|
| 117 |
+
| 115–120 分 | 24 | 寫下原始數字、計算方式與說明,提交進度。 |
|
| 118 |
+
|
| 119 |
+
基本學生沿用現成的排名文字圖也能完成。能做跨年差值的學生使用 get_count();百分比與多年度走勢是選做內容。不要要求每個人同時完成排名、差值、比例和趨勢。
|
| 120 |
+
|
| 121 |
+
## 第四週:分析報告(120 分鐘)
|
| 122 |
+
|
| 123 |
+
| 時間 | 投影片 | 帶課安排 |
|
| 124 |
+
| --- | --- | --- |
|
| 125 |
+
| 0–15 分 | 1–3 | 打開 REPORT.md,看一份完整短報告的例子。 |
|
| 126 |
+
| 15–30 分 | 4–9 | 拆開問題、來源、方法、結果與解釋,改寫只看端點卻說「一直增加」的句子。 |
|
| 127 |
+
| 30–50 分 | 10–14 | 每人重新執行,處理路徑、欄位或條件問題,核對一個報告數字。 |
|
| 128 |
+
| 50–60 分 | — | 休息。 |
|
| 129 |
+
| 60–65 分 | 15–16 | 檢查 AI 修改是否加入沒有證據的原因,寫實際使用紀錄。 |
|
| 130 |
+
| 65–85 分 | 17 | 各自完成個人報告。 |
|
| 131 |
+
| 85–90 分 | 18 | 準備展示一張圖或一組數字。 |
|
| 132 |
+
| 90–115 分 | 18–19 | 個人分享與提問。 |
|
| 133 |
+
| 115–120 分 | 20 | 確認檔案與結果,正式提交。第 21 頁為後續延伸。 |
|
| 134 |
+
|
| 135 |
+
8 人以內可每人 2 分鐘分享、1 分鐘提問。超過 8 人,改每人 1 分鐘分享、30 秒提問;若人數超過這段時間能容納的數量,另排展示時段,課內仍逐一抽問數字與程式,不用改成小組報告。
|
| 136 |
+
|
| 137 |
+
## AI 使用與評量
|
| 138 |
+
|
| 139 |
+
允許使用外部 AI。課堂要求學生能指出自己的資料、說明一段相關程式,並實際核對數字。AI 使用紀錄寫一件具體發生的事即可,不要求抄整段對話;未使用者如實填寫。
|
| 140 |
+
|
| 141 |
+
最低完成標準:
|
| 142 |
+
|
| 143 |
+
- 問題能由資料中的欄位回答。
|
| 144 |
+
- main.py 可以執行,結果能重新出現。
|
| 145 |
+
- REPORT.md 有來源、方法、原始數字與答案。
|
| 146 |
+
- 至少核對一組結果;相減時核對兩個原始數字。
|
| 147 |
+
- 學生能親自解釋結果,說出資料尚不能回答的一件事。
|
| 148 |
+
|
| 149 |
+
評量比重可維持問題與範圍 20%、資料整理 20%、計算與核對 25%、圖表與說明 20%、個人分享與 AI 使用紀錄 15%。沒有使用 AI 不扣分,程式碼長度與圖表華麗程度不作為主要評分。
|
| 150 |
+
|
| 151 |
+
## 修改教材
|
| 152 |
+
|
| 153 |
+
學生文字與例題在 tools/lesson_content.mjs。tools/build_slides.mjs 產生可編輯的正文、表格與圖表,背景及下載頁截圖放在 slides/assets。修改後重新輸出,再逐頁檢查;不要只修改 PowerPoint 卻忘記同步範例程式與教師流程。
|
classroom_data/curriculum/slides/Lesson19_資料偵探提出好問題.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5c5d8875e55eb4ba22397e6a2c90ad506f7c1e43734e812b2386688fe6752acc
|
| 3 |
+
size 56045
|
classroom_data/curriculum/slides/Lesson20_把資料整理成證據.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ecf08226553e256c91f6bba7a99e67c255fae57a546e57c75431a964dae03cc2
|
| 3 |
+
size 55876
|
classroom_data/curriculum/slides/Lesson21_找出差異圖表教學簡報.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4228baa8c7614666bdfa89ff28354bcd685ce9376742540e61812ad013572216
|
| 3 |
+
size 58187
|
classroom_data/curriculum/slides/Lesson22_資料故事發表會.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ad01fd550d4a3e209649a3512229a26cb33232d1227669942438cf19ff015749
|
| 3 |
+
size 56564
|
classroom_data/curriculum/slides/assets/README.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 投影片素材
|
| 2 |
+
|
| 3 |
+
- `telearn-background.png`:從教師提供的 `Level3_0_1電腦科學基礎I.pdf` 擷取共同背景,保留原本得能標誌、漸層與頁尾圖樣。原頁的教學文字與插圖不列入新課程。
|
| 4 |
+
- `data-download-page.png`:2026-10-17 擷取政府資料開放平臺 <https://data.gov.tw/dataset/40121> 的「資料資源下載網址」區域,供課堂說明 CSV 下載位置。
|
| 5 |
+
|
| 6 |
+
投影片正文、程式碼、表格與圖表均以可編輯物件重建;參考 PDF 的教學內容沒有沿用。
|
classroom_data/curriculum/slides/assets/data-download-page.png
ADDED
|
Git LFS Details
|
classroom_data/curriculum/slides/assets/telearn-background.png
ADDED
|
Git LFS Details
|
classroom_data/curriculum/slides/data.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4374fef0f6fe55fddfbb07ce9d05f7ebfce8a273bef5351cf8e2a103c21ce3db
|
| 3 |
+
size 7741502
|
classroom_data/curriculum/slides/data/Lesson19_資料偵探提出好問題_學生版修訂.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5c33861a50694104dd6cb8152095d7993640178c107c928c1be0d78a97631b32
|
| 3 |
+
size 2158186
|
classroom_data/curriculum/slides/data/Lesson20_先檢查再相信數字_學生版修訂.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1dfabee63aa5860723efba25b6177b5b42f8a6be0a03e2f070358eb4b8662b3b
|
| 3 |
+
size 1910079
|
classroom_data/curriculum/slides/data/Lesson21_找差異讓圖表說清楚_學生版修訂.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f718dd322d2df1b0824d367968c475b766cea0c90e12b7f485a41e11e54885b6
|
| 3 |
+
size 1986245
|
classroom_data/curriculum/slides/data/Lesson22_分享你的資料發現_學生版修訂.pptx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8e10d4345b901d3e95d9de9766caa7139ac5e4b283fd4689bf8c9fe3ba4a5801
|
| 3 |
+
size 1744900
|
classroom_data/curriculum/slides/previews/Lesson19_資料下載預覽.png
ADDED
|
Git LFS Details
|
classroom_data/curriculum/tools/build_slides.mjs
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fs from 'node:fs/promises';
|
| 2 |
+
import path from 'node:path';
|
| 3 |
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
| 4 |
+
import { decks } from './lesson_content.mjs';
|
| 5 |
+
import { importRuntimeModule } from '/Users/steveyeh/.codex/plugins/cache/openai-primary-runtime/presentations/26.1013.11914/skills/presentations/container_tools/runtime_helpers.mjs';
|
| 6 |
+
|
| 7 |
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
|
| 8 |
+
const build = path.join(root, '.presentation-build');
|
| 9 |
+
const assets = path.join(root, 'classroom_data/curriculum/slides/assets');
|
| 10 |
+
const skill = '/Users/steveyeh/.codex/plugins/cache/openai-primary-runtime/presentations/26.1013.11914/skills/presentations';
|
| 11 |
+
const { Presentation, PresentationFile } = await importRuntimeModule('@oai/artifact-tool');
|
| 12 |
+
const { finalizePresentation, applyPresentationChartFont } = await import(pathToFileURL(path.join(skill, 'container_tools/artifact_tool_utils.mjs')));
|
| 13 |
+
const PYTHON = '/Users/steveyeh/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/bin/python3';
|
| 14 |
+
const FONT = 'PingFang TC';
|
| 15 |
+
const PURPLE = '#351C75';
|
| 16 |
+
const RED = '#E00000';
|
| 17 |
+
const SOURCE = '教育部統計處「各級學校縣市別學生人數」。資料集:https://data.gov.tw/dataset/40121 ;原始 CSV:https://stats.moe.gov.tw/files/opendata/edu_B_1_4.csv 。數字以課堂附檔 data/students_by_county.csv 的 104–114 學年度快照為準。';
|
| 18 |
+
const background = await fs.readFile(path.join(assets, 'telearn-background.png'));
|
| 19 |
+
const downloadImage = await fs.readFile(path.join(assets, 'data-download-page.png'));
|
| 20 |
+
const asArrayBuffer = b => b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
|
| 21 |
+
await fs.mkdir(path.join(build, 'final'), { recursive: true });
|
| 22 |
+
|
| 23 |
+
function text(slide, value, x, y, w, h, size = 32, opts = {}) {
|
| 24 |
+
const shape = slide.shapes.add({geometry:'textbox', position:{left:x,top:y,width:w,height:h}, fill:'none', line:{fill:'none',width:0}});
|
| 25 |
+
shape.text = value.replace(/\{\{|\}\}/g,'');
|
| 26 |
+
shape.text.style = {typeface:opts.mono?'Menlo':FONT, fontSize:size, color:opts.color||'#000000', bold:opts.bold||false, alignment:opts.align||'left', verticalAlignment:'top', insets:0, autoFit:'none'};
|
| 27 |
+
for(const m of value.matchAll(/\{\{(.*?)\}\}/g)) shape.text.get(m[1]).fill = RED;
|
| 28 |
+
return shape;
|
| 29 |
+
}
|
| 30 |
+
function length(value) {return [...value.replace(/\{\{|\}\}/g,'')].reduce((n,c)=>n+(/[\x00-\x7f]/.test(c)?0.54:1),0);}
|
| 31 |
+
function paraHeight(value, width, size) {
|
| 32 |
+
const lines=value.split('\n').reduce((n,l)=>n+Math.max(1,Math.ceil(length(l)/(width/size))),0);
|
| 33 |
+
return Math.ceil(lines*size*1.36+5);
|
| 34 |
+
}
|
| 35 |
+
function paragraphs(slide, values, start=230, size=32, width=1110, gap=24) {
|
| 36 |
+
let y=start;
|
| 37 |
+
for(const value of values) {
|
| 38 |
+
const h=paraHeight(value,width,size);
|
| 39 |
+
text(slide,value,80,y,width,h,size);
|
| 40 |
+
y+=h+gap;
|
| 41 |
+
}
|
| 42 |
+
if(y-gap>635) throw new Error(`Body too long: ${y-gap}px; ${values.join(' / ')}`);
|
| 43 |
+
}
|
| 44 |
+
function addTable(slide, spec) {
|
| 45 |
+
text(slide,spec.intro,80,219,1110,68,27);
|
| 46 |
+
const cols=spec.values[0].length;
|
| 47 |
+
const h=Math.min(304,spec.values.length*56);
|
| 48 |
+
const table=slide.tables.add({rows:spec.values.length,columns:cols,left:100,top:292,width:1060,height:h,values:spec.values,...(spec.widths?{columnWidths:spec.widths}:{columnTracks:Array.from({length:cols},()=>({mode:'fr',value:1}))})});
|
| 49 |
+
table.borders.assign({fill:'#777777',width:1,style:'solid'});
|
| 50 |
+
table.cells.block({row:0,column:0,rowCount:spec.values.length,columnCount:cols}).assign({fill:'#FFFFFF',textStyle:{typeface:FONT,fontSize:27,color:'#000000'},margins:{left:12,right:10,top:7,bottom:6},anchor:'center'});
|
| 51 |
+
for(let r=0;r<spec.values.length;r++) for(let c=0;c<cols;c++) {
|
| 52 |
+
const cell=table.getCell(r,c);
|
| 53 |
+
cell.text.style={typeface:FONT,fontSize:27,color:'#000000',bold:r===0,verticalAlignment:'middle',alignment:'left'};
|
| 54 |
+
if(r===0)cell.fill='#E5E5E5';
|
| 55 |
+
}
|
| 56 |
+
if(spec.after)text(slide,spec.after,80,Math.max(568,292+h+20),1110,66,25);
|
| 57 |
+
}
|
| 58 |
+
function addCode(slide,spec) {
|
| 59 |
+
const introH=paraHeight(spec.intro,1110,28);
|
| 60 |
+
text(slide,spec.intro,80,220,1110,introH,28);
|
| 61 |
+
const top=220+introH+20;
|
| 62 |
+
const lines=spec.code.split('\n').length;
|
| 63 |
+
const size=lines>7?25:28;
|
| 64 |
+
const h=lines*size*1.32+8;
|
| 65 |
+
text(slide,spec.code,100,top,1090,h,size,{mono:true});
|
| 66 |
+
if(spec.after) {
|
| 67 |
+
const afterTop=top+h+20;
|
| 68 |
+
const afterH=paraHeight(spec.after,1110,27);
|
| 69 |
+
if(afterTop+afterH>636)throw new Error(`Code too tall: ${spec.title} ${afterTop+afterH}`);
|
| 70 |
+
text(slide,spec.after,80,afterTop,1110,afterH,27);
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
function addDownload(slide) {
|
| 74 |
+
const url=text(slide,'https://data.gov.tw/dataset/40121',80,221,1100,45,31,{color:'#184E98'});
|
| 75 |
+
url.text.get('https://data.gov.tw/dataset/40121').link={uri:'https://data.gov.tw/dataset/40121',isExternal:true};
|
| 76 |
+
slide.images.add({blob:asArrayBuffer(downloadImage),contentType:'image/png',alt:'官方資料頁的資料資源下載區,左側 CSV 按鈕用於下載檔案',position:{left:70,top:290,width:1140,height:225},fit:'contain'});
|
| 77 |
+
text(slide,'找到「資料資源下載網址」,按左側的 CSV ��鈕。\n下載後會得到 edu_B_1_4.csv。旁邊的「檢視資料」是檔案說明。',80,548,1110,85,28);
|
| 78 |
+
}
|
| 79 |
+
function addChart(slide) {
|
| 80 |
+
const chart=slide.charts.add('bar',{
|
| 81 |
+
position:{left:90,top:230,width:1080,height:350},
|
| 82 |
+
title:'114 學年度各縣市國小學生人數(前 5 名,單位:人)',titleTextStyle:{fontSize:27,fill:'#000000'},
|
| 83 |
+
categories:['臺北市','高雄市','桃園市','臺中市','新北市'],
|
| 84 |
+
series:[{name:'學生人數(人)',values:[117650,127327,132964,161332,194670],fill:PURPLE}],
|
| 85 |
+
barOptions:{direction:'bar',grouping:'clustered',gapWidth:70},hasLegend:false,
|
| 86 |
+
xAxis:{textStyle:{fontSize:24,fill:'#000000'}},
|
| 87 |
+
yAxis:{min:0,max:220000,majorUnit:50000,numberFormatCode:'#,##0',textStyle:{fontSize:21,fill:'#000000'},majorGridlines:{fill:'#CCCCCC',width:1}},
|
| 88 |
+
dataLabels:{showValue:true,position:'outEnd',numberFormatCode:'#,##0',textStyle:{fontSize:22,fill:'#000000'}},
|
| 89 |
+
chartFill:'#FFFFFF',plotAreaFill:'#FFFFFF',
|
| 90 |
+
});
|
| 91 |
+
applyPresentationChartFont(chart,{fontFamily:FONT});
|
| 92 |
+
text(slide,'資料來源:教育部統計處「各級學校縣市別學生人數」',80,602,1110,38,23);
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
for(const deck of decks.filter(d => !process.argv[2] || d.lesson === Number(process.argv[2]))) {
|
| 96 |
+
const p=Presentation.create({slideSize:{width:1280,height:720}});
|
| 97 |
+
const tables=[],charts=[];
|
| 98 |
+
for(const [index,spec] of deck.slides.entries()) {
|
| 99 |
+
const slide=p.slides.add();
|
| 100 |
+
slide.images.add({blob:asArrayBuffer(background),contentType:'image/png',alt:'得能資訊教育課程版型',position:{left:0,top:0,width:1280,height:720},fit:'fill'});
|
| 101 |
+
text(slide,'得能資訊教育 - 專題課程',32,665,800,30,21,{bold:true,color:PURPLE});
|
| 102 |
+
text(slide,String(index+1),1168,643,48,28,21,{bold:true,color:PURPLE,align:'center'});
|
| 103 |
+
if(spec.kind==='cover') {
|
| 104 |
+
text(slide,spec.title,100,300,1080,100,66,{bold:true,color:PURPLE,align:'center'});
|
| 105 |
+
text(slide,spec.subtitle,100,418,1080,80,40,{align:'center'});
|
| 106 |
+
} else {
|
| 107 |
+
text(slide,spec.title,50,77,990,72,48,{bold:true,color:PURPLE});
|
| 108 |
+
if(spec.kind==='text')paragraphs(slide,spec.paras);
|
| 109 |
+
if(spec.kind==='exercise') {
|
| 110 |
+
text(slide,spec.label,80,218,1110,45,31);
|
| 111 |
+
paragraphs(slide,spec.paras,278,31,1110,23);
|
| 112 |
+
}
|
| 113 |
+
if(spec.kind==='code')addCode(slide,spec);
|
| 114 |
+
if(spec.kind==='table'){addTable(slide,spec);tables.push(index+1);}
|
| 115 |
+
if(spec.kind==='download')addDownload(slide);
|
| 116 |
+
if(spec.kind==='chart'){addChart(slide);charts.push(index+1);}
|
| 117 |
+
}
|
| 118 |
+
slide.speakerNotes.text=[spec.note||'依投影片說明與例子帶課。練習題先讓學生獨立作答,再顯示解答。',SOURCE].join('\n\n');
|
| 119 |
+
}
|
| 120 |
+
const candidatePath=path.join(build,`candidate-${deck.file}`);
|
| 121 |
+
await(await PresentationFile.exportPptx(p)).save(candidatePath);
|
| 122 |
+
const result=await finalizePresentation({workspaceDir:root,candidatePath,finalPath:path.join(build,'final',deck.file),pythonExecutable:PYTHON,
|
| 123 |
+
integrityValidatorPath:path.join(skill,'container_tools/inspect_presentation_package_integrity.py'),
|
| 124 |
+
layoutValidatorPath:path.join(skill,'container_tools/inspect_presentation_layout_geometry.py'),
|
| 125 |
+
layoutArgs:['--expected-slide-size-emu','12192000,6858000','--validate-bullet-geometry','--validate-heading-fit',...tables.flatMap(n=>['--require-native-table-slide',String(n)])],
|
| 126 |
+
explicitTotalSlideCount:deck.slides.length,requiredNativeTableOwnerSlides:tables,requiredNativeChartOwnerSlides:charts,
|
| 127 |
+
...(charts.length?{materializeLiteralChartWorkbooks:true}:{}),fontPolicy:{basis:'design',families:[FONT,'Menlo']},verifyArtifactToolImport:true,
|
| 128 |
+
receiptPath:path.join(build,`Lesson${deck.lesson}.validation.json`),
|
| 129 |
+
});
|
| 130 |
+
console.log(JSON.stringify({lesson:deck.lesson,slides:deck.slides.length,file:deck.file,package:result.packageIntegrity.status,layout:result.presentationLayout.finding_count}));
|
| 131 |
+
}
|
classroom_data/curriculum/tools/lesson_content.mjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const text = (title, paras, note = '') => ({ kind: 'text', title, paras, note });
|
| 2 |
+
const code = (title, intro, code, after = '', note = '') => ({ kind: 'code', title, intro, code, after, note });
|
| 3 |
+
const table = (title, intro, values, after = '', note = '', widths) => ({ kind: 'table', title, intro, values, after, note, widths });
|
| 4 |
+
const exercise = (title, label, paras, note = '') => ({ kind: 'exercise', title, label, paras, note });
|
| 5 |
+
const cover = (title, subtitle) => ({ kind: 'cover', title, subtitle });
|
| 6 |
+
|
| 7 |
+
export const decks = [
|
| 8 |
+
{
|
| 9 |
+
lesson: 19,
|
| 10 |
+
title: '資料分析 I|公開資料與問題',
|
| 11 |
+
file: 'Lesson19_資料偵探提出好問題_學生版修訂.pptx',
|
| 12 |
+
slides: [
|
| 13 |
+
cover('資料分析 I', '公開資料與問題'),
|
| 14 |
+
exercise('學生人數', '[問題]', ['哪個縣市的國小學生最多?', '你會去哪裡找答案?'], '0–10 分。讓學生各自回答。暫時不公布排名,請他們說出答案的來源。'),
|
| 15 |
+
table('學生人數', '這是同一份資料裡的三筆紀錄。', [['學年度','縣市別','國小[人]'],['104','新北市','201970'],['114','新北市','194670'],['114','臺中市','161332']], '同一個縣市,可以比較不同學年度;同一個學年度,可以比較不同縣市。', '用這張表說明後面要做的兩種比較。原始 CSV 的數字沒有千分位逗號。'),
|
| 16 |
+
text('公開資料', ['政府機關把整理好的資料公開,提供大家下載使用,稱為{{公開資料}}。', '這次使用教育部統計處的「各級學校縣市別學生人數」。\n裡面有 104 到 114 學年度、22 個縣市的學生人數。'], '10–20 分。說明提供資料的機關與資料平台不同:教育部統計處整理資料,政府資料開放平臺提供查找入口。'),
|
| 17 |
+
{ kind:'download', title:'資料下載', note:'20–30 分。開啟網址,現場指出提供機關、CSV 按鈕和檢視資料的差別。CSV 按鈕下載檔案;檢視資料開啟資源說明視窗,不是試算表預覽。課堂專案已附同一份資料,下載只需示範一次。' },
|
| 18 |
+
code('資料下載', '下載的檔名是 edu_B_1_4.csv。課堂專案把它改名為 students_by_county.csv,放在 data 資料夾。', 'week19/\n├── main.py\n├── REPORT.md\n├── DATA_SOURCE.md\n└── data/\n └── students_by_county.csv', '專案裡已經有這些檔案,可以直接使用。', '說明檔案位置。這裡的 week19 是四週共用的工作區;資料夾名稱以課堂系統顯示為準。'),
|
| 19 |
+
text('CSV', ['CSV 是儲存表格資料的一種文字格式。', '{{每一行}}是一筆紀錄,{{逗號}}用來分開不同欄位。\n用試算表開啟時,就會排成表格。', '第一行通常是欄位名稱。這份檔案共有 15 個欄位,\n例如「學年度」、「縣市別」和「國小[人]」。'], '30–40 分。可開啟原始 CSV,再切到試算表或系統文字檢視,讓學生比較同一檔案的兩種樣子。'),
|
| 20 |
+
code('CSV', '下面只取原始檔的三個欄位。', '學年度,縣市別,國小[人]\n104,新北市,201970\n114,新北市,194670\n114,臺中市,161332', '在這個例子裡,每一列代表「某個學年度、某個縣市」的國小學生人數。'),
|
| 21 |
+
exercise('資料欄位', '[例1]', ['114 學年度,新北市有多少位國小學生?', '請指出要用的欄位,以及符合條件的那一列。'], '給 2 分鐘。讓學生先口頭或寫下答案,再切到下一頁。'),
|
| 22 |
+
table('資料欄位', '[例1 解答]', [['學年度','縣市別','國小[人]'],['114','新北市','194670']], '學年度是 114、縣市別是新北市。讀取「國小[人]」這一欄,答案是 194,670 人。', '這裡強調找列與取欄是不同動作。'),
|
| 23 |
+
text('學年度', ['這份資料的學年度寫成 104、105,一直到 114。', '程式要查 114 學年度,就填{{114}}。如果填 2025,會找不到符合條件的資料。', '「國小[人]」的 [人] 是單位,不是另一個欄位。程式裡的欄位名稱要和 CSV 完全一樣。']),
|
| 24 |
+
text('分析問題', ['「學生是不是越來越少?」還不夠清楚:哪個地方?哪一種學校?比較哪幾年?', '把問題寫成:{{新北市的國小學生人數,104 到 114 學年度增加還是減少?}}', '這樣就知道要選哪兩筆資料,也知道要做什麼計算。'], '40–50 分。從原問題逐項補出地區、教育階段和年份,不把這些詞當成需要背的術語。'),
|
| 25 |
+
exercise('分析問題', '[練習1]', ['從下面兩題選一題,換成你想看的縣市或教育階段。', '1. 114 學年度,哪個縣市的國小學生最多?', '2. 新北市的國小學生,104 和 114 學年度相差幾人?'], '每個學生各自選題。這兩題是基本題,先讓所有人有可執行的起點。'),
|
| 26 |
+
text('分析問題', ['也可以比較同一個學年度的不同教育階段。', '[例] 114 學年度,新北市的國小和國中學生相差幾人?', '想進一步比較各縣市的變化,可以挑戰:104 到 114 學年度,哪個縣市的國小學生增加最多?'], '最後一題需要對多個縣市重複計算,是選做題。50–60 分休息。'),
|
| 27 |
+
text('資料能回答的範圍', ['這份資料有年份、縣市和學生人數,可以回答人數多少、差多少、增加或減少。', '「學生人數為什麼減少?」需要其他資料。這張表沒有出生人數、人口遷移或每間學校的資料。'], '60–65 分。這是分析範圍的解釋,不要求學生為每一題寫一大段限制。'),
|
| 28 |
+
code('Python 讀取 CSV', 'main.py 裡的 load_rows() 負責讀取檔案。', 'def load_rows(path=DATA_FILE):\n with open(path, encoding="utf-8-sig", newline="") as file:\n return list(csv.DictReader(file))', 'DictReader 會把欄位名稱當作 key,每一列變成一個 dictionary。', '65–75 分。只講這三行的用途。utf-8-sig 用於讀取附帶 BOM 的 UTF-8 檔案;不必在這週展開編碼課程。'),
|
| 29 |
+
code('Python 讀取 CSV', '例如 rows[0] 是讀進來的第一筆資料。以下只顯示其中三個欄位。', '{\n "學年度": "104",\n "縣市別": "新北市",\n "國小[人]": "201970"\n}', '用 rows[0]["縣市別"] 可以取得「新北市」。數字目前也先以文字讀入,下一週會處理。'),
|
| 30 |
+
code('執行範例', '打開 main.py。檔案上方這兩個設定,決定要查哪個學年度、哪種學生人數。', 'SAMPLE_YEAR = "114"\nSAMPLE_FIELD = "國小[人]"', '按「執行」,到 Console 查看結果。', '75–80 分。先確認每位學生能執行。如果找不到檔案,核對 data/students_by_county.csv 的位置。'),
|
| 31 |
+
table('執行範例', 'Console 會顯示「讀入 242 列資料」,並列出各縣市的排名。前五名如下。', [['縣市','114 學年度國小學生人數(人)'],['新北市','194,670'],['臺中市','161,332'],['桃園市','132,964'],['高雄市','127,327'],['臺北市','117,650']], '回到 CSV 找新北市 114 學年度的那一列,確認人數相同。', '若網站未來更新資料,課堂仍以附檔快照為準;不要用即時下載的新版本和舊範例混算。', [360,700]),
|
| 32 |
+
code('修改查詢條件', '把 SAMPLE_YEAR 改成 "104",其他設定維持原樣。', 'SAMPLE_YEAR = "104"\nSAMPLE_FIELD = "國小[人]"', '執行後,新北市是 201,970 人。這次查到的是 104 學年度,不是程式算錯。'),
|
| 33 |
+
exercise('修改查詢條件', '[練習2]', ['改查 114 學年度的國中學生人數,寫下前 3 名。', '你需要修改哪兩個設定?', '到 CSV 核對其中一個縣市的人數。'], '80–90 分。基本題做完才換成其他教育階段。下一頁是答案。'),
|
| 34 |
+
table('修改查詢條件', '[練習2 解答] SAMPLE_YEAR = "114",SAMPLE_FIELD = "國中[人]"', [['排名','縣市','國中學生人數(人)'],['1','新北市','96,668'],['2','臺中市','82,767'],['3','臺北市','68,231']], '國小和國中的前 3 名並不完全相同。問題換了,要使用的欄位也會改變。'),
|
| 35 |
+
exercise('AI 與分析問題', '[練習3]', ['把你寫的問題和這份 CSV 的欄位名稱交給 AI,請它指出需要哪幾欄。', '如果它提到資料裡沒有的欄位,回去修改問題,或請它說明還缺哪份資料。', '在 REPORT.md 寫下最後選定的問題。'], '90–100 分。不要只問 AI「幫我做專題」。教師抽問學生需要哪個欄位,確認不是照抄不理解的題目。'),
|
| 36 |
+
text('個人專題', ['把你的問題、資料名稱、來源網址和比較的年份寫進 REPORT.md。', '執行 main.py,記下一個和問題有關的數字,並在 CSV 裡找到它。', '完成後存檔,提交這週的進度。下週沿用同一份專案。'], '100–120 分。教師逐一看問題、執行結果與來源。進度較快者改比較另一個縣市或教育階段;不用新增另一個專題。'),
|
| 37 |
+
],
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
lesson:20,
|
| 41 |
+
title:'資料分析 II|資料整理',
|
| 42 |
+
file:'Lesson20_先檢查再相信數字_學生版修訂.pptx',
|
| 43 |
+
slides:[
|
| 44 |
+
cover('資料分析 II','資料整理'),
|
| 45 |
+
exercise('上週的程式','[複習]',['上週選了什麼問題?程式目前查的是哪個學年度、哪個欄位?','打開同一份專案,執行 main.py。'], '0–10 分。教師從學生自己的設定開始,不換新的資料集。'),
|
| 46 |
+
code('文字與數字','CSV 讀進來的內容通常是文字。例如:','value = "194670"\nprint(type(value))\n\n# <class \'str\'>','畫面上看起來像數字,程式仍然把它當成文字。'),
|
| 47 |
+
exercise('文字與數字','[例1]',['下面兩行的結果一樣嗎?','print("12" + "3")','print(12 + 3)'], '10–15 分。讓學生先寫結果,再執行或看解答。'),
|
| 48 |
+
code('文字與數字','[例1 解答]','print("12" + "3") # 123\nprint(12 + 3) # 15\nprint(int("12") + int("3")) # 15','文字相加會接在一起;int() 可以把數字文字轉成整數。'),
|
| 49 |
+
code('字串整理','下面是一個帶空格和千分位逗號的練習字串。','value = " 201,970 "\ncleaned = value.strip().replace(",", "")\nprint(cleaned)\nprint(int(cleaned))','strip() 去掉前後空白,replace(",", "") 把逗號換成空字串。', '15–25 分。附檔原始數字沒有逗號;這裡是自訂練習,不暗示原始檔有格式錯誤。'),
|
| 50 |
+
code('字串整理','如果欄位裡沒有填數字,int() 會發生什麼事?','int("")\n\n# ValueError: invalid literal for int() ...','空字串不能直接轉成整數。程式要先決定這種情況怎麼處理。'),
|
| 51 |
+
table('0 與缺少資料','下面是練習資料,不代表原始檔裡一定有這些問題。',[['欄位內容','可能的意思','本次程式的處理'],['0','人數確實為 0','保留數字 0'],['空白','沒有填寫數字','以 None 表示未知'],['-','需要查看原資料的註記','暫時記為 None']], '未知和 0 的意思不同。不能因為程式比較好寫,就把未知都改成 0。','25–35 分。真實附檔中宗教研修學院欄位有 0,這些 0 不需要刪除。',[170,430,460]),
|
| 52 |
+
text('None',['Python 用 {{None}} 表示沒有取得數值。', '例如一個縣市的資料沒有填,不能說這個縣市「沒有學生」。這時保留 None,後面的計算可以略過它,並記下缺少哪一筆。', '這份課堂 CSV 有 242 列、15 個欄位,沒有空白格。下面的空白和「-」是用來測試程式的例子。']),
|
| 53 |
+
code('parse_count()','main.py 裡已附這個函式。前半段整理字串,並檢查空白或「-」。','def parse_count(value):\n cleaned = (value or "").strip().replace(",", "")\n if not cleaned or cleaned == "-":\n return None','如果是空白或「-」,先回傳 None,不繼續轉成整數。', '35–45 分。逐行對照上一頁測試值,不要求學生默寫。'),
|
| 54 |
+
code('parse_count()','後半段嘗試轉成整數。無法轉換時,回傳 None。',' try:\n return int(cleaned)\n except ValueError:\n return None','try / except 讓程式能處理預期中的轉換錯誤。原始值仍留在 CSV 裡。'),
|
| 55 |
+
exercise('parse_count()','[練習1]',['用 parse_count() 測試下面五個值。執行前,寫下你預期的結果。','"0" " 123 " "1,234" "" "-"','在 main() 裡、rows = load_rows() 下方加入測試,例如:\n print(parse_count("1,234"))'],'45–50 分測試,50–60 分休息。保留 main() 原本內容,只加入測試輸出。'),
|
| 56 |
+
table('parse_count()','[練習1 解答]',[['輸入的值','回傳結果'],['"0"','0'],['" 123 "','123'],['"1,234"','1234'],['""','None'],['"-"','None']], '檢查結果時,也要看資料型態:0 是數字;None 代表沒有數值。'),
|
| 57 |
+
code('資料筆數與欄位','在 main() 裡,rows = load_rows() 下方加入這兩行。',' print(len(rows))\n print(len(rows[0]))\n\n# 242\n# 15','第一行算有幾筆資料;第二行算第一筆資料有幾個欄位。','60–70 分。再次說明標題列不算一筆資料,15 個欄位不等於 15 個縣市。'),
|
| 58 |
+
text('資料筆數與欄位',['資料包含 11 個學年度,每個學年度都有 22 個縣市。', '{{11 × 22 = 242}},和程式讀到的筆數相同。','如果下載到另一個版本,筆數可能不同。這時要回到資料頁查看年份,不能直接刪掉多出來的資料。']),
|
| 59 |
+
table('重複資料','假設遇到這三筆資料,哪些可能重複?',[['學年度','縣市別','國小[人]'],['114','新北市','194670'],['114','臺中市','161332'],['114','新北市','194670']], '在這份資料裡,「學年度+縣市別」應該對應一筆紀錄。上表是刻意製作的練習例子。','70–75 分。原始附檔沒有重複的(學年度、縣市別)組合。'),
|
| 60 |
+
text('重複資料',['同一個縣市在不同學年度出現,是正常的,不算重複。','同一個學年度、同一個縣市出現兩次,才需要回去查明來源。也可能是一筆被重複貼上,或混用了兩個版本。','不要把這兩筆直接加起來,否則人數可能變成兩倍。']),
|
| 61 |
+
code('查詢條件','這個函式先挑出指定學年度,再依人數排序。','results = count_by_county(rows, "114", "國小[人]")\nprint(results[0])\n\n# ("新北市", 194670)','這次固定 114 學年度,只比較各縣市的國小學生人數。'),
|
| 62 |
+
table('數字核對','把程式的結果和原始 CSV 放在一起看。',[['要核對的項目','CSV 裡的內容'],['學年度','114'],['縣市別','新北市'],['欄位','國小[人]'],['人數','194670']], '程式也應該得到 194,670。四個條件都相同,才是在核對同一筆資料。','75–85 分。教師示範一筆,學生再自行核對三筆。',[330,730]),
|
| 63 |
+
exercise('數字核對','[練習2]',['從自己的結果選 3 個縣市,在 CSV 找到對應的人數。','在 REPORT.md 記下學年度、縣市、欄位名稱和人數。','如果數字不同,從年份和欄位名稱開始查。'], '85–95 分。這個活動每人獨立進行;教師看學生找到的原始列。'),
|
| 64 |
+
exercise('AI 與錯誤訊息','[練習3]',['把 ValueError: invalid literal for int() 這段錯誤交給 AI,請它解釋哪種輸入會造成錯誤。','用自己的程式實際測試其中一個例子。','記下輸入值、原本的錯誤,以及 parse_count() 的回傳結果。'],'95–105 分。不需要上傳整份檔案或帳號資訊;錯誤訊息和幾行相關程式即可。'),
|
| 65 |
+
exercise('資料檢查','[挑戰]',['parse_count("-12") 會得到什麼?','能轉成整數,就一定是合理的學生人數嗎?','如果要排除負的人數,條件應該加在哪裡?'],'105–110 分選做。答案為 -12;型態正確不代表符合資料意義。不要強迫所有人完成新功能。'),
|
| 66 |
+
text('整理紀錄',['在 REPORT.md 記下哪些值能使用、哪些值無法計算,\n並說明你的處理方式。','加入你核對過的 3 筆資料。原始 CSV 保持原樣,整理方式寫在程式裡。','存檔後再執行一次,確認你的問題仍然查得到結果,提交本週進度。'],'110–120 分。核對基本測試、三筆紀錄與可執行程式;快速學生可完成負值檢查。'),
|
| 67 |
+
],
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
lesson:21,
|
| 71 |
+
title:'資料分析 III|比較與圖表',
|
| 72 |
+
file:'Lesson21_找差異讓圖表說清楚_學生版修訂.pptx',
|
| 73 |
+
slides:[
|
| 74 |
+
cover('資料分析 III','比較與圖表'),
|
| 75 |
+
exercise('比較資料','[複習]',['你的問題要找人數最多的地方,還是比較某個地方的增減?','找出上週核對過的數字。'],'0–10 分。按學生自己的題目分別說明排名和跨年比較。'),
|
| 76 |
+
table('人數變化','新北市的國小學生人數。',[['學年度','學生人數(人)'],['104','201,970'],['114','194,670']], '人數減少了多少?','10–15 分。讓學生口算或用計算機算差值。'),
|
| 77 |
+
text('人數變化',['以後來的人數減去原來的人數:','194,670 − 201,970 = {{−7,300}}','負數表示減少,因此可以寫「新北市的國小學生人數,在 104 到 114 學年度之間減少 7,300 人」。']),
|
| 78 |
+
exercise('人數變化','[例1]',['臺中市的國小學生人數:\n104 學年度是 157,272 人,114 學年度是 161,332 人。','這段期間增加或減少了多少人?'],'15–20 分。每人各自計算,下一頁公布答案。'),
|
| 79 |
+
text('人數變化',['[例1 解答]','161,332 − 157,272 = {{4,060}}','臺中市的國小學生人數,在 104 到 114 學年度之間增加 4,060 人。']),
|
| 80 |
+
text('百分比',['少了 100 人,原來有 1,000 人和原來有 10,000 人,影響一樣大嗎?','要比較變化的程度,可以把增減人數除以{{原來的人數}},再乘以 100%。','原來有 1,000 人,少了 100 人是少 10%;原來有 10,000 人,少了 100 人是少 1%。'],'20–30 分。百分比列為選做計算,基本任務仍可用差幾人完成。'),
|
| 81 |
+
text('百分比',['新北市減少 7,300 人,原來有 201,970 人。','−7,300 ÷ 201,970 × 100% ≈ {{−3.6%}}','可以寫「新北市的國小學生人數約減少 3.6%」。這裡的分母是 104 學年度的人數。']),
|
| 82 |
+
exercise('百分比','[挑戰]',['如果原來的人數是 0,還能用剛才的公式算百分比嗎?','程式遇到這種情況,應該顯示什麼?'],'答案:不能除以 0,可以顯示無法計算變化率,並保留原來與後來的數字。不要用 1 代替分母。'),
|
| 83 |
+
table('排名','114 學年度,國小學生人數前 5 名。',[['縣市','人數(人)'],['新北市','194,670'],['臺中市','161,332'],['桃園市','132,964'],['高雄市','127,327'],['臺北市','117,650']], '表格能列出確切數字;圖表則方便比較人數的差距。','30–35 分。資料取自原始 CSV,不是隨意挑選的五個縣市。'),
|
| 84 |
+
{ kind:'chart', title:'長條圖', note:'35–40 分。先讓學生指出最高、第三和最低的長條,再回表格核對數字。前 5 名只是全臺 22 縣市的一部分。' },
|
| 85 |
+
text('圖表標題',['如果標題只寫「學生人數」,讀者不知道是哪一年,也不知道是哪個教育階段。','這張圖的標題是:{{114 學年度各縣市國小學生人數(前 5 名)}}。','人數的單位是「人」。圖表下方也要註明教育部統計處的資料來源。']),
|
| 86 |
+
text('長條圖',['長條的長度代表人數,所以人數軸應該{{從 0 開始}}。','如果把前面一大段截掉,相近的數字就可能看起來差很多。','這張圖只列前 5 名。如果想知道第 6 名,\n要回到完整的排序結果查看。'],'40–45 分。說明尺度與範圍,不延伸到所有圖都必須從零開始。折線圖的尺度另有選擇,本課重點是長條。'),
|
| 87 |
+
code('文字長條圖','課堂系統可以在 Console 用文字畫出長條。','新北市 194,670 | ████████████████████████\n臺中市 161,332 | ████████████████████\n桃園市 132,964 | ████████████████','每一格代表相同人數;旁邊仍保留實際數字。','45–50 分。文字圖不需要安裝額外套件。50–60 分休息。'),
|
| 88 |
+
text('文字長條圖',['把最多人的縣市畫成 24 格,其他縣市照比例縮短。','臺中市的長度:161,332 ÷ 194,670 × 24 ≈ {{20 格}}','因此兩個縣市用的是同一個比例,才能比較。'],'60–65 分。用計算機核對長度。'),
|
| 89 |
+
code('文字長條圖','main.py 裡的 print_text_chart() 使用下面的計算方式。','bar_length = round(value / largest * 24) if largest else 0\nprint(f"{county} {value:,} | {\'█\' * bar_length}")','largest 是這次結果中最多的人數。人數很少時可能四捨五入成 0 格,因此旁邊仍保留實際數字。','所有結果都是 0 時,長條也畫 0 格,避免除以 0。'),
|
| 90 |
+
code('取得一筆人數','main.py 已附 get_count()。下面這段加在 main() 裡、rows = load_rows() 之後。','before = get_count(rows, "104", "新北市", "國小[人]")\nafter = get_count(rows, "114", "新北市", "國小[人]")\nprint(before)\nprint(after)','參數依序是資料、學年度、縣市和欄位。\n找不到資料時回傳 None。這裡會得到 201970 和 194670。','65–75 分。這段放在 main() 的 rows = load_rows() 之後,縮排和其他主程式相同。'),
|
| 91 |
+
code('程式計算人數變化','接在剛才的 before 和 after 下面。','if before is None or after is None:\n print("找不到資料,請檢查年份、縣市和欄位")\nelse:\n change = after - before\n print(change)','查到兩筆資料後再相減,結果是 -7300。','教師示範條件判斷,學生依自己的縣市修改。百分比可在 else 區塊內加上 change / before * 100,並另處理 before 為 0 的情況。'),
|
| 92 |
+
exercise('程式計算人數變化','[練習2]',['把縣市改成臺中市,計算 104 到 114 學年度的國小學生人數變化。','程式的結果是否和例1的答案相同?','再換成你自己的專題條件。'],'75–90 分。基本目標是成功得到 4060,再改自己的條件。'),
|
| 93 |
+
text('總數與比例',['新北市有 194,670 位國小學生,這是{{學生總數}}。','如果想問「居民裡有多少比例是國小學生」,還需要新北市的居民總數,這份 CSV 沒有這個欄位。','總數最多,不等於占居民的比例最高。'],'90–95 分。這裡只區分總數與占比,不額外要求學生取得人口資料。'),
|
| 94 |
+
exercise('AI 與資料解釋','[練習3]',['假設 AI 寫出:「因為少子化,新北市國小學生在 104 到 114 學年度之間減少 7,300 人。」','哪一部分可以從這份 CSV 查到?哪一部分還需要其他資料?','把這句話改成這份資料確實能支持的說法。'],'95–100 分。可查到減少 7300;不能只靠這份表證明原因。不要否認少子化可能有影響,只區分證據是否足夠。'),
|
| 95 |
+
exercise('個人專題','[實作]',['依自己的問題完成一張圖,或一個有原始數字的比較結果。','在 REPORT.md 寫出計算方式、結果,以及資料不能回答的一件事。','圖表要有學年度、教育階段、單位和來源。'],'100–115 分。基礎學生可使用既有文字長條圖與排名;熟練者做差值或百分比。'),
|
| 96 |
+
table('個人專題','[延伸] 這三筆都來自新北市的國小學生人數。',[['學年度','國小學生人數(人)'],['104','201,970'],['109','199,521'],['114','194,670']], '只比較起點和終點,看不到中間的變化。可以再取其他學年度,檢查這段期間的走勢。','選做,不要求所有人畫折線圖。題目若談整段趨勢,就不能只憑兩個端點說每年都減少。'),
|
| 97 |
+
text('本週成果',['執行你的專案,確認圖表或計算結果能重新出現。','在 REPORT.md 保留一組原始數字、計算方式和你的說明。','存檔並提交進度。下一週會把這些內容整理成報告。'],'115–120 分。教師看條件、數字和說明是否一致,不以圖表華麗程度評分。'),
|
| 98 |
+
],
|
| 99 |
+
},
|
| 100 |
+
{
|
| 101 |
+
lesson:22,
|
| 102 |
+
title:'資料分析 IV|分析報告',
|
| 103 |
+
file:'Lesson22_分享你的資料發現_學生版修訂.pptx',
|
| 104 |
+
slides:[
|
| 105 |
+
cover('資料分析 IV','分析報告'),
|
| 106 |
+
text('分析報告',['報告要讓別人知道:你問了什麼、用了哪些資料,以及答案怎麼算出來。','報告裡選用和問題有關的數字與圖表,\n並說明計算方式,以及這些結果代表什麼。'],'0–10 分。打開學生自己的 REPORT.md,不重新選題或開新專案。'),
|
| 107 |
+
text('分析報告',['[範例]','我比較了新北市 104 和 114 學年度的國小學生人數。104 學年度有 201,970 人,114 學年度有 194,670 人,相減後減少 7,300 人。','資料來自教育部統計處。這份表可以說明人數變化,但沒有提供變化原因。'],'10–15 分。完整範例先讓學生知道最後要寫成什麼樣子,後面逐段拆開。'),
|
| 108 |
+
text('研究問題',['報告標題可以直接寫:','{{新北市的國小學生人數變化(104–114 學年度)}}','讀者看到標題,就知道比較的地區、教育階段和年份。自己���題目不同,就換成自己的條件。']),
|
| 109 |
+
table('資料來源','在 REPORT.md 的「資料來源與範圍」寫清楚這些內容。',[['項目','本次範例'],['資料名稱','各級學校縣市別學生人數'],['提供機關','教育部統計處'],['資料頁面','https://data.gov.tw/dataset/40121'],['使用範圍','新北市,104 與 114 學年度,國小[人]']], '使用其他公開資料,也要保留原本的資料名稱和網址。','15–20 分。資料已附在專案;來源網址不能只寫 Google 或 AI。',[260,800]),
|
| 110 |
+
text('計算方法',['可以這樣寫:','從資料中找出新北市 104 和 114 學年度的紀錄,讀取「國小[人]」欄位,將 114 學年度的人數減去 104 學年度的人數。','讓讀者照著這段話,也能從 CSV 找到相同的兩個數字。']),
|
| 111 |
+
table('計算結果','原始數字和計算結果都要保留。',[['項目','數值'],['104 學年度','201,970 人'],['114 學年度','194,670 人'],['114 減 104','−7,300 人']], '「減少 7,300 人」是這次比較的答案。若有百分比,另外寫明分母與計算方式。'),
|
| 112 |
+
text('結果解釋',['「新北市國小學生減少 7,300 人」是這份資料可以支持的說法。','「新北市每一間國小都少了學生」則不能從縣市總數判斷。有些學校可能增加,有些可能減少。','分析的範圍是縣市總數,解釋時也要維持這個範圍。'],'20–25 分。用同一個例子說明總數不等於每個個體都如此。'),
|
| 113 |
+
exercise('報告文字','[練習1]',['「臺中市國小學生從 157,272 人變成 161,332 人,所以學生一直增加。」','如果只用了 104 和 114 學年度兩筆資料,這句話哪裡需要修改?'],'25–30 分。可改成「104 到 114 學年度之間增加 4060 人」。不能只憑端點斷言中間每年都增加。'),
|
| 114 |
+
code('重新執行專案','報告完成後,關掉檔案,再從專案重新打開。','main.py\nREPORT.md\nDATA_SOURCE.md\ndata/\n students_by_county.csv','執行 main.py,確認報告裡的數字仍然能算出來。','30–40 分。所有學生各自重跑,教師確認是學生本人操作。'),
|
| 115 |
+
code('找不到檔案','如果出現 FileNotFoundError,檢查 main.py 上方的設定。','DATA_FILE = Path("data/students_by_county.csv")','資料夾是 data,檔名是 students_by_county.csv。\n檢查這兩個名稱是否和專案中的檔案相同。'),
|
| 116 |
+
code('找不到欄位','欄位名稱要依照原始 CSV。','row["國小學生人數"] # 這份 CSV 沒有這個 key\nrow["國小[人]"] # 原始欄位名稱','如果發生 KeyError,回去看 CSV 第一行,不要憑印象重打欄位名稱。'),
|
| 117 |
+
code('找不到資料','如果結果顯示「沒有符合條件的資料」,檢查查詢條件。','SAMPLE_YEAR = "2025" # CSV 沒有這個學年度值\nSAMPLE_YEAR = "114" # CSV 裡的寫法','同樣也要檢查縣市名稱,例如資料裡用「臺中市」,不是「台中市」。'),
|
| 118 |
+
exercise('重新執行專案','[練習2]',['核對自己的報告和程式:年份、縣市、欄位名稱及結果是否相同?','從報告裡選一個數字,在 CSV 裡找到它,並記下它在哪一列。','如果報告用到相減,就把兩個原始數字都找出來。'],'40–50 分。教師巡看學生找到的原始資料。50–60 分休息。'),
|
| 119 |
+
exercise('AI 修改報告','[例]',['你寫:「新北市的國小學生在 104 到 114 學年度減少 7,300 人。」','AI 改成:「新北市因人口外流,國小學生銳減 7,300 人。」','這個修改能直接採用嗎?'],'60–65 分。人口外流不是這份資料提供的原因;「銳減」也帶入強烈評價。讓學生保留確定的地區、年份、數字與變化方向。'),
|
| 120 |
+
text('AI 使用紀錄',['紀錄可以寫成具體的一件事:','「我請 AI 解釋空字串為什麼不能轉成整數。\n它建議先檢查空白。我用空字串和 0 測試,\n結果分別是 None 和 0。」','如果沒有使用 AI,就寫「本次未使用」,不需要補一段沒有發生過的對話。']),
|
| 121 |
+
exercise('個人報告','[實作]',['完成 REPORT.md 裡的問題、資料來源、計算方法、結果和解釋。','把程式輸出或文字圖表放在結果旁邊,並寫出這份資料還不能回答什麼。','使用 AI 的部分,記下自己接受了什麼建議、實際檢查了什麼。'],'65–85 分。學生各自整理。教師對尚未完成者優先協助題目、可執行程式與一組可信數字,先不要增加功能。'),
|
| 122 |
+
text('個人分享',['用約 2 分鐘介紹自己的題目和結果。','展示一張圖或一組數字,說明這些數字從哪來、怎麼算。最後說明這份資料還缺什麼。','分享後,回答一個問題。'],'85–90 分準備,90–115 分分享。最多 8 人時每人 2 分鐘+1 分鐘提問;超過 8 人改每人 1 分鐘+30 秒提問,或分時段分享,確保人人可展示。'),
|
| 123 |
+
exercise('分享提問','[例]',['「如果把 114 學年度換成 104 學年���,排名可能改變嗎?」','「這個負數是什麼意思?」','「你可以指出程式用的是哪個欄位嗎?」'],'作為教師提問頁,可依學生題目選一個問題。不要把問答變成背整段程式。'),
|
| 124 |
+
text('提交專題',['提交前,確認 main.py 能執行,REPORT.md 的結果和程式相同。','資料來源留在 DATA_SOURCE.md,CSV 放在 data 資料夾。','把最後修改的內容存檔,再用課堂系統提交目前版本。'],'115–120 分。評量重點是可回答的題目、來源、數字核對與個人說明。若程式仍有錯,學生先保留錯誤和目前能確認的結果,教師再處理。'),
|
| 125 |
+
exercise('後續專題','[延伸]',['把程式改成可以輸入學年度或縣市,不用每次修改程式碼。','也可以在原來的專題功能裡加入這次整理好的統計結果。'],'選做延伸。四週後回到原專題課程,這頁不列入本次最低完成標準,也不要求本週開發 GUI。'),
|
| 126 |
+
].filter(s => typeof s === 'object'),
|
| 127 |
+
},
|
| 128 |
+
];
|
classroom_data/templates/week19/DATA_SOURCE.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 範例資料來源
|
| 2 |
+
|
| 3 |
+
- 資料集:教育部統計處「各級學校縣市別學生人數」
|
| 4 |
+
- 資料集頁面:https://data.gov.tw/dataset/40121
|
| 5 |
+
- 原始 CSV:`data/students_by_county.csv`
|
| 6 |
+
- 內容:104–114 學年度各縣市各教育階段的彙總學生人數。
|
| 7 |
+
- 檔案共有 242 筆資料、15 個欄位,涵蓋 22 個縣市。
|
| 8 |
+
- 授權:政府資料開放授權條款第 1 版;使用或改作時保留資料來源註記。
|
| 9 |
+
|
| 10 |
+
## 下載同一份資料
|
| 11 |
+
|
| 12 |
+
1. 開啟上方資料集頁面。
|
| 13 |
+
2. 找到「資料資源下載網址」,按左側的 **CSV** 按鈕。旁邊的「檢視資料」是檔案說明。
|
| 14 |
+
3. 下載的檔名是 `edu_B_1_4.csv`。本課把它改名為 `students_by_county.csv`,放在專案的 `data` 資料夾。
|
| 15 |
+
|
| 16 |
+
課堂專案已附檔,不需要重新下載。網站之後可能更新年份;上課範例以本課提供的 104–114 學年度版本為準。
|
| 17 |
+
|
| 18 |
+
這是彙總統計資料。它可以回答「多少人」或「不同地區、學年度的數值如何變化」,不能單獨說明變化的原因。若換成其他公開資料,請把來源網址、下載日期、欄位和單位補進本檔。
|
classroom_data/templates/week19/REPORT.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 我的資料分析
|
| 2 |
+
|
| 3 |
+
## 我想回答的問題
|
| 4 |
+
|
| 5 |
+
<!-- 寫清楚對象、指標和比較條件。 -->
|
| 6 |
+
|
| 7 |
+
## 資料來源與範圍
|
| 8 |
+
|
| 9 |
+
- 資料名稱:
|
| 10 |
+
- 提供機關:
|
| 11 |
+
- 來源網址:
|
| 12 |
+
- 專案裡的檔案位置:
|
| 13 |
+
- 時間範圍:
|
| 14 |
+
- 一列代表什麼:
|
| 15 |
+
- 數字單位:
|
| 16 |
+
|
| 17 |
+
## 我怎麼整理和計算
|
| 18 |
+
|
| 19 |
+
<!-- 寫出使用的學年度、縣市、欄位名稱,以及怎麼計算。資料有空白時,也寫下你的處理方法。 -->
|
| 20 |
+
|
| 21 |
+
## 我找到的證據
|
| 22 |
+
|
| 23 |
+
<!-- 貼上 main.py 的輸出或文字圖表,並寫下你在 CSV 裡核對過的數字。相減時保留兩個原始數字。 -->
|
| 24 |
+
|
| 25 |
+
## 我的結論
|
| 26 |
+
|
| 27 |
+
<!-- 結論必須能由上面的數字支持。 -->
|
| 28 |
+
|
| 29 |
+
## 這份資料還不能回答什麼
|
| 30 |
+
|
| 31 |
+
<!-- 例如:人數有變化,但資料沒有告訴我們原因。 -->
|
| 32 |
+
|
| 33 |
+
## AI 使用紀錄
|
| 34 |
+
|
| 35 |
+
<!-- 寫一件實際發生的事;本次沒有用 AI,就寫「本次未使用」。 -->
|
| 36 |
+
- 我問了什麼:
|
| 37 |
+
- 我採納或修改了什麼:
|
| 38 |
+
- 我如何用資料或程式核對:
|
classroom_data/templates/week19/data/students_by_county.csv
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
學年度,縣市別,幼兒園[人],國小[人],國中[人],高級中等學校-普通科[人],高級中等學校-專業群科[人],高級中等學校-綜合高中[人],高級中等學校-實用技能學程[人],高級中等學校-進修部[人],大專校院(全部計入校本部)[人],大專校院(跨縣市教學計入所在地縣市)[人],宗教研修學院[人],國中小進修部(補校)及大專進修學校及空大[人],特殊教育學校[人]
|
| 2 |
+
104,新北市,73526,201970,116039,40566,41358,5773,1434,6816,161853,154484,138,17150,400
|
| 3 |
+
104,臺北市,47269,118471,75015,60532,39235,4892,530,7152,267363,244091,42,9174,828
|
| 4 |
+
104,桃園市,44367,128444,76299,29529,36060,4300,4963,7623,109869,120426,0,2532,438
|
| 5 |
+
104,臺中市,65946,157272,97447,37771,54951,3027,3423,7885,192593,191962,0,10182,953
|
| 6 |
+
104,臺南市,40731,92570,58160,25795,24022,6146,4557,2813,132736,135744,0,1601,533
|
| 7 |
+
104,高雄市,49321,134949,83539,35600,39574,5499,6502,4915,149988,156110,12,9450,678
|
| 8 |
+
104,宜蘭縣,9791,23227,16014,6409,4899,1471,805,732,14725,17952,0,811,129
|
| 9 |
+
104,新竹縣,13997,36265,19150,4230,5487,2763,858,1436,17525,23665,0,653,230
|
| 10 |
+
104,苗栗縣,10492,29940,18268,5922,8086,2136,1285,1247,21352,21352,0,903,136
|
| 11 |
+
104,彰化縣,24073,68920,42767,11939,19919,4790,2755,3975,38990,38990,0,1309,670
|
| 12 |
+
104,南投縣,9328,25439,17256,4504,8411,870,1236,960,12458,12458,0,555,126
|
| 13 |
+
104,雲林縣,12818,35903,24178,8009,9430,1971,1563,698,26340,27035,0,1145,211
|
| 14 |
+
104,嘉義縣,8175,22434,14440,2708,5244,515,1697,460,25071,30424,0,914,0
|
| 15 |
+
104,屏東縣,13986,39629,26193,8977,10010,1381,855,1299,42083,42203,0,1406,185
|
| 16 |
+
104,臺東縣,4319,11156,7227,513,2349,3224,188,329,5963,6008,0,54,101
|
| 17 |
+
104,花蓮縣,6561,16538,10861,4168,4184,2682,93,673,19610,19562,0,284,170
|
| 18 |
+
104,澎湖縣,1635,3972,2670,1018,1651,0,60,0,2984,2984,0,33,0
|
| 19 |
+
104,基隆市,6091,17341,11496,5081,6555,416,365,656,16077,16077,0,2018,157
|
| 20 |
+
104,新竹市,11697,29694,16662,8793,5764,3630,621,1785,51748,51050,0,249,0
|
| 21 |
+
104,嘉義市,5918,16104,11640,6358,9204,1660,1846,842,18728,15324,0,268,316
|
| 22 |
+
104,金門縣,1851,3679,2144,988,961,0,60,53,4389,4523,0,0,0
|
| 23 |
+
104,連江縣,223,443,259,0,0,335,0,0,0,21,0,0,0
|
| 24 |
+
105,新北市,77229,195706,106199,41342,39875,5791,1205,6119,157674,151012,136,16599,397
|
| 25 |
+
105,臺北市,50079,116496,69178,59008,37749,4659,472,6294,267043,244062,53,8685,815
|
| 26 |
+
105,桃園市,47794,124604,70471,29658,36600,3393,4944,7041,106343,116787,0,2675,448
|
| 27 |
+
105,臺中市,72269,153062,89408,37764,54626,2042,3634,7160,191224,190633,0,10001,940
|
| 28 |
+
105,臺南市,43250,89475,53089,26098,23331,5324,4362,2700,128882,131468,0,1429,508
|
| 29 |
+
105,高雄市,54095,129914,76167,35688,39310,4683,5988,4136,148244,154088,27,8797,667
|
| 30 |
+
105,宜蘭縣,10247,22349,14783,6383,4743,1399,769,607,14458,17717,0,872,115
|
| 31 |
+
105,新竹縣,14995,35692,18044,5089,5565,1803,829,1392,16395,22667,0,607,230
|
| 32 |
+
105,苗栗縣,11018,28579,16836,6157,8037,1852,1216,1124,20832,20794,0,707,129
|
| 33 |
+
105,彰化縣,25336,65423,39679,12079,19434,4550,2799,3684,37422,37450,0,1160,623
|
| 34 |
+
105,南投縣,9873,24180,15716,4461,8426,739,1226,791,12191,12191,13,536,149
|
| 35 |
+
105,雲林縣,13080,34012,22308,8080,9369,1810,1527,658,25729,26405,0,1070,208
|
| 36 |
+
105,嘉義縣,8490,20810,13153,2729,4953,519,1778,483,24448,29757,0,915,0
|
| 37 |
+
105,屏東縣,14616,37482,24014,9246,9664,1091,811,1206,41174,41150,0,1487,189
|
| 38 |
+
105,臺東縣,4510,10702,6682,537,2519,2847,182,303,5910,6091,0,37,111
|
| 39 |
+
105,花蓮縣,6772,15914,10165,4144,4074,2404,64,598,19426,19510,0,257,171
|
| 40 |
+
105,澎湖縣,1685,3756,2452,1000,1668,0,65,0,3018,3018,0,29,0
|
| 41 |
+
105,基隆市,6451,16654,10279,5082,6395,383,373,535,15741,15741,0,2118,159
|
| 42 |
+
105,新竹市,12544,29534,15683,9016,5783,3649,607,1677,50315,49211,0,282,0
|
| 43 |
+
105,嘉義市,6220,15468,10634,6507,9047,1475,1892,777,18457,15053,0,251,329
|
| 44 |
+
105,金門縣,1936,3618,2023,1009,1016,0,51,36,4515,4618,0,0,0
|
| 45 |
+
105,連江縣,292,455,249,0,0,324,0,0,0,18,0,0,0
|
| 46 |
+
106,新北市,81348,192321,100608,40999,37328,5581,917,5336,151475,145054,116,16615,385
|
| 47 |
+
106,臺北市,52253,115679,66746,58742,35058,4072,377,5448,263668,241829,81,8120,779
|
| 48 |
+
106,桃園市,51322,122590,67375,29822,35420,3032,4770,5965,102983,114101,0,2633,450
|
| 49 |
+
106,臺中市,77619,150412,85244,37962,50784,2020,3836,6211,188641,188114,0,9450,897
|
| 50 |
+
106,臺南市,46663,87329,50556,25817,22075,4418,4007,2540,124447,126610,0,1397,482
|
| 51 |
+
106,高雄市,56968,125900,72395,35657,38273,3347,5579,3557,144540,149969,72,8142,673
|
| 52 |
+
106,宜蘭縣,10639,21563,14108,6290,4513,1306,673,529,13546,16707,0,806,116
|
| 53 |
+
106,新竹縣,16035,35578,17398,5351,5436,1431,869,1241,15259,20942,0,561,235
|
| 54 |
+
106,苗栗縣,11727,27602,15835,6216,7835,1552,1221,934,19806,19674,0,425,135
|
| 55 |
+
106,彰化縣,26724,62830,37275,12119,18398,4270,2726,3397,35163,35166,0,1099,565
|
| 56 |
+
106,南投縣,10345,23221,14436,4525,8302,623,1114,679,11578,11578,28,530,145
|
| 57 |
+
106,雲林縣,13617,32410,21017,7949,8666,1577,1451,667,25198,25932,0,1066,197
|
| 58 |
+
106,嘉義縣,8649,19569,12207,2732,4964,495,1583,502,24088,29101,0,886,0
|
| 59 |
+
106,屏東縣,15592,35645,22574,9068,9114,964,698,1105,39787,39639,0,1494,189
|
| 60 |
+
106,臺東縣,4643,10385,6303,563,2543,2635,188,257,5860,5997,0,28,114
|
| 61 |
+
106,花蓮縣,7017,15363,9614,4047,3875,2185,28,538,18762,18819,0,256,158
|
| 62 |
+
106,澎湖縣,1766,3524,2362,970,1588,0,64,0,3008,3008,0,18,0
|
| 63 |
+
106,基隆市,6909,15944,9635,4696,5852,349,412,437,15211,15211,0,2208,131
|
| 64 |
+
106,新竹市,13284,29635,15414,9300,5680,3397,559,1518,48359,47203,0,280,0
|
| 65 |
+
106,嘉義市,6466,15068,9996,6407,8940,1371,1922,693,18014,14659,0,230,302
|
| 66 |
+
106,金門縣,2026,3619,1948,1007,1005,0,47,52,4501,4562,0,0,0
|
| 67 |
+
106,連江縣,292,474,227,0,0,304,0,0,0,19,0,0,0
|
| 68 |
+
107,新北市,83960,195862,95923,39153,34275,5128,700,4693,147293,141046,89,17056,318
|
| 69 |
+
107,臺北市,54229,118097,64651,57381,31239,3316,302,4712,259340,239263,124,7409,739
|
| 70 |
+
107,桃園市,54358,124639,64734,29262,33257,2544,4422,5138,101858,113066,0,2628,419
|
| 71 |
+
107,臺中市,79891,153260,81865,37422,45640,1970,4119,5479,186363,185886,0,9543,807
|
| 72 |
+
107,臺南市,47210,88745,48570,24629,19920,3480,3594,2101,120215,121598,47,1342,446
|
| 73 |
+
107,高雄市,57683,126527,69362,34231,36231,2134,5094,3021,141008,146140,128,7742,625
|
| 74 |
+
107,宜蘭縣,10876,21515,13233,5981,4325,1233,610,555,12864,15850,0,816,103
|
| 75 |
+
107,新竹縣,16412,36512,16990,5204,5121,1174,850,949,14411,19541,0,545,241
|
| 76 |
+
107,苗栗縣,12027,27778,14967,6182,7363,1152,1206,867,18887,18704,0,219,118
|
| 77 |
+
107,彰化縣,29513,62367,35150,11949,17139,3951,2689,2712,33376,33328,0,870,511
|
| 78 |
+
107,南投縣,10617,22763,13531,4269,7575,637,970,588,10926,10926,50,470,137
|
| 79 |
+
107,雲林縣,14170,31645,19840,7607,7833,1273,1403,642,24836,25598,0,1038,202
|
| 80 |
+
107,嘉義縣,8865,18736,11450,2700,4597,426,1336,399,27312,31702,0,979,0
|
| 81 |
+
107,屏東縣,16004,35146,21110,8665,8086,828,640,1095,38794,38541,0,1532,173
|
| 82 |
+
107,臺東縣,4733,10189,6052,562,2622,2349,185,275,5757,5814,0,37,108
|
| 83 |
+
107,花蓮縣,7160,15235,9102,3975,3725,1780,72,491,18269,18301,0,269,143
|
| 84 |
+
107,澎湖縣,1803,3472,2200,897,1542,0,44,0,3025,3025,0,18,0
|
| 85 |
+
107,基隆市,7169,15896,9031,4093,5088,338,411,413,14802,14802,0,2162,114
|
| 86 |
+
107,新竹市,13773,30866,15214,9256,5875,2906,533,1358,47161,46003,0,246,0
|
| 87 |
+
107,嘉義市,6628,15090,9351,6298,8298,1214,1865,589,14022,11234,0,208,288
|
| 88 |
+
107,金門縣,2037,3729,1863,976,1018,0,34,47,4303,4374,0,0,0
|
| 89 |
+
107,連江縣,286,482,220,0,0,285,0,0,0,80,0,0,0
|
| 90 |
+
108,新北市,87311,198588,93426,37425,30732,4471,600,4028,141670,135795,81,17747,271
|
| 91 |
+
108,臺北市,56635,120623,64558,56217,26944,2641,323,4024,256209,237838,148,7326,653
|
| 92 |
+
108,桃園市,58589,126902,63378,29030,30448,1781,3993,4183,99870,110856,0,2639,409
|
| 93 |
+
108,臺中市,81908,155376,80549,36466,39657,1806,4435,4760,184268,183875,0,9967,702
|
| 94 |
+
108,臺南市,47967,90188,47473,23228,17808,2843,3121,1912,115057,115794,67,1578,408
|
| 95 |
+
108,高雄市,60483,127513,67579,32753,33865,1212,4279,2681,138258,143051,171,7619,566
|
| 96 |
+
108,宜蘭縣,11389,21698,12534,5663,4093,1103,601,581,12197,14880,0,689,86
|
| 97 |
+
108,新竹縣,17325,37203,16909,5029,4977,918,824,760,13314,17919,0,584,213
|
| 98 |
+
108,苗栗縣,13390,27856,14515,5933,6656,971,1083,769,18246,18018,0,228,114
|
| 99 |
+
108,彰化縣,31301,62052,33185,11609,15924,3582,2643,2113,31147,31147,0,817,493
|
| 100 |
+
108,南投縣,11295,22689,12793,3907,6876,778,796,561,10214,10214,58,494,123
|
| 101 |
+
108,雲林縣,14988,31117,18968,7225,6736,1182,1285,530,24603,25358,0,817,193
|
| 102 |
+
108,嘉義縣,9097,18355,10668,2636,4058,352,1080,333,26309,30480,0,1095,0
|
| 103 |
+
108,屏東縣,17157,34688,20054,8123,7020,700,562,866,36734,36549,0,1310,168
|
| 104 |
+
108,臺東縣,4853,9975,5805,541,2533,2105,187,237,5842,5863,0,33,114
|
| 105 |
+
108,花蓮縣,7531,15217,8567,3810,3504,1485,20,434,18027,18040,0,289,127
|
| 106 |
+
108,澎湖縣,1866,3472,2062,833,1368,0,52,0,3051,3051,0,36,0
|
| 107 |
+
108,基隆市,7506,15862,8704,3502,4334,270,323,370,14561,14561,0,2061,114
|
| 108 |
+
108,新竹市,14559,31907,15323,9025,6102,2073,494,1158,45986,44777,0,179,0
|
| 109 |
+
108,嘉義市,6996,15107,8843,6076,7488,991,1746,423,13222,10593,0,184,264
|
| 110 |
+
108,金門縣,2099,3756,1846,948,931,0,4,63,4387,4451,0,0,0
|
| 111 |
+
108,連江縣,300,470,241,0,0,257,0,0,0,62,0,0,0
|
| 112 |
+
109,新北市,89211,199521,92271,36692,28494,4259,516,3514,139130,133358,74,17165,260
|
| 113 |
+
109,臺北市,58312,122605,65349,55247,24765,2264,328,3646,256492,239317,154,4010,633
|
| 114 |
+
109,桃園市,61674,128236,62781,28861,28965,1269,3538,3496,100402,111374,0,1409,384
|
| 115 |
+
109,臺中市,84021,156584,79598,36000,35898,1680,5002,4219,186976,186543,0,6087,655
|
| 116 |
+
109,臺南市,49113,90522,46886,22560,16556,2453,2897,1689,111807,112370,75,819,391
|
| 117 |
+
109,高雄市,63771,127298,66052,31908,32132,1033,3562,2256,137812,142353,178,5159,535
|
| 118 |
+
109,宜蘭縣,11549,21737,11869,5520,4016,988,566,481,11674,14166,0,427,91
|
| 119 |
+
109,新竹縣,18473,37672,17130,4971,4884,851,780,729,12657,16589,0,187,192
|
| 120 |
+
109,苗栗縣,14077,27698,14088,5921,6148,720,946,655,18036,17809,0,221,105
|
| 121 |
+
109,彰化縣,32060,61453,31796,10988,15106,3324,2598,1737,28036,28036,0,455,498
|
| 122 |
+
109,南投縣,11741,22273,12183,3646,6155,913,636,512,9771,9771,69,220,114
|
| 123 |
+
109,雲林縣,15183,30472,18282,7091,6310,1116,1228,469,24519,25319,0,435,187
|
| 124 |
+
109,嘉義縣,9397,17770,9990,2682,3212,197,850,218,25456,29682,0,118,0
|
| 125 |
+
109,屏東縣,17983,34316,18930,7907,6302,649,483,722,35511,35454,0,683,163
|
| 126 |
+
109,臺東縣,4923,9859,5592,592,2457,1867,167,199,5980,5953,0,28,104
|
| 127 |
+
109,花蓮縣,7601,15194,8248,3696,3356,1212,14,419,17821,17714,0,192,110
|
| 128 |
+
109,澎湖縣,1848,3446,1902,818,1283,0,47,0,3047,3047,0,33,0
|
| 129 |
+
109,基隆市,8009,15691,8368,3338,3956,240,271,332,15735,15735,0,261,119
|
| 130 |
+
109,新竹市,14728,32372,15651,8807,6680,1373,492,1106,45641,44392,0,154,0
|
| 131 |
+
109,嘉義市,7237,14846,8710,6177,6906,796,1706,373,12826,10221,0,95,218
|
| 132 |
+
109,金門縣,2202,3808,1854,941,911,0,0,50,4131,4213,0,0,0
|
| 133 |
+
109,連江縣,293,493,257,0,0,237,0,0,0,44,0,0,0
|
| 134 |
+
110,新北市,88778,202529,91044,36286,27493,4080,403,3135,135995,130782,65,16922,262
|
| 135 |
+
110,臺北市,57492,125093,65268,54449,22915,1942,360,3373,250939,239082,165,3422,587
|
| 136 |
+
110,桃園市,62155,131258,62184,28842,27813,922,3225,3082,99535,110565,18,1203,353
|
| 137 |
+
110,臺中市,82579,160242,78320,35832,33498,1509,5328,3795,187115,186634,0,4737,635
|
| 138 |
+
110,臺南市,49071,91967,46589,22150,15877,2340,2637,1595,108005,108508,73,728,387
|
| 139 |
+
110,高雄市,65003,128846,64368,31497,30510,1052,3279,2098,136309,140652,150,5263,499
|
| 140 |
+
110,宜蘭縣,11578,21720,11620,5346,3938,933,521,459,11076,13022,0,312,91
|
| 141 |
+
110,新竹縣,18949,38429,17235,4993,4709,832,829,690,11956,15458,0,148,195
|
| 142 |
+
110,苗栗縣,14072,28275,13787,5967,5755,498,802,602,17825,17633,0,175,105
|
| 143 |
+
110,彰化縣,31533,61364,30638,10593,14115,3062,2428,1482,25049,25049,0,381,496
|
| 144 |
+
110,南投縣,11684,22481,11549,3413,5517,1043,574,486,9477,9477,89,205,111
|
| 145 |
+
110,雲林縣,15011,30256,17466,6900,5978,978,1153,399,24124,24936,0,269,180
|
| 146 |
+
110,嘉義縣,9198,17571,9219,2652,2692,74,657,138,24808,28824,0,92,0
|
| 147 |
+
110,屏東縣,18038,34546,17910,7705,5825,642,398,621,34038,33925,0,378,156
|
| 148 |
+
110,臺東縣,4937,9768,5322,577,2341,1786,151,160,6095,6105,0,19,99
|
| 149 |
+
110,花蓮縣,7687,15242,8045,3608,3276,987,16,374,17714,17588,0,171,103
|
| 150 |
+
110,澎湖縣,1853,3485,1810,776,1189,0,41,0,3063,3063,0,30,0
|
| 151 |
+
110,基隆市,7961,15795,8125,3255,3598,195,156,274,15000,15000,0,231,107
|
| 152 |
+
110,新竹市,14554,33197,15841,8654,6983,972,452,937,50745,44811,0,122,0
|
| 153 |
+
110,嘉義市,7302,14883,8456,6038,6502,600,1594,375,12747,10384,0,78,207
|
| 154 |
+
110,金門縣,2194,3878,1871,940,828,0,0,46,4215,4284,0,0,0
|
| 155 |
+
110,連江縣,292,492,247,0,0,232,0,0,0,48,0,0,0
|
| 156 |
+
111,新北市,88525,207675,87567,36341,27082,3983,282,2660,129608,125103,69,16834,252
|
| 157 |
+
111,臺北市,55777,127285,63593,53909,22367,1690,376,3139,244638,234009,175,3305,583
|
| 158 |
+
111,桃園市,62603,135930,59783,28878,26777,752,2852,2508,96681,107419,43,1092,321
|
| 159 |
+
111,臺中市,80497,165662,75249,36005,32114,1480,5374,3438,181341,180820,0,4170,640
|
| 160 |
+
111,臺南市,48170,95050,44319,21835,15310,2307,2461,1416,101751,102225,68,639,393
|
| 161 |
+
111,高雄市,63808,132841,61145,31388,28889,987,3032,1767,130544,134435,118,5064,483
|
| 162 |
+
111,宜蘭縣,11243,22238,11253,5297,3685,916,473,385,10345,11767,0,249,81
|
| 163 |
+
111,新竹縣,19065,39663,16837,4983,4707,873,862,605,11119,14069,0,119,204
|
| 164 |
+
111,苗栗縣,13742,29098,12944,6063,5488,262,775,384,17239,17071,0,155,113
|
| 165 |
+
111,彰化縣,30784,62313,29093,10370,13124,2801,2293,1249,21299,21299,0,260,480
|
| 166 |
+
111,南投縣,11322,22789,10915,3344,4990,991,436,420,9165,9165,86,162,105
|
| 167 |
+
111,雲林縣,14710,30506,16394,6807,5802,878,1068,340,23820,24677,0,218,163
|
| 168 |
+
111,嘉義縣,8811,17658,8625,2613,2277,0,462,105,23901,27830,0,75,0
|
| 169 |
+
111,屏東縣,17798,35286,16731,7658,5470,644,328,518,32324,32114,0,298,141
|
| 170 |
+
111,臺東縣,4907,9976,4962,546,2243,1742,140,149,5854,5958,0,12,99
|
| 171 |
+
111,花蓮縣,7407,15530,7741,3538,3112,913,7,359,17143,17038,0,139,101
|
| 172 |
+
111,澎湖縣,1788,3548,1697,733,1082,0,31,0,2927,2927,0,29,0
|
| 173 |
+
111,基隆市,7733,16146,7644,3289,3321,170,121,224,14190,14190,0,162,92
|
| 174 |
+
111,新竹市,14456,33869,15530,8716,7225,704,426,835,50067,44047,0,104,0
|
| 175 |
+
111,嘉義市,7143,15123,8126,5925,6159,523,1363,310,11958,9656,0,50,203
|
| 176 |
+
111,金門縣,2151,3909,1823,939,807,0,0,44,4175,4243,0,0,0
|
| 177 |
+
111,連江縣,290,480,245,0,0,265,0,0,0,27,0,0,0
|
| 178 |
+
112,新北市,88341,208980,85762,36496,26180,3948,254,2371,121961,117829,46,16626,243
|
| 179 |
+
112,臺北市,53918,127398,62422,53653,21986,1648,456,2982,237221,228032,159,3289,572
|
| 180 |
+
112,桃園市,63995,138435,58718,29096,26703,740,2731,2016,94438,104594,56,1092,310
|
| 181 |
+
112,臺中市,79789,168752,73448,36378,31315,1380,5568,3166,177725,177187,0,4035,640
|
| 182 |
+
112,臺南市,47927,96383,42710,21844,14793,2258,2295,1231,94711,95217,59,605,387
|
| 183 |
+
112,高雄市,63390,134605,58888,31351,27449,1027,3129,1629,124633,127985,77,4930,471
|
| 184 |
+
112,宜蘭縣,11251,22523,11013,5164,3434,880,422,308,9876,11221,0,247,65
|
| 185 |
+
112,新竹縣,18821,40410,16537,5062,4741,1006,907,533,10596,13208,0,137,210
|
| 186 |
+
112,苗栗縣,13893,29208,12557,6025,5249,195,714,325,16591,16425,0,164,106
|
| 187 |
+
112,彰化縣,30602,62675,27965,10231,12599,2596,2035,1123,17722,17722,0,249,446
|
| 188 |
+
112,南投縣,11253,22883,10602,3288,4731,922,320,359,9122,9122,114,156,105
|
| 189 |
+
112,雲林縣,14799,30604,15454,6719,5383,769,1006,305,23049,23832,0,206,161
|
| 190 |
+
112,嘉義縣,8757,17664,8216,2535,2276,0,326,98,23233,27497,0,67,0
|
| 191 |
+
112,屏東縣,17854,35817,15960,7301,5100,612,279,450,30157,29958,0,331,134
|
| 192 |
+
112,臺東縣,4916,9949,4752,506,2058,1650,121,129,5807,5907,0,8,90
|
| 193 |
+
112,花蓮縣,7313,15595,7484,3460,2800,972,5,322,16622,16532,0,137,96
|
| 194 |
+
112,澎湖縣,1822,3632,1625,693,971,0,26,0,2702,2702,0,29,0
|
| 195 |
+
112,基隆市,7703,16303,7270,3292,3083,136,59,222,13813,13813,0,182,80
|
| 196 |
+
112,新竹市,14584,34035,15109,8810,7432,552,378,731,49073,42827,0,111,0
|
| 197 |
+
112,嘉義市,7288,15035,7760,5899,5861,470,1126,298,11727,9060,0,42,218
|
| 198 |
+
112,金門縣,2087,3988,1795,939,778,0,0,41,4050,4105,0,0,0
|
| 199 |
+
112,連江縣,278,496,252,0,0,269,0,0,0,54,0,0,0
|
| 200 |
+
113,新北市,85532,202483,90131,36744,25707,3992,242,2340,119190,116885,28,16888,222
|
| 201 |
+
113,臺北市,50990,122987,64808,53247,21907,1665,413,2968,234053,223992,172,3113,590
|
| 202 |
+
113,桃園市,63421,135942,61743,29186,26245,770,2661,1844,94684,104871,54,1106,293
|
| 203 |
+
113,臺中市,77511,165484,77345,36635,30749,1371,5705,3055,175722,175075,0,3970,624
|
| 204 |
+
113,臺南市,46610,93966,44331,21940,14353,2291,2245,1106,91347,91947,53,564,377
|
| 205 |
+
113,高雄市,61751,131156,61242,31247,26786,914,3412,1444,121965,124137,43,4698,468
|
| 206 |
+
113,宜蘭縣,10954,22157,11263,5227,3292,860,405,304,9388,10659,0,237,69
|
| 207 |
+
113,新竹縣,18790,39379,17585,5054,4947,1130,999,523,10980,12951,0,161,201
|
| 208 |
+
113,苗栗縣,13459,28315,13092,6104,5088,106,700,310,16438,16365,0,175,108
|
| 209 |
+
113,彰化縣,29864,60625,28516,10028,12375,2407,1922,928,15787,15854,0,270,435
|
| 210 |
+
113,南投縣,10944,22256,10999,3191,4494,856,206,335,9073,9073,147,170,109
|
| 211 |
+
113,雲林縣,14534,29618,15584,6655,5026,694,949,252,22445,23301,0,236,157
|
| 212 |
+
113,嘉義縣,8639,17154,8240,2490,2221,0,224,98,23133,27250,0,59,0
|
| 213 |
+
113,屏東縣,17527,34969,16427,7017,4806,565,182,390,28810,28750,0,329,142
|
| 214 |
+
113,臺東縣,4739,9790,4834,519,1913,1634,111,140,5645,5833,0,11,85
|
| 215 |
+
113,花蓮縣,6943,15179,7507,3455,2530,1102,16,299,15861,15839,0,120,106
|
| 216 |
+
113,澎湖縣,1814,3534,1666,682,854,0,22,0,2388,2651,0,12,0
|
| 217 |
+
113,基隆市,7355,15951,7408,3263,2905,127,45,185,13383,13429,0,189,90
|
| 218 |
+
113,新竹市,14063,32858,15928,9046,7575,421,369,606,48901,42763,0,108,0
|
| 219 |
+
113,嘉義市,6997,14593,8017,5819,5922,447,939,300,11349,8734,0,57,227
|
| 220 |
+
113,金門縣,2011,3928,1877,954,803,0,0,39,3823,3866,0,0,0
|
| 221 |
+
113,連江縣,268,487,257,0,0,257,0,0,0,140,0,0,0
|
| 222 |
+
114,新北市,81149,194670,96668,36359,23972,4106,221,2306,117768,117768,42,16892,243
|
| 223 |
+
114,臺北市,47886,117650,68231,53020,21069,1626,415,2829,230483,230483,162,3140,564
|
| 224 |
+
114,桃園市,61764,132964,66607,28850,25498,760,2636,1742,93462,93462,57,1154,285
|
| 225 |
+
114,臺中市,74369,161332,82767,36484,29330,1354,5575,2772,174618,174618,0,3975,562
|
| 226 |
+
114,臺南市,44318,91142,47883,21507,13301,2070,2128,1012,88863,88863,56,404,375
|
| 227 |
+
114,高雄市,59174,127327,65631,30498,25072,820,3435,1329,118531,118531,0,5465,466
|
| 228 |
+
114,宜蘭縣,10651,21605,11843,5046,3099,865,372,291,9143,9143,0,219,74
|
| 229 |
+
114,新竹縣,18409,38373,18746,5267,4847,1160,1077,489,11340,11340,0,129,194
|
| 230 |
+
114,苗栗縣,12852,27368,14041,6011,4587,44,641,285,15950,15950,0,159,100
|
| 231 |
+
114,彰化縣,28486,58539,29788,9769,11956,2287,1656,758,15361,15361,0,279,429
|
| 232 |
+
114,南投縣,10499,21608,11428,3121,4139,803,184,318,8947,8947,160,192,111
|
| 233 |
+
114,雲林縣,13953,28798,16118,6500,4573,638,776,254,22453,22453,30,250,152
|
| 234 |
+
114,嘉義縣,8270,16524,8509,2467,2208,0,117,93,22510,22510,0,81,0
|
| 235 |
+
114,屏東縣,16988,34245,17225,6668,4547,504,162,371,27749,27749,0,298,151
|
| 236 |
+
114,臺東縣,4553,9507,5035,502,1671,1580,95,139,5391,5391,0,10,85
|
| 237 |
+
114,花蓮縣,6670,14809,7708,3328,2390,1115,7,301,15029,15029,0,156,115
|
| 238 |
+
114,澎湖縣,1809,3434,1757,658,799,0,26,0,2202,2202,0,22,0
|
| 239 |
+
114,基隆市,7020,15403,7827,3066,2530,127,26,168,13242,13242,0,154,83
|
| 240 |
+
114,新竹市,13500,31460,16964,9222,7124,416,313,573,49039,49039,0,96,0
|
| 241 |
+
114,嘉義市,6655,14126,8377,5680,5782,384,879,323,11103,11103,0,64,210
|
| 242 |
+
114,金門縣,1867,3886,1948,950,752,0,0,32,3660,3660,0,0,0
|
| 243 |
+
114,連江縣,235,488,247,0,0,252,0,0,0,0,0,0,0
|
classroom_data/templates/week19/main.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""資料分析:讀取公開資料,查詢各縣市學生人數。"""
|
| 2 |
+
|
| 3 |
+
import csv
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
DATA_FILE = Path("data/students_by_county.csv")
|
| 8 |
+
SAMPLE_YEAR = "114"
|
| 9 |
+
SAMPLE_FIELD = "國小[人]"
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def load_rows(path=DATA_FILE):
|
| 13 |
+
"""讀取 CSV;utf-8-sig 也能正確處理政府資料常見的 BOM。"""
|
| 14 |
+
with open(path, encoding="utf-8-sig", newline="") as file:
|
| 15 |
+
return list(csv.DictReader(file))
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def parse_count(value):
|
| 19 |
+
"""將有人數的欄位轉成整數;空白或非數字代表未知,不當成 0。"""
|
| 20 |
+
cleaned = (value or "").strip().replace(",", "")
|
| 21 |
+
if not cleaned or cleaned == "-":
|
| 22 |
+
return None
|
| 23 |
+
try:
|
| 24 |
+
return int(cleaned)
|
| 25 |
+
except ValueError:
|
| 26 |
+
return None
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def count_by_county(rows, school_year, field):
|
| 30 |
+
"""依學年度篩選,再回傳各縣市的人數排序。"""
|
| 31 |
+
totals = {}
|
| 32 |
+
for row in rows:
|
| 33 |
+
if row.get("學年度", "").strip() != str(school_year):
|
| 34 |
+
continue
|
| 35 |
+
|
| 36 |
+
count = parse_count(row.get(field))
|
| 37 |
+
county = row.get("縣市別", "").strip()
|
| 38 |
+
if county and count is not None:
|
| 39 |
+
totals[county] = count
|
| 40 |
+
|
| 41 |
+
return sorted(totals.items(), key=lambda item: item[1], reverse=True)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def get_count(rows, school_year, county, field):
|
| 45 |
+
"""取得指定學年度、縣市及欄位的人數;找不到時回傳 None。"""
|
| 46 |
+
for row in rows:
|
| 47 |
+
if row.get("學年度", "").strip() != str(school_year):
|
| 48 |
+
continue
|
| 49 |
+
if row.get("縣市別", "").strip() == county:
|
| 50 |
+
return parse_count(row.get(field))
|
| 51 |
+
return None
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def print_text_chart(results, limit=8):
|
| 55 |
+
"""在 Console 印出簡單長條圖,讓每筆數字都能被核對。"""
|
| 56 |
+
if not results:
|
| 57 |
+
print("沒有符合條件的資料,請檢查學年度和欄位名稱。")
|
| 58 |
+
return
|
| 59 |
+
|
| 60 |
+
visible = results[:limit]
|
| 61 |
+
largest = max(value for _, value in visible)
|
| 62 |
+
print("縣市 人數 | 比例長條")
|
| 63 |
+
for county, value in visible:
|
| 64 |
+
bar_length = round(value / largest * 24) if largest else 0
|
| 65 |
+
print(f"{county:<6} {value:>8,} | {'█' * bar_length}")
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def main():
|
| 69 |
+
rows = load_rows()
|
| 70 |
+
fields = list(rows[0]) if rows else []
|
| 71 |
+
print("=== 資料分析:縣市學生人數 ===")
|
| 72 |
+
print(f"讀入 {len(rows)} 列資料")
|
| 73 |
+
print(f"欄位:{', '.join(fields)}")
|
| 74 |
+
print()
|
| 75 |
+
|
| 76 |
+
# 範例問題:114 學年度,哪些縣市的國小學生人數最多?
|
| 77 |
+
question = f"{SAMPLE_YEAR} 學年度,哪些縣市的 {SAMPLE_FIELD} 最多?"
|
| 78 |
+
results = count_by_county(rows, SAMPLE_YEAR, SAMPLE_FIELD)
|
| 79 |
+
print(f"範例問題:{question}")
|
| 80 |
+
print_text_chart(results)
|
| 81 |
+
|
| 82 |
+
# 你的下一步:換一個學年度、教育階段或可比較的欄位,
|
| 83 |
+
# 先寫下預測,再執行並核對其中一筆原始資料。
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
if __name__ == "__main__":
|
| 87 |
+
main()
|
classroom_static/classroom.css
CHANGED
|
@@ -199,6 +199,8 @@ body.panel-resizing { cursor:col-resize;user-select:none; }
|
|
| 199 |
.lesson-timeline { gap:.65rem!important; }
|
| 200 |
.lesson-timeline strong { color:var(--navy); }
|
| 201 |
.lesson-guide-block li::marker,.lesson-details li::marker { color:var(--action);font-weight:800; }
|
|
|
|
|
|
|
| 202 |
.lesson-target { padding:var(--space-md);border:0;border-radius:var(--radius-md);background:var(--action-soft); }
|
| 203 |
.lesson-ai-note { padding:var(--space-md);border:0;border-radius:var(--radius-md);background:var(--help-soft); }
|
| 204 |
.lesson-details { margin:0 0 var(--space-lg);border-bottom:1px solid var(--line); }
|
|
|
|
| 199 |
.lesson-timeline { gap:.65rem!important; }
|
| 200 |
.lesson-timeline strong { color:var(--navy); }
|
| 201 |
.lesson-guide-block li::marker,.lesson-details li::marker { color:var(--action);font-weight:800; }
|
| 202 |
+
.lesson-source-link { display:inline-flex;min-height:44px;align-items:center;color:var(--action);font-size:.84rem;font-weight:800;text-decoration-thickness:1px;text-underline-offset:3px; }
|
| 203 |
+
.lesson-source-link:focus-visible { outline:2px solid var(--action);outline-offset:3px;border-radius:2px; }
|
| 204 |
.lesson-target { padding:var(--space-md);border:0;border-radius:var(--radius-md);background:var(--action-soft); }
|
| 205 |
.lesson-ai-note { padding:var(--space-md);border:0;border-radius:var(--radius-md);background:var(--help-soft); }
|
| 206 |
.lesson-details { margin:0 0 var(--space-lg);border-bottom:1px solid var(--line); }
|
classroom_static/student.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
| 7 |
<title>Python 專案教室|學生工作區</title>
|
| 8 |
<link rel="stylesheet" href="/classroom-assets/vendor/codemirror/codemirror.css">
|
| 9 |
<link rel="stylesheet" href="/classroom-assets/vendor/codemirror/material-darker.css">
|
| 10 |
-
<link rel="stylesheet" href="/classroom-assets/classroom.css?v=2026fall-
|
| 11 |
</head>
|
| 12 |
<body>
|
| 13 |
<header class="app-header">
|
|
@@ -143,6 +143,6 @@
|
|
| 143 |
<script src="/classroom-assets/vendor/codemirror/python.js"></script>
|
| 144 |
<script src="/classroom-assets/vendor/jszip/jszip.min.js"></script>
|
| 145 |
<script src="/classroom-assets/pygame-support.js?v=2026fall-1"></script>
|
| 146 |
-
<script src="/classroom-assets/student.js?v=2026fall-
|
| 147 |
</body>
|
| 148 |
</html>
|
|
|
|
| 7 |
<title>Python 專案教室|學生工作區</title>
|
| 8 |
<link rel="stylesheet" href="/classroom-assets/vendor/codemirror/codemirror.css">
|
| 9 |
<link rel="stylesheet" href="/classroom-assets/vendor/codemirror/material-darker.css">
|
| 10 |
+
<link rel="stylesheet" href="/classroom-assets/classroom.css?v=2026fall-21">
|
| 11 |
</head>
|
| 12 |
<body>
|
| 13 |
<header class="app-header">
|
|
|
|
| 143 |
<script src="/classroom-assets/vendor/codemirror/python.js"></script>
|
| 144 |
<script src="/classroom-assets/vendor/jszip/jszip.min.js"></script>
|
| 145 |
<script src="/classroom-assets/pygame-support.js?v=2026fall-1"></script>
|
| 146 |
+
<script src="/classroom-assets/student.js?v=2026fall-19"></script>
|
| 147 |
</body>
|
| 148 |
</html>
|
classroom_static/student.js
CHANGED
|
@@ -83,12 +83,32 @@ function initResizablePanels(){
|
|
| 83 |
setupResizer("reference");setupResizer("console");new ResizeObserver(refreshEditorLayout).observe(E.editorPanel);window.addEventListener("resize",()=>syncPanelWidths(false));
|
| 84 |
}
|
| 85 |
function setLayout(referenceHidden,consoleHidden){E.studentLayout.classList.toggle("reference-hidden",referenceHidden);E.studentLayout.classList.toggle("console-hidden",consoleHidden);E.toggleReferenceButton.setAttribute("aria-pressed",String(referenceHidden));E.toggleConsoleButton.setAttribute("aria-pressed",String(consoleHidden));E.toggleReferenceButton.textContent=referenceHidden?"顯示側欄":"隱藏側欄";E.toggleConsoleButton.textContent=consoleHidden?"顯示 Console":"隱藏 Console";localStorage.setItem("classroom_hide_reference",String(referenceHidden));localStorage.setItem("classroom_hide_console",String(consoleHidden));requestAnimationFrame(()=>{syncPanelWidths(false);refreshEditorLayout()})}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
function renderLesson(){
|
| 87 |
const item=lessonInfo(),shared=item?.workspace_id&&item.workspace_id!==item.id,isClosure=item?.id==="week05";
|
| 88 |
E.templateButton.disabled=!item?.template;
|
| 89 |
E.templateButton.textContent=item?.template?"匯入專案起始套版":shared?"沿用目前專案":"尚未提供套版";
|
| 90 |
E.lessonHeading.textContent=item?`Lesson ${item.lesson??item.week}|${item.title}`:"本堂任務";
|
| 91 |
E.lessonLabel.textContent=item?.project||"";
|
|
|
|
| 92 |
E.lessonGuideContent.innerHTML=isClosure?`
|
| 93 |
<section class="lesson-guide-block lesson-target"><h2>今天完成一條可玩的案件路線</h2><p>新玩家可以自己開始調查、收集線索、完成推理並看到結局。</p></section>
|
| 94 |
<section class="lesson-guide-block"><h2>收尾檢查</h2><ul><li>至少三個地點、三項不同線索</li><li>能查看證物,重複調查不會重複加入</li><li>必要線索能解鎖推理和合理結局</li><li>空白或錯誤輸入不會讓程式中止</li></ul></section>
|
|
@@ -102,7 +122,7 @@ function renderLesson(){
|
|
| 102 |
<section class="lesson-help-note"><h2>需要老師幫忙?</h2><p>按一次就會通知老師。成功呼叫後冷卻 ${helpCooldownLabel()};取消或老師回應都不會重置。</p></section>`;
|
| 103 |
if(isClosure)setSideView("lessonView");
|
| 104 |
}
|
| 105 |
-
function setSideView(name){document.querySelectorAll(".side-tab").forEach(button=>button.classList.toggle("active",button.dataset.side===name));document.querySelectorAll(".side-view").forEach(view=>view.hidden=view.id!==name);if(lessonInfo()?.id==="week05"){document.querySelectorAll(".mobile-tab").forEach(button=>button.setAttribute("aria-selected",String(button.dataset.panel==="referencePanel")));document.querySelectorAll(".student-layout>.panel").forEach(panel=>panel.classList.toggle("mobile-active",panel.id==="referencePanel"))}}
|
| 106 |
function setConsoleView(view){E.consoleStream.dataset.view=view;E.consoleOutputTab.classList.toggle("active",view==="console");E.pygameOutputTab.classList.toggle("active",view==="pygame");E.consoleOutputTab.setAttribute("aria-selected",String(view==="console"));E.pygameOutputTab.setAttribute("aria-selected",String(view==="pygame"))}
|
| 107 |
ClassroomPygame.attach(E.pygameFrame,message=>{if(message.type==="output"&&message.text)append(message.text);if(message.type==="status"&&message.label)E.runStatusText.textContent=message.label;if(message.type==="done"){pygameRunning=false;runState("success","遊戲已結束")}if(message.type==="error"||message.type==="load_error"){pygameRunning=false;if(message.text)append(`${message.text}\n`);runState("error","Pygame 執行錯誤");setConsoleView("console")}});
|
| 108 |
function initWorker(){worker?.terminate();workerReady=false;running=false;E.consoleInputForm.hidden=true;E.runButton.disabled=true;E.runButton.textContent="載入 Python…";if(!crossOriginIsolated||typeof SharedArrayBuffer==="undefined"){append("瀏覽器未啟用互動式 Python。請重新整理,並使用最新版 Chrome、Edge 或 Safari。\n");runState("error","環境不支援");return}inputBuffer=new SharedArrayBuffer(16392);inputControl=new Int32Array(inputBuffer,0,2);inputBytes=new Uint8Array(inputBuffer,8);worker=new Worker("/classroom-assets/py-worker.js?v=2026fall-4");worker.onmessage=e=>{const m=e.data;if(m.type==="ready"){workerReady=true;E.runButton.disabled=false;E.runButton.textContent="執行";runState("online","準備完成")}else if(m.type==="stream"){const decoder=m.channel==="stderr"?stderrDecoder:stdoutDecoder;append(decoder.decode(m.bytes,{stream:true}))}else if(m.type==="input_request"){E.consoleInputForm.hidden=false;E.consoleInput.value="";E.runStatusText.textContent="等待輸入";E.consoleInput.focus()}else if(m.type==="done"){E.consoleInputForm.hidden=true;runState("success","執行成功")}else if(m.type==="python_error"){E.consoleInputForm.hidden=true;if(m.text)append(`${m.text}\n`);runState("error","程式錯誤")}else if(m.type==="load_error"){append(`Python 載入失敗:${m.text}\n`);runState("error","載入失敗")}};worker.onerror=e=>{append(`Python 執行器錯誤:${e.message}\n`);runState("error","執行器錯誤")}}
|
|
|
|
| 83 |
setupResizer("reference");setupResizer("console");new ResizeObserver(refreshEditorLayout).observe(E.editorPanel);window.addEventListener("resize",()=>syncPanelWidths(false));
|
| 84 |
}
|
| 85 |
function setLayout(referenceHidden,consoleHidden){E.studentLayout.classList.toggle("reference-hidden",referenceHidden);E.studentLayout.classList.toggle("console-hidden",consoleHidden);E.toggleReferenceButton.setAttribute("aria-pressed",String(referenceHidden));E.toggleConsoleButton.setAttribute("aria-pressed",String(consoleHidden));E.toggleReferenceButton.textContent=referenceHidden?"顯示側欄":"隱藏側欄";E.toggleConsoleButton.textContent=consoleHidden?"顯示 Console":"隱藏 Console";localStorage.setItem("classroom_hide_reference",String(referenceHidden));localStorage.setItem("classroom_hide_console",String(consoleHidden));requestAnimationFrame(()=>{syncPanelWidths(false);refreshEditorLayout()})}
|
| 86 |
+
function renderStructuredGuide(guide){
|
| 87 |
+
const bank=Array.isArray(guide.question_bank)?guide.question_bank:[];
|
| 88 |
+
const flow=Array.isArray(guide.flow)?guide.flow:[];
|
| 89 |
+
const checklist=Array.isArray(guide.checklist)?guide.checklist:[];
|
| 90 |
+
const rules=Array.isArray(guide.question_rules)?guide.question_rules:[];
|
| 91 |
+
const sourceUrl=String(guide.source_url||"");
|
| 92 |
+
const sourceLink=/^https:\/\//i.test(sourceUrl)?`<a class="lesson-source-link" href="${esc(sourceUrl)}" target="_blank" rel="noopener noreferrer">查看官方資料集</a>`:"";
|
| 93 |
+
return `
|
| 94 |
+
<section class="lesson-guide-block lesson-target"><h2>本週目標</h2><p>${esc(guide.goal||"")}</p></section>
|
| 95 |
+
${bank.length?`<section class="lesson-guide-block"><h2>可選研究問題</h2><p>先選一題,改成你真正好奇的比較。</p><ul>${bank.map(item=>`<li><strong>${esc(item.group||"選題")}</strong> ${esc(item.text||"")}</li>`).join("")}</ul></section>`:""}
|
| 96 |
+
${guide.source_title?`<section class="lesson-guide-block"><h2>共用範例資料</h2><p><strong>${esc(guide.source_title)}</strong><br>${esc(guide.source_note||"")}</p>${sourceLink}</section>`:""}
|
| 97 |
+
${rules.length?`<details class="lesson-details"><summary>自訂題目檢查</summary><ul>${rules.map(item=>`<li>${esc(item)}</li>`).join("")}</ul></details>`:""}
|
| 98 |
+
${flow.length?`<section class="lesson-guide-block"><h2>課堂流程|120 分鐘</h2><ol class="lesson-timeline">${flow.map(step=>`<li><strong>${esc(step.time||"")}|${esc(step.title||"")}:</strong>${esc(step.body||"")}</li>`).join("")}</ol></section>`:""}
|
| 99 |
+
${checklist.length?`<section class="lesson-guide-block"><h2>完成檢查</h2><ul>${checklist.map(item=>`<li>${esc(item)}</li>`).join("")}</ul></section>`:""}
|
| 100 |
+
<section class="lesson-guide-block lesson-ai-note"><h2>AI 自主驗證</h2><p>${esc(guide.ai_checkpoint||"")}</p>${guide.ai_note?`<p>${esc(guide.ai_note)}</p>`:""}</section>
|
| 101 |
+
<section class="lesson-help-note"><h2>本週要留下的成果</h2><p>${esc(guide.submission||"")}</p></section>
|
| 102 |
+
<details class="lesson-details"><summary>暫存和正式提交的差別</summary><ul><li><strong>自動同步:</strong>老師可查看目前草稿,不會建立歷史版本。</li><li><strong>暫存版本:</strong>可載回的工作進度,不會交給老師或公開。</li><li><strong>正式提交:</strong>凍結一份交給老師檢查;老師另行發布後才會公開。</li></ul></details>
|
| 103 |
+
<section class="lesson-help-note"><h2>需要老師幫忙?</h2><p>按一次就會通知老師。成功呼叫後冷卻 ${helpCooldownLabel()};取消或老師回應都不會重置。</p></section>`;
|
| 104 |
+
}
|
| 105 |
function renderLesson(){
|
| 106 |
const item=lessonInfo(),shared=item?.workspace_id&&item.workspace_id!==item.id,isClosure=item?.id==="week05";
|
| 107 |
E.templateButton.disabled=!item?.template;
|
| 108 |
E.templateButton.textContent=item?.template?"匯入專案起始套版":shared?"沿用目前專案":"尚未提供套版";
|
| 109 |
E.lessonHeading.textContent=item?`Lesson ${item.lesson??item.week}|${item.title}`:"本堂任務";
|
| 110 |
E.lessonLabel.textContent=item?.project||"";
|
| 111 |
+
if(item?.guide){E.lessonGuideContent.innerHTML=renderStructuredGuide(item.guide);setSideView("lessonView");return}
|
| 112 |
E.lessonGuideContent.innerHTML=isClosure?`
|
| 113 |
<section class="lesson-guide-block lesson-target"><h2>今天完成一條可玩的案件路線</h2><p>新玩家可以自己開始調查、收集線索、完成推理並看到結局。</p></section>
|
| 114 |
<section class="lesson-guide-block"><h2>收尾檢查</h2><ul><li>至少三個地點、三項不同線索</li><li>能查看證物,重複調查不會重複加入</li><li>必要線索能解鎖推理和合理結局</li><li>空白或錯誤輸入不會讓程式中止</li></ul></section>
|
|
|
|
| 122 |
<section class="lesson-help-note"><h2>需要老師幫忙?</h2><p>按一次就會通知老師。成功呼叫後冷卻 ${helpCooldownLabel()};取消或老師回應都不會重置。</p></section>`;
|
| 123 |
if(isClosure)setSideView("lessonView");
|
| 124 |
}
|
| 125 |
+
function setSideView(name){document.querySelectorAll(".side-tab").forEach(button=>button.classList.toggle("active",button.dataset.side===name));document.querySelectorAll(".side-view").forEach(view=>view.hidden=view.id!==name);if(lessonInfo()?.id==="week05"||lessonInfo()?.guide){document.querySelectorAll(".mobile-tab").forEach(button=>button.setAttribute("aria-selected",String(button.dataset.panel==="referencePanel")));document.querySelectorAll(".student-layout>.panel").forEach(panel=>panel.classList.toggle("mobile-active",panel.id==="referencePanel"))}}
|
| 126 |
function setConsoleView(view){E.consoleStream.dataset.view=view;E.consoleOutputTab.classList.toggle("active",view==="console");E.pygameOutputTab.classList.toggle("active",view==="pygame");E.consoleOutputTab.setAttribute("aria-selected",String(view==="console"));E.pygameOutputTab.setAttribute("aria-selected",String(view==="pygame"))}
|
| 127 |
ClassroomPygame.attach(E.pygameFrame,message=>{if(message.type==="output"&&message.text)append(message.text);if(message.type==="status"&&message.label)E.runStatusText.textContent=message.label;if(message.type==="done"){pygameRunning=false;runState("success","遊戲已結束")}if(message.type==="error"||message.type==="load_error"){pygameRunning=false;if(message.text)append(`${message.text}\n`);runState("error","Pygame 執行錯誤");setConsoleView("console")}});
|
| 128 |
function initWorker(){worker?.terminate();workerReady=false;running=false;E.consoleInputForm.hidden=true;E.runButton.disabled=true;E.runButton.textContent="載入 Python…";if(!crossOriginIsolated||typeof SharedArrayBuffer==="undefined"){append("瀏覽器未啟用互動式 Python。請重新整理,並使用最新版 Chrome、Edge 或 Safari。\n");runState("error","環境不支援");return}inputBuffer=new SharedArrayBuffer(16392);inputControl=new Int32Array(inputBuffer,0,2);inputBytes=new Uint8Array(inputBuffer,8);worker=new Worker("/classroom-assets/py-worker.js?v=2026fall-4");worker.onmessage=e=>{const m=e.data;if(m.type==="ready"){workerReady=true;E.runButton.disabled=false;E.runButton.textContent="執行";runState("online","準備完成")}else if(m.type==="stream"){const decoder=m.channel==="stderr"?stderrDecoder:stdoutDecoder;append(decoder.decode(m.bytes,{stream:true}))}else if(m.type==="input_request"){E.consoleInputForm.hidden=false;E.consoleInput.value="";E.runStatusText.textContent="等待輸入";E.consoleInput.focus()}else if(m.type==="done"){E.consoleInputForm.hidden=true;runState("success","執行成功")}else if(m.type==="python_error"){E.consoleInputForm.hidden=true;if(m.text)append(`${m.text}\n`);runState("error","程式錯誤")}else if(m.type==="load_error"){append(`Python 載入失敗:${m.text}\n`);runState("error","載入失敗")}};worker.onerror=e=>{append(`Python 執行器錯誤:${e.message}\n`);runState("error","執行器錯誤")}}
|