Home
Explore Uedu
Student Console
Register as Member/Login
(2) In future presentations of the research findings, in addition to the course project website and public presentations, your real name and personal information will not appear in this research report. If you are interested in the research results, we can provide you with an executive summary after the study is completed.
問卷中心
Teacher Console
Course Setup
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

0

Local AI

--

CISOSE26 本地 AI UG26
政治大學 AQI 18 29°C

AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

用 Python 做資料分析

用 Python 做資料分析:numpy、pandas 與 matplotlib 實戰

從一份成績 CSV 出發,用向量化陣列、DataFrame 與視覺化串起完整工作流,並接回優統計與優智慧 AI。

從一份成績 CSV 開始:你的第一場資料偵探任務

假設你手上有一份匯出的課堂資料:三百位同學、五次小考、出席率、還有一欄「最後總成績」。老師問你一個看似簡單的問題:「出席率高的同學,成績真的比較好嗎?」

你可以打開試算表,一格一格地框選、排序、目測。但當資料變成三千筆、三萬筆,或是每天自動更新時,手動的方法就崩潰了。這時候,Python 的資料分析三劍客——numpypandasmatplotlib——會把這份偵探任務變成幾行可重複執行的程式。更棒的是,這條工作流不會停在「畫一張圖」,它會自然接到「優統計」的敘述統計與迴歸,以及「優智慧 AI」的資料前處理。

這篇文章帶你一步一步動手,從原始的數字陣列,走到一個能回答真實問題的分析腳本。

用 Python 做資料分析概念示意圖

numpy:把「一堆數字」變成會算術的陣列

先從最底層說起。如果你只用 Python 內建的串列(list)來存一萬個分數,要把每個分數乘以 1.1(加分),你得寫一個迴圈:

scores = [62, 75, 88, 91, 54]
adjusted = []
for s in scores:
    adjusted.append(s * 1.1)
print(adjusted)
# 輸出:[68.2, 82.5, 96.8, 100.10000000000001, 59.400000000000006]

這能跑,但既冗長又慢。numpy 提供了 ndarray(N 維陣列),讓你把整個運算「一次套用到所有元素」,這叫做向量化(vectorization)

import numpy as np

scores = np.array([62, 75, 88, 91, 54])
adjusted = scores * 1.1          # 一行,整個陣列一起算
print(adjusted)
# 輸出:[68.2 82.5 96.8 100.1 59.4]

注意 scores * 1.1 並沒有迴圈,卻把每個元素都乘上了 1.1。這種「純量對整個陣列」的運算稱為廣播(broadcasting)。numpy 還內建大量的統計函式,直接對陣列操作:

print(scores.mean())   # 平均
# 輸出:74.0
print(scores.std())    # 母體標準差
# 輸出:14.177446878757825
print(scores.max(), scores.min())
# 輸出:91 54
print((scores >= 60).sum())   # 及格人數:布林陣列加總
# 輸出:4

最後一行特別值得玩味:scores >= 60 會回傳一個布林陣列 [True True True True False],而 True 在求和時當作 $1$、False 當作 $0$,所以 .sum() 直接數出及格人數。這種「用布林陣列當遮罩」的思路,是整個資料分析的核心慣用法。

我們也可以用布林遮罩篩選元素:

passing = scores[scores >= 60]   # 只留下及格的
print(passing)
# 輸出:[62 75 88 91]

向量化的好處不只是程式變短,更重要的是——這點我們留到最後一節從底層解釋。現在先記住一個原則:在 numpy / pandas 裡,能不寫 Python 迴圈就不寫

pandas:給資料一個「表格」的形狀

numpy 的陣列是純數字的方陣,但真實資料有欄位名稱、有不同型別(姓名是字串、分數是數字、出席是日期)。這時候就輪到 pandasDataFrame 登場——你可以把它想成「程式版的試算表」。

讀進一份 CSV

假設我們有一個 grades.csv

name,attendance,quiz_avg,final
Amy,0.95,82,88
Ben,0.60,55,51
Cathy,0.88,79,84
Dan,0.45,48,40
Eva,0.92,90,93

讀進來只要一行:

import pandas as pd

df = pd.read_csv("grades.csv")
print(df.head())     # 看前幾列
print(df.shape)      # 輸出:(5, 4)  → 5 列、4 欄
print(df.dtypes)     # 每欄的資料型別

df.head() 預設顯示前 5 列,是你拿到任何資料的第一個動作。df.describe() 則一次給你所有數值欄的敘述統計——這正是「優統計」敘述統計的起點:

print(df.describe())
# 輸出(節錄):
#        attendance   quiz_avg      final
# count    5.000000   5.000000   5.000000
# mean     0.760000  70.800000  71.200000
# std      0.218861  17.633774  23.491488
# min      0.450000  48.000000  40.000000
# ...

篩選:用條件挑出你要的列

延續開頭的問題,我們想看「出席率高於 0.8」的同學:

high_attend = df[df["attendance"] > 0.8]
print(high_attend[["name", "attendance", "final"]])
# 輸出:
#     name  attendance  final
# 0    Amy        0.95     88
# 2  Cathy        0.88     84
# 4    Eva        0.92     93

df["attendance"] > 0.8 跟 numpy 一樣回傳一個布林序列,放進 df[...] 就會把為 True 的列留下。多個條件要用 &(且)、|(或)連接,而且每個條件都要用括號包起來(這是初學者最常踩的雷):

# 出席率高「且」小考平均及格
mask = (df["attendance"] > 0.8) & (df["quiz_avg"] >= 60)
print(df[mask]["name"].tolist())
# 輸出:['Amy', 'Cathy', 'Eva']

groupby:分組後再彙總

groupby 是 pandas 最有威力的工具。先幫每位同學貼上「出席等級」標籤,再分組算平均:

# 用 numpy 的條件函式產生分組欄位
df["attend_level"] = np.where(df["attendance"] >= 0.8, "高出席", "低出席")

summary = df.groupby("attend_level")["final"].agg(["mean", "count"])
print(summary)
# 輸出:
#                    mean  count
# attend_level
# 低出席         45.500000      2
# 高出席         88.333333      3

短短三行,我們就回答了開頭的問題:高出席組的總成績平均 88.3,低出席組只有 45.5。groupby(欄位)[目標欄].agg([...]) 是一個你會用一輩子的句型——「依某欄分組,對另一欄做彙總」。

matplotlib:讓數字說話

數字再清楚,也不如一張圖直觀。matplotlib 是 Python 最基礎的繪圖庫,pandas 與它無縫整合。畫一張「出席率 vs 總成績」的散佈圖:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6, 4))
ax.scatter(df["attendance"], df["final"], s=80, color="#3C938A")
ax.set_xlabel("出席率")
ax.set_ylabel("總成績")
ax.set_title("出席率與總成績的關係")
plt.tight_layout()
plt.savefig("scatter.png", dpi=120)   # 存檔,不一定要 show()

幾個慣例值得記住:用 fig, ax = plt.subplots() 取得「畫布」與「座標軸」物件,之後所有設定都掛在 ax 上,這是比舊式 plt.plot() 更清楚、更好維護的物件導向寫法。畫長條圖呈現分組結果也很直接:

fig, ax = plt.subplots(figsize=(5, 4))
summary["mean"].plot(kind="bar", ax=ax, color=["#3C6F9A", "#00b297"])
ax.set_ylabel("平均總成績")
ax.set_title("不同出席等級的平均成績")
plt.tight_layout()
plt.savefig("bar.png", dpi=120)

summary["mean"].plot(kind="bar") 直接把 pandas 的彙總結果畫成長條圖——這就是三劍客協作的縮影:numpy 算、pandas 整理、matplotlib 呈現。

動手寫一段:一個完整的迷你分析腳本

把上面所有片段串成一個能獨立執行的腳本。它建立資料、做敘述統計、分組彙總、並計算一個簡單的相關係數(這一步就踏進了「優統計」的領域):

import numpy as np
import pandas as pd

# 1. 建立資料(實務上改成 pd.read_csv("grades.csv"))
data = {
    "name":       ["Amy", "Ben", "Cathy", "Dan", "Eva"],
    "attendance": [0.95, 0.60, 0.88, 0.45, 0.92],
    "quiz_avg":   [82, 55, 79, 48, 90],
    "final":      [88, 51, 84, 40, 93],
}
df = pd.DataFrame(data)

# 2. 敘述統計(接「優統計」敘述統計)
print("總成績平均:", round(df["final"].mean(), 1))
print("總成績標準差:", round(df["final"].std(), 1))

# 3. 分組彙總
df["attend_level"] = np.where(df["attendance"] >= 0.8, "高出席", "低出席")
print(df.groupby("attend_level")["final"].mean())

# 4. 出席率與成績的相關係數(接「優統計」迴歸的前奏)
corr = df["attendance"].corr(df["final"])
print("出席率與總成績的相關係數:", round(corr, 3))
# 輸出:
總成績平均: 71.2
總成績標準差: 23.5
attend_level
低出席    45.5
高出席    88.3
Name: final, dtype: float64
出席率與總成績的相關係數: 0.974

相關係數 $0.974$ 接近 $1$,量化地確認了我們的直覺:出席率與成績高度正相關。下一步如果要建立預測模型(例如「給定出席率,預測成績」),就會用到「優統計」的線性迴歸——而它收的輸入,正是這份整理好的 DataFrame。

接回優統計與優智慧 AI

這條工作流的價值在於它是各模組共同的「資料底座」:

  • 接「優統計」(敘述統計、迴歸)df.describe() 給你平均、標準差、四分位數;df["x"].corr(df["y"]) 給你相關係數。要做線性迴歸時,常見的 scikit-learn 模型直接吃 numpy 陣列或 DataFrame:model.fit(df[["attendance"]], df["final"])。換句話說,pandas 整理好的資料就是迴歸的原料。
  • 接「優智慧 AI」(資料前處理):機器學習模型不能直接吃髒資料。處理缺失值(df.fillna(df.mean()))、特徵縮放、把類別欄轉成數字(pd.get_dummies(df["attend_level"]))——這些前處理幾乎全在 pandas 裡完成。可以說,任何 AI 專案的第一個小時,幾乎都花在 pandas 上。
# 前處理示範:補缺失值 + 類別轉 one-hot
df_clean = df.copy()
df_clean["quiz_avg"] = df_clean["quiz_avg"].fillna(df_clean["quiz_avg"].mean())
features = pd.get_dummies(df_clean[["attendance", "attend_level"]])
print(features.columns.tolist())
# 輸出:['attendance', 'attend_level_低出席', 'attend_level_高出席']

常見錯誤

  • 多條件篩選忘了加括號df["a"] > 1 & df["b"] < 2 會因運算子優先序而報錯。& 的優先序高於比較運算子,務必寫成 (df["a"] > 1) & (df["b"] < 2),而且用 & / | 而不是 Python 的 and / or
  • 在 DataFrame 上手寫 for 迴圈逐列計算for i in range(len(df)): df.loc[i, "x"] = ... 又慢又容易出錯。先想想能不能用向量化或 groupby 取代——九成情況可以。
  • 鏈式賦值的 SettingWithCopyWarningdf[df["a"] > 1]["b"] = 0 可能改不到原資料。要修改請用 df.loc[df["a"] > 1, "b"] = 0 一次定位列與欄。
  • 混淆母體與樣本標準差numpy.std() 預設是母體標準差(分母 $N$),pandas.std() 預設是樣本標準差(分母 $N-1$)。做統計推論時這個差別會影響結果,要看清楚你用的是哪一個。
  • 以為讀進來的型別都對:CSV 全是文字,read_csv 會猜型別但不一定準。拿到資料先 df.dtypes 檢查,數字欄若變成 object,多半是有非數字的髒值混在裡面。

深入探討(研究所視角)

向量化為什麼比 Python 迴圈快?

關鍵在於 Python 是直譯式、動態型別的語言。當你寫 for s in scores: s * 1.1,每一圈直譯器都要做一連串昂貴的工作:檢查 s 是什麼型別、找出 * 對這個型別該怎麼做、把結果裝箱成一個新的 Python 物件。對一萬個元素,這些「解譯開銷」就重複一萬次。

numpy 的陣列則完全不同。一個 ndarray 在記憶體裡是一塊連續(contiguous)、同質(homogeneous)型別的緩衝區——一萬個 float64 就是緊密排列的 $10000 \times 8$ 位元組,中間沒有 Python 物件的指標與標頭。當你寫 scores * 1.1,numpy 把整個運算交給底層用 C 寫好的迴圈執行:型別只檢查一次、迴圈在編譯後的機器碼裡跑、完全不碰 Python 直譯器。這通常帶來數十到上百倍的加速。

連續記憶體還帶來第二層好處:CPU 快取友善。現代處理器一次會把鄰近的一整段記憶體載入快取(cache line),連續存放的陣列正好讓每次載入都「物盡其用」;而 Python 串列存的是一堆指向四散物件的指標,存取時不斷在記憶體裡跳躍,快取命中率低。再加上 numpy 的 C 迴圈能讓編譯器啟用 SIMD(單指令多資料)向量指令,一道指令同時處理多個浮點數——這就是「向量化」一詞的硬體根源。

所以「能不寫迴圈就不寫」不只是風格偏好,而是把計算從「慢速的 Python 解譯層」推到「快速的 C/硬體層」的具體策略。

pandas 背後其實是 numpy

理解 pandas 的效能與行為,最好的角度是:DataFrame 的每一欄(Series),底層多半就是一個 numpy 陣列。你可以親眼驗證:

print(type(df["final"].to_numpy()))
# 輸出:<class 'numpy.ndarray'>

這解釋了許多現象。為什麼 df["final"] * 2 這麼快?因為它直接調用 numpy 的向量化乘法。為什麼數值欄要型別一致才有效率?因為背後是同質型別的 numpy 緩衝區;一旦混入字串,整欄退化成 object 型別(一堆 Python 物件指標),就失去了向量化優勢,這也是 dtype 變成 object 通常是效能警訊的原因。

更廣地看,這正是整個 Python 資料科學生態的設計哲學:以 numpy 的 ndarray 作為共通的資料交換格式。pandas 在它之上加了標籤與彙總語意;matplotlib 直接畫 numpy 陣列;scikit-learn 的模型輸入輸出也是 numpy 陣列。於是一條典型工作流長這樣:

pandas 讀檔與清理轉成 numpy 陣列scikit-learn 訓練模型numpy 算出結果pandas 整理 / matplotlib 視覺化

當你之後接觸到 PyTorch 的 tensor、或處理大到放不進記憶體的資料而改用 Dask、Polars 時,會發現它們大量沿用同一套「陣列與向量化」的心智模型。換句話說,這篇文章裡那個小小的 np.array([62, 75, 88, 91, 54]),其實是通往整個現代資料科學與 AI 工程的第一塊基石。把向量化、DataFrame、視覺化這三件事練熟,你就握住了之後所有進階主題的鑰匙。

AI 共讀助教正在陪你讀:用 Python 做資料分析:numpy、pandas 與 matplotlib 實戰
嗨!我是這篇文章的共讀助教,只根據〈用 Python 做資料分析:numpy、pandas 與 matplotlib 實戰〉的內容回答。可以問我「解釋某段」「舉個例子」「出題考我」,或反白文中段落後點下方「解釋選取段落」。