跳到主要內容

發表文章

目前顯示的是 10月, 2024的文章

Day 18 Pandas 資料清理

Day 18 Pandas 資料清理 引言 真實世界的資料幾乎都不乾淨:可能缺值、可能有重複列、可能有格式不一致、可能有離群值。資料科學家常說「資料清理佔了 80% 的時間」,剩下的 20% 才是建模與分析。Pandas 提供一整套清理工具,今天要學會的正是這些日常必備:缺失值處理、條件篩選、排序、去重、型別轉換,以及最實用的 groupby 分組聚合。這些動作不需要寫複雜的演算法,但每一招都得做扎實,模型訓練的成果才會穩定。 學完今天的內容,你會具備處理一份中等規模的真實表格資料的能力。從明天起,我們會進入視覺化階段,用 Matplotlib 與 Seaborn 把清理後的資料畫成圖表。資料清理與視覺化是資料分析的兩條腿,先把資料整理好,畫出來的圖才有意義。 缺失值處理:isnull、fillna、dropna Pandas 用 NaN(Not a Number)表示缺失值。可以用 isnull() 或 notnull() 判斷位置,再用 fillna() 填補、dropna() 丟棄。實務上建議先檢查缺失比例,決定要補還是刪:缺失很少就刪,缺失有規律或占比偏高就補。判斷缺失型態也是清理的關鍵:是「完全隨機缺漏」還是「某個族群特別容易漏」,處理方式會很不一樣。如果是後者,直接補平均反而會掩蓋問題,這時通常會加上「是否缺值」的衍生欄位給模型參考。 import pandas as pd import numpy as np df = pd.DataFrame({ "姓名" : [ "小明" , "小華" , "小美" , "小李" ], "數學" : [ 90 , np.nan, 95 , 70 ], "英文" : [ 78 , 80 , np.nan, 75 ], }) print (df.isnull()) # 標出每個位置是否缺失 print (df.isnull(). sum ()) # 每欄缺失值數量 # 刪除有缺失的列 df_drop = df.dropna() # 填補:固定值 df_fill_const = df.fil...

Day 17 Pandas 入門

Day 17 Pandas 入門 引言 NumPy 的陣列很適合做數值運算,但現實世界的資料多半是表格:有欄位名稱、有不同型別、可能缺值、可能來自 CSV 檔或資料庫。Pandas 正是為了處理這種「表格型資料」而生的套件,它的核心資料結構是 Series(一維)與 DataFrame(二維)。可以把它想成「Python 版的 Excel」,但能用程式靈活操作幾十萬、幾百萬列的資料,而且很多操作寫起來比 Excel 函式更直覺,也更容易自動化與重現。 今天會從安裝開始,認識 Series 與 DataFrame 的建立方式,學會用 read_csv() 讀取外部資料,並用 head()、info()、describe() 快速掌握一份陌生資料的輪廓。這些基本功是後續資料清理與分析的起點。建議手上準備一份自己的 CSV(學生資料、銷售紀錄、記帳檔都可以),實際跟著範例操作一次,印象會更深刻。如果沒有現成資料,也可以從政府開放資料平台下載一份小型 CSV,例如停車場、氣象、票房等都很常見。 安裝與匯入 Pandas 的安裝方式與 NumPy 一樣,都是用 pip。目前主流版本是 2.2.x(2024 年初發布),它在字串欄位、缺失值處理與 PyArrow 整合上有不少改進。建議把 numpy 與 pandas 一起裝好,後續資料處理的工具鏈大致就齊了。如果之後要做深度學習,torch 安裝時會順帶安裝 numpy,但 pandas 仍需要另外安裝。如果在 Linux 上遇到 pip 編譯失敗,可以先裝好 python3-dev 與 build-essential,這是某些版本的 pandas 編譯加速程式所需要的。 pip install pandas==2.2.3 numpy 匯入的慣例是 import pandas as pd,與 numpy 一樣用短別名減少打字。之後寫程式會大量用到 pd.xxx(),熟悉之後閱讀別人的程式碼會非常順。在 Jupyter Notebook 裡,也可以用 from pandas import *,但這會把 read_csv、DataFrame 等名稱一口氣匯入,容易和專案內自己定義的東西衝突,正式檔案不建議這樣寫。為了讓資料處理更靈活,Pandas 也常常和 matplotlib、seaborn 一起匯入...

Day 16 NumPy 運算與索引

Day 16 NumPy 運算與索引 引言 昨天認識了 ndarray 與基本運算,今天要把 NumPy 真正威力強大的部分學起來:索引、切片、廣播與統計函式。這幾個機制讓我們能用極短的程式碼完成大量資料的篩選、轉換與彙整,是資料科學家每天都在用的核心技能。學會之後,從此處理幾萬甚至幾百萬筆資料都不再痛苦,可以說是從「寫 Python」升級到「用 Python 做資料工作」的轉捩點。在真實的資料科學工作裡,NumPy 的角色雖然會逐漸被 Pandas 與 PyTorch 取代,但底層運算邏輯完全相同,所以這幾天打好的基礎會一直派上用場。 這幾個觀念也會一路用到後續的 Pandas 與 PyTorch:Pandas 的 DataFrame 篩選語法借鏡自 NumPy 的布林索引,PyTorch 的張量運算更直接延伸 NumPy 的廣播規則。先把基礎打穩,後面就會順很多。建議在進入今天的內容前,確認昨天安裝的 NumPy 還能正常匯入,並熟悉昨天的 ndarray 建立方式。今天的範例都不大,可以放心全部跑過一遍,確認輸出與預期一致。 索引與切片:取出你想要的部分 NumPy 的索引寫法和 Python 串列很像:中括號內放索引值,從 0 開始。多維陣列則用逗號分隔,例如 x[row, col]。切片用冒號寫成 x[start:stop:step],可以省略任何一段。掌握這些基本寫法之後,就能用一致的方式處理向量、矩陣甚至更高維度的張量。例如影像資料常用 (高度, 寬度, 通道) 三維陣列,這時用 x[:, :, 0] 就能取出第一個通道的所有像素。 import numpy as np x = np.arange( 1 , 13 ).reshape( 3 , 4 ) print (x) # 輸出: # [[ 1 2 3 4] # [ 5 6 7 8] # [ 9 10 11 12]] print (x[ 0 ]) # 第一列:[1 2 3 4] print (x[ 0 , 0 ]) # 第一列第一個:1 print (x[ - 1 , - 1 ]) # 最後一列最後一個:12 print (x[ 1 , :]) # 第二列整列 print (x[:, 1 ]) ...

Day 15 NumPy 入門

Day 15 NumPy 入門 引言 Python 內建的串列很靈活,但當資料量變大、想做數值運算時,速度與便利性都不夠。NumPy 是 Python 資料科學的基石,它的核心是 ndarray(N-dimensional array,多維陣列),可以用來表示向量、矩陣、甚至更高維度的資料。相較於純 Python 串列,NumPy 陣列在記憶體配置上更連續,運算速度往往快上幾十倍甚至上百倍,這在處理影像、訊號、機器學習特徵時特別明顯。從這個章節開始,你的程式碼會越來越像在做數學運算,而不是在處理「一堆容器」了。 從今天起,我們正式進入資料處理的章節。先把 NumPy 的基礎打穩,後面 Pandas 的 DataFrame 內部其實就是用 NumPy 陣列儲存資料,再後面 PyTorch 的張量(Tensor)設計也深受 NumPy 影響。今天會學會安裝、建立陣列、認識 shape 與 dtype,並做最基本的運算。學完之後,你會對「為什麼資料科學幾乎離不開它」有具體感受,也能在自己的腳本裡取代一些低效的 Python 迴圈。 安裝與匯入 NumPy NumPy 並不在 Python 標準庫裡,需要透過 pip 安裝。目前主流的穩定版本是 2.1.x(2024 年中發布),2.0 起對 dtype 與字串處理做了不少重整,如果你從 1.x 升級上來,可能會看到一些 deprecation 訊息。建議搭配 venv 為資料科學專案建立乾淨環境,並把版本固定在 requirements.txt 裡,這樣未來升級時能事先看到警告,不會默默壞掉。如果是在 Windows 上安裝,pip 會自動抓預先編譯的 wheel 套件,多數情況下不需要額外的 C 編譯工具。 pip install numpy==2.1.3 安裝完成後,慣例上用 import numpy as np 匯入,並用 np 當作別名,這是整個資料科學社群共同的命名習慣,讀別人的程式碼也會看到。np 這個短短的別名讓後續寫程式時可以少打很多字,也讓公式化的運算(例如 np.dot(A, B))看起來更像數學表達式。即使到了 PyTorch 章節,你也會看到 import torch 之後,許多公式寫法跟 NumPy 幾乎一樣,這是 NumPy 在資料科學領域影響深遠的證明。 imp...

Day 14 物件導向進階與小專案

Day 14 物件導向進階與小專案 引言 昨天學會了類別、屬性、方法與特殊方法,已經能寫出具備基本結構的物件。今天要把物件導向的三大支柱補完:繼承讓我們在既有類別上延伸新功能、封裝把內部細節藏起來保護資料、多型則讓不同物件對同一訊息有不同反應。學完這些,就能寫出真正可重複使用、容易維護的類別設計。這三個觀念不只在 Python 中重要,幾乎所有主流的物件導向語言(Java、C++、C#、Kotlin)都把它們列為核心。 為了讓觀念落地,我們會在最後用這些機制實作一個簡單的記帳系統:包含兩種類型的交易(收入與支出)、能計算總額、也能列出明細。這個小專案同時會用到檔案讀寫,把前面學的內容串起來。專案規模雖然小,卻已經用到了類別階層、方法覆寫、多型與檔案 I/O,是個很棒的綜合練習。完成後你會發現,這個記帳系統其實已經具備了一個真實 App 的核心架構:資料模型(類別)+ 商業邏輯(方法)+ 儲存層(JSON)。 繼承:讓子類別複用父類別 繼承(inheritance)讓新類別複用既有類別的屬性與方法。被繼承的稱為父類別(base class),衍生的新類別稱為子類別(derived class)。子類別除了擁有父類別的功能,還能新增自己的功能,或覆寫(override)父類別的方法。這樣做的好處是「共同的部分寫一次就好」,差異的部分交給各子類別自己實作。 class Animal: def __init__(self, name): self.name = name def speak(self): raise NotImplementedError( "子類別必須實作 speak()" ) class Dog(Animal): def speak(self): return f "{self.name} 汪汪叫" class Cat(Animal): def speak(self): return f "{self.name} 喵喵叫" animals = [Dog( "小黑" ), Cat( "小花" ), Dog...

Day 13 物件導向入門

Day 13 物件導向入門 引言 過去十二天,我們寫的都是函式導向的程式:用變數儲存資料、用函式處理資料,再用串列、字典把資料裝在一起。這種寫法在規模小的時候很直覺,但專案一長,函式之間的關係就會變得難以追蹤。光是要記住「哪些函式會改哪些變數」「哪些變數在哪個模組裡」就要花上不少心思,維護起來也容易踩到地雷。 物件導向(OOP)提供了另一種思路:把「資料」與「操作資料的函式」綁在一起,這個組合就稱為「物件」。學會這種寫法,程式碼會更像在描述真實世界的角色與行為,閱讀起來也更貼近自然語言。學物件導向不只是為了寫大型軟體,更是為了看懂接下來的 PyTorch 神經網路:PyTorch 把每一層神經網路定義成一個 class(類別),模型的每一層都是物件;如果你不懂 self、__init__ 與方法呼叫,看 PyTorch 模型程式碼會霧煞煞。今天先用簡單的生活範例把基礎打穩,明天再進到繼承與小專案。 類別與物件:class 的基本結構 類別(class)是物件的藍圖,物件(object)是類別的實例。可以把類別想成「房屋的設計圖」,把物件想成「依照設計圖蓋出來的每一棟房子」。在 Python 中定義類別非常簡單,使用 class 關鍵字即可。類別內的函式稱為方法(method),第一個參數慣例上命名為 self,代表「這個物件自己」。 class Dog: # 建構子:建立物件時自動執行 def __init__(self, name, age): self.name = name # 實例屬性 self.age = age def bark(self): print (f "{self.name} 汪汪叫!" ) def introduce(self): print (f "我叫 {self.name},今年 {self.age} 歲" ) # 建立物件 dog1 = Dog( "小黑" , 3 ) dog1.bark() # 輸出:小黑 汪汪叫! dog1.introduce() # 輸出:我叫 小黑,今年 3 歲 __init__...

Day 12 模組與套件

Day 12 模組與套件 引言 當專案開始長大,把所有程式碼塞進同一個檔案會變得難以維護。這時候把程式拆成「模組」與「套件」,讓不同檔案各自負責一部分功能,是寫大型 Python 專案的基礎。模組化之後,每個人可以專心修改自己負責的部分,別人只需要知道「這個模組提供哪些函式」,而不必從頭讀完所有程式碼。 另一方面,Python 之所以好用,是因為有著豐富的第三方套件生態系:NumPy、Pandas、Requests、Django……,只要會用 pip 就能把它們裝進自己的專案。今天就要把這兩件事一起搞懂:先學怎麼組織自己的程式碼,再用 pip 與 venv 把外部套件與環境管理好。學會模組與套件之後,你就能把過去十天寫的小工具整理成可重複使用的程式庫,也準備好迎接接下來的資料科學章節。 匯入模組:import 的各種寫法 Python 的模組其實就是一個 .py 檔案,檔案名稱就是模組名稱。例如有個 utils.py,就可以透過 import 把它載入並使用裡面的函式。最簡單的寫法是直接 import 整個模組,使用時要寫「模組名.函式名」。這種寫法雖然稍長,但能清楚看出函式來自哪個模組,閱讀性較好。 # utils.py 內有一個 greet 函式 # greet(name) 會回傳 "Hello, {name}" import utils print (utils.greet( "小明" )) # 輸出:Hello, 小明 如果只想要某幾個函式,可以用 from ... import,呼叫時就不必寫模組名。更進階的寫法還能搭配 as 替模組或函式取別名,避免名稱太長或撞名。 from utils import greet print (greet( "小華" )) # 直接呼叫,不用寫 utils. import numpy as np # 第三方套件常用別名 np import pandas as pd # 第三方套件常用別名 pd from math import sqrt as square_root print (square_root( 16 )) # 輸出:4.0 注意 from ... import * 雖然...

Day 11 檔案讀寫與例外處理

Day 11 檔案讀寫與例外處理 引言 到目前為止,我們的 Python 程式都在記憶體裡運作:變數、串列、字典這些資料,只要程式結束就消失了。如果想讓資料留下來,就得把結果寫到磁碟;想讀別人整理好的資料,也得從檔案讀進來。檔案讀寫是寫程式很常見的工作,不論是記錄使用者輸入、讀取設定檔、輸出報表,或是處理資料科學的 CSV,都脫離不了檔案操作。可以說,學會檔案讀寫之後,Python 才真正從「練習小工具」升級成「能處理真實資料的工具」。 不過,現實世界的檔案操作常常出問題:檔案可能不存在、權限不足、磁碟滿了,或讀到一半網路斷線。這時候如果沒有處理例外,程式就會直接當機,並把一段落落長的 Traceback 丟到使用者面前。今天除了學會基本的檔案讀寫,也會介紹 try / except 的機制,讓程式在出錯時能優雅地回應,而不是整支停下來。學完之後,你就能寫出對錯誤有耐受度的小工具,給同事或自己使用時也更安心。 讀取檔案:open() 與 with 敘述 Python 提供內建的 open() 函式來開啟檔案。最重要的兩個參數是檔案路徑與開啟模式:'r' 表示讀取(預設)、'w' 表示寫入、'a' 表示附加在檔尾。開啟後回傳的是一個檔案物件,可以呼叫 read()、readline()、readlines() 等方法讀取內容。讀取完成後要記得呼叫 close() 關閉檔案,否則可能會遺失部分資料、檔案被鎖住無法刪除,甚至在 Windows 上直接跳出「檔案已被另一個程式使用」的錯誤。 實務上更推薦用 with 敘述(context manager)。它會在區塊結束時自動關閉檔案,即使中間發生錯誤也會正確釋放資源。這種寫法不僅更安全,也讓程式碼更乾淨,省下手動呼叫 close() 的一行。 以下範例假設工作目錄下有一個 notes.txt (內容是幾行文字);如果手邊沒有這個檔案,可以先建立一個再跟著執行。 # 讀取整個檔案 with open( "notes.txt" , "r" , encoding = "utf-8" ) as f: content = f.read() print (content) ...

Day 10 容器型別進階操作

Day 10 容器型別進階操作 引言 Day 2 我們認識了 Python 的四大容器型別:串列、元組、字典與集合。經過一週的練習,你應該已經熟悉新增、刪除、修改、查詢這些基本動作。但在真實的程式裡,光靠基本操作往往不夠。我們經常需要「從一個串列產生出另一個串列」、「把多組資料配對起來」、「把容器當作函式的參數與回傳值」等,這些都是容器進階操作的範疇。 這篇文章會帶你認識 Python 容器型別的進階技巧:首先是「推導式(comprehension)」,這是 Python 寫出簡潔、有 Python 風格程式碼的招牌語法;接著是「解包(unpacking)」,讓你能一次取出多個值;然後介紹 enumerate() 與 zip() 這兩個走訪容器的利器;最後用一張表整理如何挑選容器型別,幫助你在實務上做出更合適的選擇。 推導式:以一個容器產生出另一個容器 推導式(comprehension)是 Python 用一行運算式產生新容器的寫法。串列推導式最常見,語法是「[運算式 for 元素 in 容器 if 條件]」,可以一次完成「篩選 + 轉換 + 收集」三件事。 # 把 1-10 的偶數平方後收集成串列 result = [n ** 2 for n in range ( 1 , 11 ) if n % 2 == 0 ] print (result) # [4, 16, 36, 64, 100] 這一行做了三件事:先用 range(1, 11) 走訪 1 到 10,再用 if n % 2 == 0 篩選偶數,最後用 n ** 2 把每個偶數平方並收集起來。對照傳統寫法,推導式通常更短也更直覺。 除了串列推導式,Python 也支援字典推導式與集合推導式,語法類似但用大括號: # 字典推導式:建立數字到平方的對應 squares = {n: n ** 2 for n in range ( 1 , 6 )} print (squares) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25} # 集合推導式:把字串中的字母去重 chars = {ch for ch in "hello world" if ch.isalpha()} print...