Pandas系列(十八)- 多级索引
多级索引
多级索引(也称层次化索引)是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()