多級索引
多級索引(也稱層次化索引)是pandas的重要功能,可以在Series、DataFrame對象上擁有2個以及2個以上的索引。
實質上,單級索引對應Index對象,多級索引對應MultiIndex對象。
一、Series對象的多級索引
- 多級索引Series對象的創建
import pandas as pd
import numpy as np
se1=pd.Series(np.random.randn(4),index=[list("aabb"),[1,2,1,2]])
se1
Out[6]:
a 1 0.357171
2 0.084055
b 1 -0.678752
2 0.132007
dtype: float64
- 子集的選取
se1['a'] Out[7]: 1 0.357171 2 0.084055 dtype: float64 se1['a':'b'] Out[8]: a 1 0.357171 2 0.084055 b 1 -0.678752 2 0.132007 dtype: float64
-
內層選取
se1[:, 1] Out[9]: a 0.357171 b -0.678752 dtype: float64 se1[:, 2] Out[10]: a 0.084055 b 0.132007 dtype: float64
二、DataFrame對象的多級索引
- 1. 創建多層行索引
- 隱式構造
import numpy as np
df = pd.DataFrame(np.random.randint(0, 150, size=(6,3)), columns=['語文', '數學', 'Python'], index=[['Michal', 'Michal', 'Kobe','Kobe', 'James', 'James'],['Mid','End', 'Mid', 'End','Mid', 'End']])
df
Out[25]:
語文 數學 Python
Michal Mid 100 43 73
End 11 18 60
Kobe Mid 104 66 54
End 30 120 134
James Mid 135 77 56
End 45 127 63
顯式構造
- 使用數組
df = pd.DataFrame(np.random.randint(0, 150, size=(6,3)), columns=['語文', '數學', 'Python'], index=pd.MultiIndex.from_arrays([['Michal', 'Michal', 'Kobe','Kobe', 'James', 'James'],['Mid','End', 'Mid', 'End','Mid', 'End']]))
df
Out[27]:
語文 數學 Python
Michal Mid 56 46 104
End 83 57 95
Kobe Mid 48 94 45
End 22 99 49
James Mid 65 66 91
End 69 101 84
- 使用元組
df = pd.DataFrame(np.random.randint(0, 150, size=(6,3)), columns=['語文', '數學', 'Python'], index=pd.MultiIndex.from_tuples([('Michal','期中'), ('Michal','期末'), ('Kobe','期中'), ('Kobe','期末'), ('James','期中'), ('James','期末')]))
df
Out[29]:
語文 數學 Python
Michal 期中 10 107 48
期末 113 49 147
Kobe 期中 116 138 29
期末 7 64 53
James 期中 1 30 21
期末 70 76 108
- 使用product
df = pd.DataFrame(np.random.randint(0, 150, size=(6,3)), columns=['語文', '數學', 'Python'], index=pd.MultiIndex.from_product([['Michal','Kobe','James'],['Mid','End']]))
df
Out[31]:
語文 數學 Python
Michal Mid 85 89 17
End 21 4 23
Kobe Mid 54 117 108
End 37 20 79
James Mid 56 47 82
End 45 57 126
2. 多層列索引
df = pd.DataFrame(np.random.randint(0, 150, size=(3,6)), index=['語文', '數學', 'Python'], columns=pd.MultiIndex.from_product([['Michal','Kobe','James'],['Mid','End']]))
df
Out[34]:
Michal Kobe James
Mid End Mid End Mid End
語文 74 84 76 142 36 87
數學 44 90 57 143 78 68
Python 79 46 120 47 128 145
3. 索引賦值,設置名稱和交換
df1=pd.DataFrame(np.arange(12).reshape(4,3),index=[list("AABB"),[1,2,1,2]],columns=[list("XXY"),[10,11,10]])
df1
Out[11]:
X Y
10 11 10
A 1 0 1 2
2 3 4 5
B 1 6 7 8
2 9 10 11
- 賦名
df1.columns.names=['XY','sum'] df1.index.names=['AB','num'] df1 Out[12]: XY X Y sum 10 11 10 AB num A 1 0 1 2 2 3 4 5 B 1 6 7 8 2 9 10 11
- 創建MultiIndex對象再作為索引
df1.index=pd.MultiIndex.from_arrays([list("AABB"),[3,4,3,4]],names=["AB","num"])
df1
Out[13]:
XY X Y
sum 10 11 10
AB num
A 3 0 1 2
4 3 4 5
B 3 6 7 8
4 9 10 11
- 可以對各級索引進行互換
df1.swaplevel('AB','num')
Out[14]:
XY X Y
sum 10 11 10
num AB
3 A 0 1 2
4 A 3 4 5
3 B 6 7 8
4 B 9 10 11
4. 獲取索引值
df = pd.DataFrame(np.random.randint(0, 150, size=(6,3)), columns=['語文', '數學', 'Python'], index=pd.MultiIndex.from_product([['Michal','Kobe','James'],['Mid','End']]))
df
Out[38]:
語文 數學 Python
Michal Mid 65 134 85
End 94 91 48
Kobe Mid 118 142 141
End 2 32 106
James Mid 29 11 127
End 16 93 99
df.index
Out[39]:
MultiIndex([('Michal', 'Mid'),
('Michal', 'End'),
( 'Kobe', 'Mid'),
( 'Kobe', 'End'),
( 'James', 'Mid'),
( 'James', 'End')],
)
# loc獲取
df.loc['James', :]
Out[42]:
語文 數學 Python
Mid 29 11 127
End 16 93 99
df[df.index.get_level_values(0) == 'Michal']
Out[45]:
語文 數學 Python
Michal Mid 65 134 85
End 94 91 48
df[df.index.get_level_values(1) == 'Mid']
Out[46]:
語文 數學 Python
Michal Mid 65 134 85
Kobe Mid 118 142 141
James Mid 29 11 127
df.loc[('James', 'Mid'), :]
Out[41]:
語文 29
數學 11
Python 127
Name: (James, Mid), dtype: int32
三、案例:考試數據分析

試題:
-
1. 計算每個學生的總成績
-
2. 計算每個學生各學期的總成績
-
3. 各門課程平均成績
-
4. 各學期大於本課程平均成績的學生姓名及成績
整理數據

- 讀取數據
import pandas as pd
exam_data = pd.read_excel('試題.xlsx', sheet_name='試題數據')
exam_data
Out[3]:
姓名 課程 學期 成績
0 王大偉 大學英語 1 92
1 王大偉 大學英語 2 85
2 王大偉 大學英語 3 83
3 王大偉 大學英語 4 90
4 王大偉 高等數學 1 91
5 王大偉 高等數學 2 86
6 王大偉 高等數學 3 98
7 王大偉 高等數學 4 84
8 王大偉 大學體育 1 78
9 王大偉 大學體育 2 91
- 設置索引
# 讀取時設置
exam_data = pd.read_excel('試題.xlsx', sheet_name='試題數據', index_col=[0, 1])
exam_data
Out[5]:
學期 成績
姓名 課程
王大偉 大學英語 1 92
大學英語 2 85
大學英語 3 83
大學英語 4 90
高等數學 1 91
高等數學 2 86
高等數學 3 98
高等數學 4 84
大學體育 1 78
大學體育 2 91
大學體育 3 80
大學體育 4 90
孫力 大學英語 1 87
大學英語 2 79
大學英語 3 93
大學英語 4 78
高等數學 1 87
高等數學 2 93
高等數學 3 85
高等數學 4 89
大學體育 1 77
大學體育 2 83
大學體育 3 99
大學體育 4 88
張明 大學英語 1 88
大學英語 2 94
大學英語 3 96
大學英語 4 87
高等數學 1 97
高等數學 2 89
高等數學 3 94
高等數學 4 86
大學體育 1 87
大學體育 2 85
大學體育 3 86
大學體育 4 92
# 設置索引列
exam_data.set_index(keys=['姓名', '課程'])
Out[21]:
學期 成績
姓名 課程
王大偉 大學英語 1 92
大學英語 2 85
大學英語 3 83
大學英語 4 90
高等數學 1 91
高等數學 2 86
高等數學 3 98
高等數學 4 84
大學體育 1 78
大學體育 2 91
大學體育 3 80
大學體育 4 90
孫力 大學英語 1 87
大學英語 2 79
大學英語 3 93
大學英語 4 78
高等數學 1 87
高等數學 2 93
高等數學 3 85
高等數學 4 89
大學體育 1 77
大學體育 2 83
大學體育 3 99
大學體育 4 88
張明 大學英語 1 88
大學英語 2 94
大學英語 3 96
大學英語 4 87
高等數學 1 97
高等數學 2 89
高等數學 3 94
高等數學 4 86
大學體育 1 87
大學體育 2 85
大學體育 3 86
大學體育 4 92
當然,這里我們不需要對其設置索引
- 1. 計算每個學生總成績
exam_data = pd.read_excel('試題.xlsx', sheet_name='試題數據')
# 1. 計算每個學生總成績
student_total_score = exam_data.groupby(by=['姓名']).agg(
{'成績': sum}).rename(columns={'成績': '總成績'})
print('1. 學生總成績:\n', student_total_score)
1. 學生總成績:
總成績
姓名
孫力 1038
張明 1081
王大偉 1048
- 2. 每個學生各學期的總成績
# 2. 每個學生各學期的總成績
student_semester_total = exam_data.groupby(by=['姓名', '學期']).agg(
{'成績': sum}).rename({'成績': ' 總成績'})
print('\n2. 學生每個學期總成績:\n', student_semester_total)
2. 學生每個學期總成績:
成績
姓名 學期
孫力 1 251
2 255
3 277
4 255
張明 1 272
2 268
3 276
4 265
王大偉 1 261
2 262
3 261
4 264
3. 各門課程平均成績
# 3. 各門課程平均成績
course_avg_score = exam_data.groupby(by=['課程'])['成績'].mean()
print('\n3. 各門課程平均成績:\n', course_avg_score)
3. 各門課程平均成績:
課程
大學體育 86.333333
大學英語 87.666667
高等數學 89.916667
Name: 成績, dtype: float64
- 4. 各學期大於本課程平均成績的學生姓名及成績
def judge_score(row):
return row['成績'] > course_avg_score[row['課程']]
greater_than_avg_student = exam_data[exam_data.apply(judge_score, axis=1)].set_index(keys=['姓名', '課程'])
print('\n4. 各學期大於本課程平均成績的學生姓名及成績: \n', greater_than_avg_student)
4. 各學期大於本課程平均成績的學生姓名及成績:
學期 成績
姓名 課程
王大偉 大學英語 1 92
大學英語 4 90
高等數學 1 91
高等數學 3 98
大學體育 2 91
大學體育 4 90
孫力 大學英語 3 93
高等數學 2 93
大學體育 3 99
大學體育 4 88
張明 大學英語 1 88
大學英語 2 94
大學英語 3 96
高等數學 1 97
高等數學 3 94
大學體育 1 87
大學體育 4 92
- 將結果輸出到文件
# 輸出文件
with pd.ExcelWriter(path="結果.xlsx") as writer:
exam_data.to_excel(excel_writer=writer, sheet_name='試題數據', encoding='utf-8', index=False)
student_total_score.to_excel(excel_writer=writer, sheet_name='學生總成績', encoding='utf-8')
student_semester_total.to_excel(excel_writer=writer, sheet_name='每個學生各學期總成績', encoding='utf-8')
course_avg_score.to_excel(excel_writer=writer, sheet_name='各門課程平均成績', encoding='utf-8')
greater_than_avg_student.to_excel(excel_writer=writer, sheet_name='各學期大於本課程平均成績的學生姓名及成績', encoding='utf-8')
writer.save()
