iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0

條件過濾是我們在處理資料最常遇到的需求~

先準備一下今天要用到的data。
做一個很常見的成績表。

df = pd.DataFrame({"name":pd.Series(["Mary", "John", "Cindy", "Bojack", "Zoy", "Taylor", "Denny", "Mike", "Bob", "Peter"]),
                  "grade":pd.Series(["6", "7", "8", "6", "7", "8", "6", "7", "8", "6"])}) 
df[["art","math","physics","history","english","sport"]] = np.random.randint(50, 100, size=(len(df), 6))

用bool篩選

bool篩選是利用我們提供條件讓程式判斷回傳true/false。

df["grade"] > 7 

這時pandas會回傳一個series,跟grade欄位長度一致的series,告訴我們grade中的每一列是否符合大於7(>7)的條件,依照我們的data會回傳如下的陣列。

false
false
true
false
false
true
false
false
true
false

除了用python的算符外,我們也可以使用series方法,以下兩行是一樣的效果

  • df["grade"] > 7
  • df["grade"].gt(7)

其他的比較運算符號

小於 大於 小於等於 大於等於 等於 不等於
< > <= >= == !=
lt gt le ge eq ne

如何在dataframe中篩選

把運算式放到df裡,pandas會回傳符合這個條件的所有欄位
df[df["grade"] > 7]

如何多欄位篩選

用位元算符相符,並且每個條件用()括號包起來:

  • & 代表 and
  • | 代表 or
  • ~ 代表 not
    為什麼一定要先用()括號包起來呢,因為位元運算符的優先性高於比較運算符,沒有用括號識別會引發can't compare 的錯誤。

#篩選名為Bojack的學生
df[df["name"] == "Bojack"]                     

#篩選大於7年級 "並且" 歷史大於等於80分的學生
df[(df["grade"] > 7) & (df["history"] >= 80)]  

#篩選物理大於85 "或者" 歷史大於等於80分的學生
df[(df["physics"] > 85) | (df["history"] >= 80)]  

#篩選除了Taylor外,art大於等於80分的學生
df[~(df["name"] == "Taylor") & (df["art"] >= 80)] 

字串過濾

df[df["name"].str.contains("M")]      #名字裡面有M的學生
df[df["name"].str.startswith("M")]    #名字以M開頭的學生 
df[df["name"].str.endswith("y")]      #名字以y結尾的學生 

多值比對 IN

如果有比對值不只一個,很多個or組起來不只打字很累還看得很亂,這就是in出現的好時機了
需求:篩選出名字為["Bojack", "Zoy"]的學生
df[df["name"].isin(["Bojack", "Zoy"])]

範圍過濾

#篩選英文分數介於60-90間的學生
df[df["english"].between(60, 90)]

用.query()

熟悉sql的朋友有福了,用query簡直小魚游回太平洋那麼悠游自在。

df.query("english > 60 and english < 80")
df.query("grade in [6, 7]")
df.query("grade not in [6, 7]")
df.query("not name == 'Bojack'")

#條件裡的grade要是很多個的話,可以先寫成陣列,再用@去接變數
inGrade = [6, 7]
df.query("grade not in @inGrade")

loc選擇器

雖然day2有簡單帶過,但它實在強大到可以再仔細講講

指定列/欄

df.loc[指定列, 指定欄]
指定列/欄可用於index label,list、切片、bool篩選
要用指定列,就必須先set index,選擇器才能用index label去找data

#先把index設為name,之後就可以用name來操作
df = df.set_index("name")
#回傳單一值:Mary這列的物理成績
df.loc["Mary", "physics"]
#回傳Series: 把Mary這列的所有值回傳series,並把欄名做為label
df.loc["Mary"]      # 單一值
#回傳DataFrame: Mary/Taylor的完整列
df.loc[["Mary", "Taylor"]]

#回傳DataFrame: 列Cindy~Taylor的欄位art~physic
df.loc['Cindy':'Taylor', "art":"physics"]
#回傳DataFrame: 所有列的欄位art~physic
df.loc[:, "art":"physics"]

有幾個地方要注意一下

  1. []括號一層還是兩層會有不同結果
    • df.loc["Mary"]
      回傳的是series,縱向資料
    • df.loc[["Mary"]]
      回傳的是dataframe,每一列都是一個橫向資料
  2. 若指定列/欄是一組陣列,要加[]括號
  3. 切片slicing的不需要[]括號
  4. 如果參數只給一個,預設會是列
    • df.loc["Mary"] 等同於 df.loc["Mary", :]
    • :表示全選
  5. 一般python的slicing是左閉右開,loc裡的slicing是閉區間,:右邊的值也會被包含。

bool篩選

前面說過篩選了,這邊就不多做介紹

#篩選出grade為7年級,僅列出欄位physics和math
df.loc[df["grade"] == 7, ["physics","math"]]

修改值

#把mary的數學成績改為100
df.loc["Mary", "math"] = 100
#把Bojack, Taylor的art成績改為93
df.loc[["Bojack", "Taylor"], "art"] = 93
#美術老師佛心大開,把不及格的都改成及格
df.loc[df["art"] < 60, "art"] = 60

新增值

# 新增一列
df.loc["Ivy"] = np.random.randint(50, 100, 8)    
# 新增一欄
df.loc[:, "passed"] = df["math"] >= 60 

明天來講講讀寫資料。


上一篇
Day4: 基於Series的操作及缺失值處理
下一篇
Day6: 資料讀寫
系列文
從零開始Pandas-外加一點Matplotlib11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言