Learning Objectives
- 用 df["欄"] 與 df[["欄", "欄"]] 選出欄,說出結果是 Series 還是資料表
- 用 loc(標籤)與 iloc(位置)選出一塊資料,說出兩種切片在結尾的差別
- 用布林條件篩選列,以 &、|、~ 組合條件,並說出為什麼每個比較要加括號
- 用 isin 與 query 寫出常見的篩選,知道篩選後的索引不會重新編號
只看需要的那一塊
讀進一份資料表(DataFrame)後,分析幾乎都從「挑出一部分」開始:只看期末成績這一欄、只看微積分的修課紀錄、 只看缺席 5 次以上又不及格的人。挑得對,後面的平均、圖表才有意義。
pandas 有四種常用的挑法:
| 想做的事 | 寫法 | 例子 |
|---|---|---|
| 選欄 | df["欄"]、df[["欄", "欄"]] |
只看課程與期末 |
| 依標籤選列與欄 | df.loc[列, 欄] |
學號 S03 到 S06 的期中、期末 |
| 依位置選列與欄 | df.iloc[列, 欄] |
第 0~2 列、第 2~3 欄 |
| 依條件選列 | df[條件]、df.query("條件") |
期末不及格的人 |
這一課用 grades.csv(600 筆修課成績)與 students.csv(200 位學生)。兩份都是程式產生的模擬資料,不是任何學校的真實資料。
逐步看:單層括號與雙層括號
注意右邊記憶體區:final 只有一欄、沒有表頭;two 和 one 是資料表。
loc 看標籤,iloc 看位置
每一列都有一個標籤(索引,index),也有一個位置(從 0 開始數第幾列)。剛讀進來的表,兩者剛好一樣(0、1、2…), 所以初學時很容易混在一起;只要篩選或排序過,兩者就不一樣了。
loc |
iloc |
|
|---|---|---|
| 用什麼找 | 列標籤、欄名稱 | 列位置、欄位置(整數,從 0 起算) |
切片 a:b |
包含 b | 不含 b(和串列切片一樣) |
| 例子 | df.loc["S02":"S04", "final"] |
df.iloc[1:3, 0:2] |
逗號前面是列、後面是欄;只寫一個就是只選列(欄全部要)。df.loc["S03"] 取出一列,得到的是 Series。
逐步看:loc 與 iloc 選到的範圍
這個小表用學號當列標籤。看最後兩行:同一位學生,在篩選後的表裡位置變了,標籤沒變。
布林篩選:先算出一串 True/False
grades["final"] >= 90 不會只得到一個 True 或 False,而是每一列一個:一串和資料表一樣長的布林值(布林 Series)。
把這一串放進方括號 grades[...],留下的就是 True 的那些列。
兩個條件要用 &(而且)、|(或)連接,前面加 ~ 表示「不是」。每個比較都要加括號:
grades[(grades["course"] == "微積分") & (grades["final"] < 45)]
原因是 Python 的運算子優先順序:&、| 比 >=、== 先算。不加括號時,Python 會先算 "微積分" & grades["final"],
pandas 會出現錯誤。也不能用 and、or:它們要把整串布林值變成一個 True 或 False,pandas 不知道該怎麼變,所以會出現
「The truth value of a Series is ambiguous」錯誤。
兩個常用的簡寫:
grades["course"].isin(["英文", "經濟學"]):值在清單裡就是 True,比寫好幾個|清楚。grades.query("absences >= 5 and final < 60"):把條件寫成一個字串,欄名直接寫、可以用and、or。
逐步看:篩選 600 筆修課紀錄
看 high 在記憶體區的樣子:它是一串 True/False,長度和資料表一樣。
實作:住宿又認真讀書的學生
讀入 students.csv,選出住宿(dorm 是 "Y")而且每週讀書時數(study_hours)15 小時以上的學生,
只留 student_id、dept、study_hours 三欄,存進 busy。程式最後兩行會印出人數與前 5 列。
小測驗:選取與篩選
下面程式會印出什麼?
import pandas as pd
df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})
print(type(df["a"]).__name__, type(df[["a"]]).__name__)
下面程式會印出什麼?
import pandas as pd
df = pd.DataFrame({"x": [5, 6, 7, 8, 9]}, index=["a", "b", "c", "d", "e"])
print(len(df.loc["b":"d"]), len(df.iloc[1:3]))
df[df["final"] >= 60 & df["dept"] == "資工"] 執行時出現錯誤,該怎麼改?
哪一個和 df.query("absences >= 5 and final < 60") 選出相同的列?
下面程式會印出什麼?
import pandas as pd
s = pd.Series([10, 20, 30, 40])
t = s[s > 15]
print(t.iloc[0], t.loc[3])
重點整理
df["欄"]得到 Series(一維),df[["欄", "欄"]]得到資料表(二維)。loc用列標籤與欄名稱,切片包含結尾;iloc用位置(從 0 起算),切片不含結尾。逗號前是列、後是欄。- 篩選時先算出一串 True/False,再放進方括號;
sum()可以數出符合的筆數。 - 條件用
&、|、~組合,每個比較加括號;不能用and、or。isin與query讓常見的條件更好讀。 - 篩選後的索引沿用原本的標籤,不會重新編號,所以
loc與iloc的結果會不一樣。 - 下一課學排序(
sort_values)與新增計算欄位。