第一次看到一份 CSV,最有用的能力是知道自己正在操作什麼:一列代表一張訂單,還是一個商品?金額欄真的是數字,還是看起來像數字的文字?少了一個值,程式會報錯、跳過,還是悄悄產生另一種結果?
這篇用一份只有五筆資料的訂單表,把這些問題實際走一遍。完成後,你會有一個可以重跑的練習,能算出訂單金額、挑出指定訂單、看出缺失值,也能辨認資料的維度。
先分清三個工具的工作:Python 是執行程式的語言;pandas 提供有列、欄與標籤的資料表;NumPy 提供適合數值運算的多維陣列。它們會一起使用,不需要選邊站。
先準備一個能重跑的練習資料夾
本文範例已在 Python 3.10.12、NumPy 1.26.3、pandas 2.2.2 執行核對。這是本次驗證環境,不是宣稱它們是最新版本。為了讓第一次練習的結果容易對照,可以先使用同樣版本。
開始前需要已安裝 Python 3.10,並能在 WSL 執行 python3 --version,或在 Windows 執行 py -3.10 --version 確認版本。尚未安裝時,可先參考 Python 官方下載頁。
建立一個資料夾,放入 orders.csv 和 lesson.py。以下所有 Python 區塊依順序放進同一份 lesson.py,CSV 則使用這份完整內容。五筆訂單都是本文設計的教學資料,金額單位假設為元,配送時間單位為分鐘。
order_id,city,quantity,unit_price,delivery_minutes
1001,台北,2,100,14
1002,台中,1,200,18
1003,高雄,3,100,21
1004,台北,2,150,17
1005,台中,1,200,
在 WSL/Linux 的練習資料夾內,可以這樣建立獨立環境並執行:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install numpy==1.26.3 pandas==2.2.2
python lesson.py
原生 Windows 的 PowerShell 則可使用下列指令;這裡直接呼叫虛擬環境的 Python,不需要先啟用環境。Windows 指令是操作說明,本次實測在 WSL 進行。
py -3.10 -m venv .venv
.\.venv\Scripts\python.exe -m pip install numpy==1.26.3 pandas==2.2.2
.\.venv\Scripts\python.exe lesson.py
如果出現找不到 orders.csv,先檢查終端機是否位在這個練習資料夾。相對路徑會從目前工作目錄尋找檔案;它不會自動猜測 CSV 存在哪裡。
讀進來之後,先知道一列、一欄各代表什麼
我們把一列定義為一張訂單;五個欄位分別記錄訂單編號、城市、數量、單價與配送時間。這個「一列代表什麼」的定義要先確定,否則後面連筆數都可能算錯。
import numpy as np
import pandas as pd
df = pd.read_csv("orders.csv", dtype={"order_id": "string"})
print("原始形狀:", df.shape)
print(df.head().to_string(index=False))
print("缺失數:", df.isna().sum().to_dict())
df 是我們替這張資料表取的變數名。它的 shape 會是 (5, 5),依序表示五列、五欄。head() 方便先看前幾筆;本例只有五筆,因此會全部顯示。
我們刻意把 order_id 讀成文字型態。它雖然由數字組成,卻是識別碼;把編號相加沒有業務意義。資料若帶有前導零,也應在讀檔時保留,而不是等零消失後再補猜。
缺失值檢查會指出 delivery_minutes 少一筆。這時先記下缺口,不要立刻用 0 或平均值填進去。缺什麼、為什麼缺,會影響該如何處理。
一個欄位是 Series;保留表格形狀則是 DataFrame
先執行這兩行,再看結果:
print("Series:", df["quantity"].shape)
print("DataFrame:", df[["quantity"]].shape)
第一個結果是 (5,),第二個是 (5, 1)。多出來的一層中括號,傳入的是「欄位名稱清單」,因此 pandas 回傳資料表。後面把資料交給模型時,這個差別經常就是維度錯誤的來源。
先挑欄、再挑列,把篩選條件讀成一句話
只想看訂單編號和城市,可以指定欄位清單:
print(df[["order_id", "city"]].to_string(index=False))
只想看台北的訂單,則先建立一個逐列判斷的條件:
taipei = df["city"] == "台北"
print("逐列條件:", taipei.tolist())
print(df.loc[taipei].to_string(index=False))
這個條件會得到 [True, False, False, True, False]。True 的兩列留下來,對應訂單 1001 與 1004。它不是只有一個「整張表是不是台北」的判斷,而是每一列各有一個結果。
兩種操作可以一起寫。先不用記很多語法,先把 .loc[列條件, 欄位] 讀成一句話:符合哪些列,再留下哪些欄。
print(df.loc[taipei, ["order_id", "quantity"]].to_string(index=False))
計算新欄位:先理解逐項運算,再回到訂單金額
NumPy 的陣列(array)可以把一組同型態的數值放在一起,進行逐項或整體計算。
先看一個常見差別:Python 清單相加是串接;NumPy 陣列相加,對形狀相容的陣列則進行逐項運算。
print("清單相加:", [1, 2] + [1, 1])
a = np.array([1, 2])
b = np.array([1, 1])
print("陣列相加:", (a + b).tolist())
兩個結果分別是 [1, 2, 1, 1] 和 [2, 3]。符號都是 +,資料結構不同,意義就不同。
回到訂單。我們可以直接對 pandas 的兩個欄位相乘:
df["amount"] = df["quantity"] * df["unit_price"]
print("每筆金額:", df["amount"].tolist())
print("總金額:", int(df["amount"].sum()))
print("各城市金額:", df.groupby("city")["amount"].sum().to_dict())
五筆金額依序是 [200, 200, 300, 300, 200],總額為 1,200。各城市加總是台北 500、台中 400、高雄 300。
這裡是同一張表的兩個 Series 相乘,索引一一對應。若把來自不同表、索引不同的 Series 直接相乘,pandas 的標籤對齊可能產生缺失值;它不一定按照你眼前看到的位置硬配對。
接著挑出金額至少 300 的訂單:
large_orders = df.loc[df["amount"] >= 300, ["order_id", "city", "amount"]]
print(large_orders.to_string(index=False))
結果應只有 1003 和 1004。你現在已經把「計算欄位」和「依條件取資料」接起來了。
切片與聚合:取出一段,或濃縮成一個值
下面改用三個小數字示範一維切片;來源圖片另外用二維陣列說明同一條切片規則。兩者都不是把訂單改成三筆。
data = np.array([1, 2, 3])
print("第一項:", int(data[0]))
print("前兩項:", data[:2].tolist())
print("從第二項起:", data[1:].tolist())
NumPy 的基本切片通常回傳原陣列的檢視(view),不是獨立副本。若準備修改切出來的資料、又不希望動到原陣列,可以明確使用 .copy()。這個行為可在 NumPy 官方入門文件的切片範例中對照。
first_two = data[:2].copy()
first_two[0] = 99
print("原陣列仍是:", data.tolist())
print("最大/最小/總和:", int(data.max()), int(data.min()), int(data.sum()))
原陣列仍為 [1, 2, 3];最大值、最小值與總和則為 3、1、6。
二維陣列的 shape、轉置與 reshape,分別在改什麼?
matrix = np.array([[1, 2], [3, 4], [5, 6]])
print("二維形狀:", matrix.shape)
print("第一列第二欄:", int(matrix[0, 1]))
print("前兩列的第一欄:", matrix[:2, 0].tolist())
shape 是 (3, 2);matrix[0, 1] 是 2;matrix[:2, 0] 則是 [1, 3]。二維索引裡,逗號前選第一個軸,逗號後選第二個軸;在這個表格例子中,就是列與欄。
轉置 .T 交換這個二維陣列的兩個軸,原本直向的第一欄,會變成橫向的第一列:
print("轉置:", matrix.T.tolist())
結果是 [[1, 3, 5], [2, 4, 6]],形狀變成 (2, 3)。
reshape 則按指定形狀重新安排元素。以這個預設順序的例子來看:
print("重新塑形:", matrix.reshape(2, 3).tolist())
結果是 [[1, 2, 3], [4, 5, 6]]。它和轉置的形狀相同,內容的位置卻不同。
因此,模型抱怨形狀不合時,不要直接把資料 reshape 成能執行的樣子。先弄清楚每個軸代表樣本、特徵、時間還是其他維度,才能知道應該怎麼改。
收尾前,回頭檢查缺失值與計算的分母
最後回到配送時間。pandas 的平均值計算預設跳過缺失值:
known = df["delivery_minutes"].notna()
print("已知配送時間筆數:", int(known.sum()))
print("已知資料平均分鐘:", float(df["delivery_minutes"].mean()))
結果是四筆、17.5 分鐘。這是四筆已知配送時間的平均,不能宣稱為五張訂單的真實平均。第五張訂單為什麼沒有值,是還沒送到、資料漏接,還是紀錄格式出了問題,需要另外追查。
若你的執行結果同時符合以下三件事,這次練習的主要步驟就接起來了:
- 原始資料為五列五欄,配送時間恰有一個缺失值。
- 五張訂單總金額是 1,200,金額至少 300 的訂單只有 1003 和 1004。
- 已知配送時間有四筆,平均 17.5 分鐘;轉置與 reshape 得到的元素位置不同。
下一篇〈平均值不夠:分布、機率、抽樣與不確定性〉會接著問:即使這個平均算對了,它是否足以描述讀者真正關心的情況?
來源與延伸閱讀
- pandas,What kind of data does pandas handle?:DataFrame 與 Series 原圖。
- pandas,How do I select a subset of a DataFrame?:選欄、選列與
.loc。 - NumPy,The absolute basics for beginners:陣列、切片、shape 與 view。
- Scientific Python Lectures,NumPy arrays與數值運算:圖片和原頁輸出取自Scientific Python Lectures contributors,依CC BY 4.0使用;原圖數值保留,裁切與重新渲染逐張註明。訂單CSV、本文程式與教學情境另行設計。
這份練習沒有訓練模型,也沒有比較套件效能。它先建立能讀懂資料結構與核對結果的基礎。
圖像授權聲明
以下保留本篇pandas原圖所附BSD條款、著作權與免責聲明。這不表示原作者為本站內容背書。
BSD 3-Clause License
Copyright (c) 2008-2011, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
All rights reserved.
Copyright (c) 2011-2026, Open source contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
封面沿用本篇已註明來源的配圖,僅等比縮放並加白邊以保留全圖;圖片原有的署名與授權條件同樣適用於封面版本。
留言0
登入後就能留言。
登入