首先了解一下什么是多級索引,以及它的作用,為什么要有這個玩意。
多級索引也稱為層次化索引(hierarchical indexing),是指數據在一個軸上(行或者列)擁有多個(兩個以上)索引級別。之所以引入多級索引,在于它可以使用戶能以低維度形式處理高維度數據。這句話可能不太好理解,下面舉個栗子:
在一張二維表格中可以方便的存儲兩個維度的數據,比如我們現在有一張高三八班的期末考試成績表,第一個維度行是某個學生各科的考試成績,另一個維度列是某一科所有學生的成績
image.png
現在在加入一個維度:不同的班級。即如果我們想在這張表上在加上高三七班同樣的成績該怎么操作?這時候我們就可以在行上在加入一個班級的維度,如下:
image.png
正常情況下,不同的班級的所有學生的成績應該是引入不同的表格來存儲,即三八班和三七班是兩張表。但是引入多級索引就可以把這兩張表整合在一起,即所謂的以低維度形式處理高維度的數據。 這種情況在每張表數據量不大或者字段不多的時候使用起來比較方便
1. 多級索引的創建
通常由兩種方式創建多級索引:
通過多級數組隱式創建
通過pd.MultiIndex顯示創建
下面分別舉例
方法1-隱式創建,即給DataFrame的index或columns參數傳遞兩個或更多的數組。
import pandas as pd
import numpy as np
from pandas import DataFrame, Series
df = DataFrame (np.random.randint ( 0 , 100 ,( 4 , 6 )),
index = ['學生' + i for i in 'ABCD'],
columns =[[ '數學' , '數學' , '語文' , '語文' , '英語' , '英語' ],
[ '期中' , '期末' , '期中' , '期末' , '期中' , '期末' ]])
df
上面通過在columns傳入一個二維數組來隱式創建多級索引,結果如下:
image.png
仔細觀察就可以發現上面的表格中包含學生(A/B/C/D)、科目(數學/語文)、考試階段(期中/期末)三個維度的信息,但是是用一張二維表格來呈現。
方法二 —— 通過pd.MultiIndex顯示創建
常用的有 from_tuples, from_arrays, from_product 三種方法,它們都是 pd.MultiIndex 對象下的函數。其中, from_product最簡單,推薦使用。下面分別舉例說明。
2.1: from_tuples指根據傳入由元組組成的列表進行構造:
my_tup = [('Python', '期中'),('Python', '期末'),('Java', '期中'),('Java', '期末')]
my_index = pd.MultiIndex.from_tuples(my_tup, names = ['Obj', 'time'])
pd.DataFrame(np.random.randint(60,100, (4,3)),
index = my_index,
columns = [*'ABC'])
image.png
2.2 from_arrays指根據傳入列表中,對應層的列表進行構造:
arr = [[*'ABCD'], ['a', 'b'] *2]
my_index = pd.MultiIndex.from_arrays(arr, names = ['first', 'secoond'])
col = ['China', 'US', 'UK']
val = np.random.randint(50,100, (4, 3))
df = pd.DataFrame(val, index = my_index, columns = col )
df
image.png
2.3 from_product指根據多個列表的笛卡爾積構造多級索引
mul_col = pd.MultiIndex.from_product([['Python', 'C++', 'Java'],['期中', '期末']])
df = DataFrame ( np . random . randint ( 0 , 100 ,( 6 , 4 )),
index = mul_col,
columns = list('ABCD'))
df
image.png
知識鏈接
笛卡爾積:令A和B是任意兩個集合,若序偶的第一個成員是A的元素,第二個成員是B的元素,所有這樣的序偶集合,稱為集合A和B的笛卡爾乘積或直積,記做A X B
若A={a1,a2,a3……an},B = {b1,b2,b3,……bn},
則A X B = {(a1,b1), (a1,b2),....(an,bn) }, 共有n*n個元素
且每個元素中a永遠在前面,b永遠在后面。
例如,A={a,b}, B={0,1,2},則
A×B={(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}
B×A={(0, a), (0, b), (1, a), (1, b), (2, a), (2, b)}
總結一下多級索引MultiIndex和單極索引類似,只不過其索引中的一個元素是元組而不是單層索引中的標量。例如上面的例子中和單極索引一樣我們可以使用index來查看索引。另外外層連續出現相同的值時,第一次之后出現的會被隱藏顯示,使結果的可讀性增強。
df.index
>>>
MultiIndex([('Python', '期中'),
('Python', '期末'),
( 'C++', '期中'),
( 'C++', '期末'),
( 'Java', '期中'),
( 'Java', '期末')],
)
2. 多級索引的常用操作
2.1 索引層的交換和刪除
既然是多級索引,那么必然涉及到不同層之間的順序調整。在pandas中索引層的交換由swaplevel和reorder_levels完成,前者只能交換兩個層,而后者可以交換任意層,兩者都可以指定交換的是軸是哪一個,即行索引(axis = 0)或列索引(axis = 1).為了方便舉例說明,下面先創建一個多層索引的DataFrame
L1,L2,L3 = ['A','B'],['a','b'],['alpha','beta']
mul_index1 = pd.MultiIndex.from_product([L1,L2,L3], names=('Upper', 'Lower','Extra'))
L4,L5,L6 = ['C','D'],['c','d'],['cat','dog']
mul_index2 = pd.MultiIndex.from_product([L4,L5,L6], names=('Big', 'Small', 'animal'))
df_ex = pd.DataFrame(np.random.randint(-9,10,(8,8)), index=mul_index1, columns=mul_index2)
df_ex
image.png
swaplevel交換兩層
df_ex.swaplevel(0,2, axis = 1) # 列索引的第一層和最后一層互換
image.png
reorder_levels調整多層順序
df_ex.reorder_levels([2,0,1], axis = 0) # 行索引改變順序
image.png
若想要刪除某一層的索引,可以使用droplevel方法。同時刪除多層,可以傳入一個列表
df_ex.droplevel([0,2], axis = 0)
image.png
2.2 索引屬性的修改
常用的有rename_axis和rename,其中:
rename_axis:用于修改索引層的名字,即在使用pd.MultiIndex創建時傳入的names參數的值。可以傳入字典進行修改
rename: 對索引的值進行修改,如果是多級索引需要指定修改的層號level:
# 修改索引層的名字
df_ex.rename_axis(index={'Extra':'Extra_change'},
columns={'animal':'animal_change'})
image.png
# ranme修改索引值,index/columns均可
df_ex.rename(index = {'alpha': 'alpha_change'}, level = 2)
image.png
對于rename,傳入參數也可以是函數,其輸入值就是索引元素:
df_ex.rename(columns = lambda x: str.upper(x), level=2)
image.png
另外,在修改索引時還有一個map函數比較好用。它是定義在index之上的方法。與前面rename方法中層的函數式用法是類似的,只不過它傳入的不是層的標量值,而是直接傳入索引的元組,這樣可以對整個多級索引進行修改
例如我們將上面的索引中的小寫轉化為大寫,且在每個索引后加上 “_change':
df_temp = df_ex.copy()
new_idx = df_temp.index.map(lambda x: (x[0]+'_change',str.upper(x[1])+'_change', str.upper(x[2])+'_change'))
df_temp.index = new_idx
df_temp
image.png
另外,map的另外一個常用的用法是用于多層索引的壓縮,如下:
df_temp = df_ex.copy()
new_idx = df_temp.index.map(lambda x: (x[0]+'-'+x[1]+'-'+x[2]))
df_temp.index = new_idx
df_temp.head() # 將原來的三層索引壓縮為一層
image.png
自然的,也可以反向展開:
new_idx = df_temp.index.map(lambda x:tuple(x.split('-')))
df_temp.index = new_idx
df_temp# 三層索引
image.png
好了,關于多級索引就學習這么多內容!
參考:開源內容joyful-pandas, 作者: Datawhale-耿遠昊
另外,更多精彩內容也可以微信搜索,并關注公眾號:‘Python數據科學家之路“ ,期待您的到來和我交流!