天天看點

資料分析8 -- 進階索引:過濾與檢視表格中的局部資料

上一篇中,我們學習了 pandas 中兩個核心的資料結構:Series 和 DataFrame,之後還學習了 DataFrame 的常見操作,比如對列、行的增删查改。

但 DataFrame 的能力遠不止于此,今天我們會圍繞資料分析中各種各樣的查詢需求,來系統性介紹 DataFrame 強大的資料查詢與過濾能力。

使用 [] 查詢元素

中括号[], 是 pandas 中最基礎的索引器。索引器是指我們提供索引,然後索引器就傳回索引對應的内容。其實我們早在變量與資料類型一講中已經打過交道。

比如一個清單 a, 我們想要通路第三個元素則可以寫 a[2] , 這裡的 2 就是索引,[] 就是索引器。a[2] 就能為我們通路清單 a 中索引 2 對應的元素,也就是清單的第三個元素。

以字典為例的話,如果我們有如下字典: b = {"name":"小亮", "age": 24}。我們希望通路字典 b 的 age 字段,則可以寫 b["age"],字元串 "age" 是索引,[]是索引器,最終 b["age"] 傳回了索引對應的内容:24。

DataFrame 和 Series 同樣支援 []. 具體的行為是:

對 Series 使用[],傳回索引對應的元素;

對 DataFrame 使用[],傳回列名等于索引的那一列,以 Series 的形式。

準備資料

首先建立部門資訊 DataFrame :

import pandas as pd
# 将列索引儲存在 index_arr 變量中
index_arr = ['姓名','年齡','籍貫','部門']
# 建構小明、小亮、小E 的行series,并使用我們建立好的 index_arr 作為 series的index
ser_xiaoming=pd.Series(["小明",22,"河北","IT部"],index=index_arr)
ser_xiaoliang=pd.Series(["小亮",25,"廣東","IT部"],index=index_arr)
ser_xiaoe=pd.Series(["小E",23,"四川","财務部"],index=index_arr)
# 直接将三個 series 以清單的形式作為 DataFrame的參數,建立DataFrame
df_info = pd.DataFrame([ser_xiaoming,ser_xiaoliang,ser_xiaoe])
print(df_info)
           
姓名  年齡  籍貫   部門
0  小明  22  河北  IT部
1  小亮  25  廣東  IT部
2  小E  23  四川  财務部
           

這樣,我們實驗的資料就準備好了。

簡單索引

接下來,我們通過實驗來加深一下 [] 選擇器的了解。

首先是對 DataFrame 使用 [] 索引器。

# 對 DataFrame 使用[],傳回列名= 傳入的索引值的 Series
ser_name = df_info['姓名']
# 列印 ser_name 變量的類型
print(type(ser_name))
print(ser_name)
           
<class 'pandas.core.series.Series'>
0    小明
1    小亮
2    小E
Name: 姓名, dtype: object
           

從輸出的結果可以看出。通過 [] 索引器和"姓名"這個索引,拿到的是一個 Series 類型。并且内容就是上一步構造的 DataFrame 中,姓名那一列對應的 Series。

對一個 Series 對象使用 [] 索引器,則會傳回索引對應的具體資料。比如當我們希望拿索引 1 對應的資料,從上面的例子中可以看到,索引 1 對應的資料是“小亮”這個名字。我們通過代碼來驗證一下。

# 對上一步擷取的series對象:ser_name 使用 [] 索引器
# 傳入索引 1
result = ser_name[1]
print(result)
           
小亮
           

多重索引

[]除了可以傳入單一索引實作資料選擇,還支援傳入一個索引清單來獲得原始資料集的一個子集。規則如下:

當對 DataFrame 的 [] 索引器傳入一個索引清單時,傳回一個新的 DataFrame,這個 DataFrame 隻包含索引清單中的列,相當于是原 DataFrame 的子 DataFrame;

同理對于 Series,傳入索引清單時,傳回一個子 Series,包含索引清單對應的資料。

我們繼續通過代碼來加深了解:

# 傳入一個索引清單,包含兩個索引項:"姓名""年齡"
sub_df_info = df_info[['姓名','年齡']]
print(type(sub_df_info))
print(sub_df_info)
           
<class 'pandas.core.frame.DataFrame'>
   姓名  年齡
0  小明  22
1  小亮  25
2  小E  23
           

傳入索引清單之後,結果仍然是一個 DataFrame,并且這個 DataFrame 隻包含了姓名、年齡這兩列。

對于 Series 而言也是類似,比如我們希望同時挑選索引為 0 和 2 的兩條記錄,可以用如下方式實作。

# 傳入索引清單[0,2]給series,并将結果儲存在變量sub_ser_name中
sub_ser_name = ser_name[[0,2]]
print(type(sub_ser_name))
print(sub_ser_name)
           
0    小明
2    小E
Name: 姓名, dtype: object
           

可以看到,結果仍然是一個 Series 類型,但隻包含了兩條記錄,分别是索引 0 對應的小明和索引 2 對應的小 E。從另一個角度看,Series 的數字索引是可以不連續的,這個也是和清單的一個重要差別。

範圍選擇

在學習如何使用 [] 進行範圍選擇之前,我們先給我們的 DataFrame 添加新的兩條記錄,友善示範功能效果。代碼如下:

# 建立小王和小李的series
ser_xiaoli = pd.Series(['小李',22,'雲南','設計部'],index=index_arr)
ser_xiaowang = pd.Series(['小王',20,'福建','設計部'],index=index_arr)
# 将series 添加到 DataFrame中
df_info = df_info.append(ser_xiaoli,ignore_index=True)
df_info = df_info.append(ser_xiaowang,ignore_index=True)
print(df_info)
           
姓名  年齡  籍貫   部門
0  小明  22  河北  IT部
1  小亮  25  廣東  IT部
2  小E  23  四川  财務部
3  小李  22  雲南  設計部
4  小王  20  福建  設計部
           

現在進入正題,[] 索引器支援常見的範圍選擇有以下幾種:

(1)df[n:m], 選擇第 n 條到第 m 條之間的記錄。示例代碼:

# 取第 2 到 3 條的記錄
print(df_info[2:4])
           
姓名  年齡  籍貫   部門
2  小E  23  四川  财務部
3  小李  22  雲南  設計部
           

(2)df[:m], 選擇前 m 條記錄。示例代碼:

#取前 3 條資料
print(df_info[0:3])
           
姓名  年齡  籍貫   部門
0  小明  22  河北  IT部
1  小亮  25  廣東  IT部
2  小E  23  四川  财務部
           

如果不寫 m , 直接寫 df[:] 的話,代表傳回所有的記錄。

(3)df[::n], 間隔 n 條選擇。示例代碼:

# 每2個取一次資料
print(df_info[::2])
           
姓名  年齡  籍貫   部門
0  小明  22  河北  IT部
2  小E  23  四川  财務部
4  小王  20  福建  設計部
           
# 每取一次跳過1個(也就是預設的選擇方式)
print(df_info[::1])
           
姓名  年齡  籍貫   部門
0  小明  22  河北  IT部
1  小亮  25  廣東  IT部
2  小E  23  四川  财務部
3  小李  22  雲南  設計部
4  小王  20  福建  設計部
           

(4)df[::-1], 從最後一條開始逐一選擇。

# 從後往前取
print(df_info[::-1])
           
姓名  年齡  籍貫   部門
4  小王  20  福建  設計部
3  小李  22  雲南  設計部
2  小E  23  四川  财務部
1  小亮  25  廣東  IT部
0  小明  22  河北  IT部
           

在範圍選擇的應用中,Series 的用法和 DataFrame 是一緻的。

至此,我們就學習完了 [] 選擇器的常見用法。

loc 與 iloc

在學習 [] 選擇器的過程中,如果我們想查詢 DataFrame 中某個單元格的資料,那往往都需要分三步走:

  1. 檢視單元格所在行的索引;
  2. 拿到單元格所在列的列 Series;
  3. 用 1 拿到的索引去 2 拿到的 Series 中查詢出具體的資料。

還是上面的部門表格:

姓名  年齡  籍貫   部門
0  小明  22  河北  IT部
1  小亮  25  廣東  IT部
2  小E  23  四川  财務部
3  小李  22  雲南  設計部
4  小王  20  福建  設計部
           

可以看到,小亮的行索引是 1,然後我們擷取部門的列 Series 并進行查詢:

# 獲得部門的列 Series
dep_series = df_info["部門"]
# 列印部門 Series 中,索引為 1 的值
print("小亮的部門:" + dep_series[1])
           
小亮的部門:IT部
           

整個過程還是比較麻煩的,并且需要記得我們要查詢的資料所在行的索引,非常容易出錯。有沒有更好的方式呢?答案是肯定的。

pandas 除了 [] 索引器之外,還提供了一套非常強大的資料查詢方式:loc 和 iloc。

loc 基礎用法

loc 和 iloc 是 pandas 的一種特殊的對象,專門用于查詢 DataFrame 中的資料。

首先是 loc,基本用法如下:

df.loc[行索引名稱, 列索引名稱]
           

使用 loc 對象我們可以一次性執行行索引+列索引,這樣就使得定位單元格的内容可以直接一行代碼就搞定。

拿上面的例子來說,如果要查詢小亮的部門,用 loc 直接這麼寫即可:

# 小亮的行索引是1,查詢部門,則列索引是 '部門'
print("小亮的部門:" + df_info.loc[1, "部門"])
           
小亮的部門:IT部
           

可以看到,通過 loc, 我們将之前的三步縮短成了兩步,省去了先取 Series 出來的環節。

loc 對象的 [] 索引器支援所有 DataFrame 的 [] 索引器的能力。具體來說就是 loc 對象的行索引部分和列索引部分都可以分别使用我們第一部分介紹的多種索引、範圍選擇的文法。

舉個例子來說明一下,比如我們任務是在上面的 df_info 表中,從後往前選擇每個同僚的姓名和年齡兩列。

上述任務規定了兩個條件:一個是需要從後往前,即我們取行的時候,需要使用範圍選擇中學到的技術;另一個是隻取姓名和年齡兩列,需要用到我們之前在多重索引中學到的技術。

具體的代碼如下:

# 使用 loc,行索引處使用 ::-1 來指定從後往前選取,列索引部分傳入列名清單來實作取兩列
print(df_info.loc[::-1, ["姓名", "年齡"]])
           
姓名  年齡
4  小王  20
3  小李  22
2  小E  23
1  小亮  25
0  小明  22
           

iloc 基礎用法

iloc 的用法和 loc 非常類似,差別是 iloc 僅支援傳入整數索引。簡單來說,loc 是需要傳入行索引和列索引的名稱,而 iloc 則需要傳入第幾行、第幾列這樣的數字。基本用法如下:

df.iloc[第幾行, 第幾列]
           

拿上面的例子來說,假設我們要用 iloc 對象來列印小亮的部門,可以這麼做。

# 從資料表中可以看到,小亮的部門這個資料在第一行,第三列。是以可以将 1 3 傳入 iloc 即可
print("小亮的部門:" + df_info.iloc[1, 3])
           
小亮的部門:IT部
           

從輸出來看,效果和使用 loc 對象是一樣的。但是從易用性的層面,loc 顯然比 iloc 更加容易使用且不容易出錯。使用 iloc 每次都需要去數我們所要的資料在第幾行、第幾列,非常容易出錯。是以一般情況下,我們都推薦直接使用 loc。但也有一些場景,不知道行索引,但明确要拿第一個元素的場合就需要使用 iloc。

總體來說 iloc 是和 loc 打配合使用的, loc 最常用。

條件查詢

我們回過頭去看單元格查詢的三個步驟,雖然通過 loc 對象,我們省去了先取 Series 再取資料的備援步驟,但是第一步:檢視小亮所在的行的索引。這一步對于我們的例子來說是很簡單的,畢竟一共也就這麼幾行。但如果我們的表裡面資料有幾十萬、上百萬,我們不可能逐一去看小亮所在的行的索引到底是什麼。

這個時候就需要用到 loc 對象的一個重要特性:條件索引。

loc 的條件索引的具體用法如下所示,它和普通的 loc 用法差別最大的就是将行索引部分替換為條件表達式。

df.loc[條件表達式, 列索引名稱]
           

比如,我們希望獲得年齡大于等于 23 歲的員工的資訊。使用條件查詢,我們可以這樣寫:

# 條件表達式:df_info["年齡"] >= 23, 然後因為我們希望獲得所有列,是以列索引直接寫 :
print(df_info.loc[df_info["年齡"] >= 23, :])
           
姓名  年齡  籍貫   部門
1  小亮  25  廣東  IT部
2  小E  23  四川  财務部
           

條件表達式往往是判斷 DataFrame 的某個列滿足某個條件,比如是否大于或等于,等等。這樣我們就不用每次都要看我們想要資料的行索引是什麼,而是直接通過寫合适的條件表達式就可以篩選出我們想要的資料。

拿我們最開始的任務來說,我們要查詢小亮的部門,有了條件表達式,我們不再需要關心小亮所在行的行索引。而是可以這麼寫:

# 使用條件表達式: df["姓名"] == "小亮", 并取 部門 這個列
print(df_info.loc[df_info["姓名"] == "小亮", "部門"])
           
1    IT部
Name: 部門, dtype: object
           

可以看到,小亮的部門被成功查詢出來了,而且還是在我們完全不知道他行索引的前提下。不過,我們希望查詢的是一個值,但這裡的結果似乎是一個 Series,這是因為一旦在 loc 中使用了條件表達式,它傳回的結果就會是 Series,因為會存在滿足條件的行有多個的情況。

在這個例子裡,我們知道表中隻有一個小亮,是以直接從結果 Series 取第一個就可以。這裡我們不關心結果中的行索引,是以可以直接使用 iloc 取第一個即可。(Series 的 iloc 和 DataFrame 的 iloc 作用類似,即不關心索引,而是按照第幾個這樣的排序來取)

綜上所述,我們通過條件查詢來列印小亮的部門,代碼如下:

print("小亮的部門:" + df_info.loc[df_info["姓名"] == "小亮", "部門"].iloc[0])
           
小亮的部門:IT部
           

進階條件查詢

當我們篩選資料的時候,一個條件不能滿足要求,就需要組合多個條件來篩選出我們想要的資料。組合多個條件時,最常見的兩個邏輯關系就是:邏輯與和邏輯或。

(1)邏輯與

假設有兩個條件:A 和 B,A & B 代表邏輯與,邏輯與的意思是 A 和 B 兩個條件需要同時滿足,則 A & B 才算滿足。

舉個例子,我們希望查詢 IT 部中 25 歲以下的員工資訊。這裡就有兩個條件,一個是部分是 IT部,另一個是年齡小于 25,這兩個條件需要同時滿足。

# loc 條件表達式的邏輯與操作。每個子條件都需要用括号括起來
print(df_info.loc[(df_info["部門"] == "IT部") & (df_info["年齡"] <25), :])
           
姓名  年齡  籍貫   部門
0  小明  22  河北  IT部
           

(2)邏輯或

假設有兩個條件,A 和 B,A | B 代表邏輯或,意思是 A 和 B 隻需要有一個條件滿足,則 A | B 就滿足。

舉個例子,我們希望查詢出所有财務部和設計部的員工。這裡有兩個條件,一個是部門等于财務部,一個是部門等于設計部,隻需要滿足其中一個條件就需要列印出來。代碼如下:

print(df_info.loc[(df_info["部門"] == "财務部") | (df_info["部門"] == "設計部"), :])
           
姓名  年齡  籍貫   部門
0  小明  22  河北  IT部
   姓名  年齡  籍貫   部門
2  小E  23  四川  财務部
3  小李  22  雲南  設計部
4  小王  20  福建  設計部
           

小結

至此,關于 DataFrame 和 Series 的資料查詢技術就已經全部學完了,我們在這裡簡單地回顧一下。

首先,我們學習了使用 [] 來查詢 DataFrame 的 Series 的内容,關鍵點如下。

  • 針對 Series 使用 [], 傳回傳入的索引對應的元素;針對 DataFrame 使用 [] ,傳回傳入的索引對應的列 Series。
  • 當傳給 [] 索引器的索引是多個索引,即一個索引清單時,DataFrame 會傳回包含索引清單中指定的列的子 DataFrame,而 Series 則會傳回索引清單中索引對應的元素組成的子 Series。
  • [n:m],代表查詢從 n 到 m 中間的這一段記錄,不寫 n 時,代表查詢前 m 條資料,n 和 m 都不寫時,傳回查詢全部資料。
  • [::n],代表每隔 n 條傳回一條,一般用于基于固定的頻率采樣資料集;[::-1] 代表從後向前逐一傳回。

之後,我們學習了查詢資料更強大的 loc 和 iloc,關鍵點如下。

  • loc 後接[] ,可以一次性傳入行索引和列索引,使用逗号隔開,實作了直接取單元格的資料;行索引和列索引都遵循第一部分介紹的各種規則,如多重索引、範圍選擇等。
  • iloc 和 loc 類似,隻是傳入的不是索引,而是第幾行、第幾列這樣的整數。
  • loc 的行索引部分可以替換為條件表達式,來實作通過條件來選擇行,而不是通過固定的行索引。
  • 條件表達式可以組合,& 代表邏輯與,| 代表邏輯或。