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. 学生总成绩:
', student_total_score)
1. 学生总成绩:
       总成绩
姓名       
孙力   1038
张明   1081
王大伟  1048
  • 2. 每个学生各学期的总成绩
# 2. 每个学生各学期的总成绩
student_semester_total = exam_data.groupby(by=['姓名', '学期']).agg(
    {'成绩': sum}).rename({'成绩': ' 总成绩'})
print('
2. 学生每个学期总成绩:
', 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('
3. 各门课程平均成绩:
', 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('
4. 各学期大于本课程平均成绩的学生姓名及成绩: 
', 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()
原文地址:https://www.cnblogs.com/zhangyafei/p/12114521.html