【Task 3】索引

一、索引器

导入相关库:

import numpy as np

import pandas as pd

1. 表的列索引

列索引是最常见的索引形式,一般通过 [] 来实现。

  • 如果取出一列,通过 [列名] 可以从 DataFrame 中取出相应的列,返回值为 Series 。
  • 如果要取出多个列,则可以通过 [列名组成的列表] ,其返回值为一个 DataFrame 。
df = pd.read_csv('data/learn_pandas.csv',usecols = ['School', 'Grade', 'Name','Gender','Weight', 'Transfer'])

df['Name'].head()  # 从表中取出姓名一列
## 结果 
0      Gaopeng Yang
1    Changqiang You
2           Mei Sun
3      Xiaojuan Sun
4       Gaojuan You
Name: Name, dtype: object


df[['Gender', 'Name']].head()  # 从表中取出性别和姓名两列
## 结果 
   Gender            Name
0  Female    Gaopeng Yang
1    Male  Changqiang You
2    Male         Mei Sun
3  Female    Xiaojuan Sun
4    Male     Gaojuan You

此外,若要取出单列,且列名中不包含空格,则可以用 .列名 取出,这和 [列名] 是等价的。

df.Name.head()  # 与 df.['Name'].head() 等价
## 结果 
0      Gaopeng Yang
1    Changqiang You
2           Mei Sun
3      Xiaojuan Sun
4       Gaojuan You
Name: Name, dtype: object

2. 序列的行索引

(1)以字符串为索引的 Series

如果取出单个索引的对应元素,则可以使用 [item] :

  • 若 Series 只有单个值对应,则返回这个标量值
  • 若有多个值对应,则返回一个 Series
s = pd.Series([1, 2, 3, 4, 5, 6],index=['a', 'b', 'a', 'a', 'a', 'c'])


s['a']  # 有多个值对应,返回一个 Series
## 结果 
a    1
a    3
a    4
a    5
dtype: int64

s['b']  # 只有一个值,返回一个标量值
## 结果
2

如果取出多个索引的对应元素,则可以使用 [items的列表] :

s[['c', 'b']]
## 结果 
c    6
b    2
dtype: int64

如果想要取出某两个索引之间的元素,并且这两个索引是在整个索引中唯一出现,则可以使用切片,同时需要注意这里的切片会包含两个端点:

s['c': 'b': -2]  # 以 字符串 为索引切片,会包含两个端点
## 结果          # 以 整数 为索引切片,不包含右端点
c    6
a    4
b    2
dtype: int64

(2)以整数为索引的 Series

如果不特别指定所对应的列作为索引,那么会生成从0开始的整数索引作为默认索引。

任意一组符合长度要求的整数都可以作为索引。

和字符串一样,如果使用 [int] 或 [int_list] ,则可以取出对应索引 元素 的值:

s = pd.Series(['a', 'b', 'c', 'd', 'e', 'f'],index=[1, 3, 1, 2, 5, 4])


s[1]
## 结果
1    a
1    c
dtype: object

s[[2,3]]
## 结果
2    d
3    b
dtype: object

如果使用整数切片,则会取出对应索引 位置 的值,注意这里的整数切片同 Python 中的切片一样不包含右端点

s[1:-1:2]  # 这里不包含右端点
## 结果 
3    b
2    d
dtype: object

说明:

关于索引类型,如果不想陷入麻烦,那么请不要把纯浮点以及任何混合类型(字符串、整数、浮点类型等的混合)作为索引,否则可能会在具体的操作时报错或者返回非预期的结果,并且在实际的数据分析中也不存在这样做的动机。

3. loc索引器

前面讲到了对 DataFrame 的列进行选取,下面要讨论其行的选取。

对于表而言,有两种索引器:

  •  loc :基于 元素 的 loc 索引器
  •  iloc :基于 位置 的 iloc 索引器

loc 索引器的一般形式是 loc[*, *] ,其中第一个 * 代表行的选择,第二个 * 代表列的选择,如果省略第二个位置写作 loc[*] ,这个 * 是指行的筛选。其中, * 的位置一共有五类合法对象,分别是:

  • 单个元素
  • 元素列表
  • 元素切片
  • 布尔列表
  • 函数

(1) * 为单个元素

直接取出相应的行或列,如果该元素在索引中重复则结果为 DataFrame,否则为 Series

df_demo.loc['Qiang Sun'] # 多个人叫此名字
## 结果
                                  School      Grade  Gender  Weight Transfer
Name                                                                        
Qiang Sun            Tsinghua University     Junior  Female    53.0        N
Qiang Sun            Tsinghua University  Sophomore  Female    40.0        N
Qiang Sun  Shanghai Jiao Tong University     Junior  Female     NaN        N

In [19]: df_demo.loc['Quan Zhao'] # 名字唯一
Out[19]: 
School      Shanghai Jiao Tong University
Grade                              Junior
Gender                             Female
Weight                                 53
Transfer                                N
Name: Quan Zhao, dtype: object

可以同时选择

df_demo.loc['Qiang Sun', 'School'] # 返回Series
## 结果
Name
Qiang Sun              Tsinghua University
Qiang Sun              Tsinghua University
Qiang Sun    Shanghai Jiao Tong University
Name: School, dtype: object

df_demo.loc['Quan Zhao', 'School'] # 返回单个元素
## 结果
'Shanghai Jiao Tong University'

(2) * 为元素列表

此时,取出列表中所有元素值对应的行或列:

df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]  # 返回 DataFrame
## 结果
                                  School  Gender
Name                                            
Qiang Sun            Tsinghua University  Female
Qiang Sun            Tsinghua University  Female
Qiang Sun  Shanghai Jiao Tong University  Female
Quan Zhao  Shanghai Jiao Tong University  Female

(3) * 为切片

之前的 Series 使用字符串索引时提到,如果是唯一值的起点和终点字符,那么就可以使用切片,并且包含两个端点,如果不唯一则报错:

df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']  # 切片
## 结果 
                                      School      Grade  Gender
Name                                                           
Gaojuan You                 Fudan University  Sophomore    Male
Xiaoli Qian              Tsinghua University   Freshman  Female
Qiang Chu      Shanghai Jiao Tong University   Freshman  Female
Gaoqiang Qian            Tsinghua University     Junior  Female


## 切片中,还有别的单属性可以分开写,逗号分隔
df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School']
## 结果
Name
Gaojuan You                   Fudan University
Xiaoli Qian                Tsinghua University
Qiang Chu        Shanghai Jiao Tong University
Gaoqiang Qian              Tsinghua University
Name: School, dtype: object

## 若如果还有别的多个属性,也需要使用切片,若使用逗号分隔开写,则报错
df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School','Gender']
## 结果 报错
IndexingError: Too many indexers

注意:

如果 DataFrame 使用整数索引,其使用整数切片的时候和上面字符串索引的要求一致,都是 元素 切片,包含端点且起点、终点不允许有重复值。

df_loc_slice_demo = df_demo.copy()

df_loc_slice_demo.index = range(df_demo.shape[0],0,-1)  # 索引值从大到小,200`1(不包括右端点)逆序来排列

df_loc_slice_demo.loc[5:3]
## 结果
                          School   Grade  Gender  Weight Transfer
5               Fudan University  Junior  Female    46.0        N
4            Tsinghua University  Senior  Female    50.0        N
3  Shanghai Jiao Tong University  Senior  Female    45.0        N

df_loc_slice_demo.loc[3:5] # 没有返回,说明不是整数位置切片
## 结果 
Empty DataFrame
Columns: [School, Grade, Gender, Weight, Transfer]
Index: []

(4) * 为布尔列表

在实际的数据处理中,根据条件来筛选行是极其常见的,此处传入 loc 的布尔列表与 DataFrame 长度相同,且列表为 True 的位置所对应的行会被选中, False 则会被剔除。

df_demo.loc[df_demo.Weight>70].head()  # 选出体重超过70kg的学生
## 结果
                                      School      Grade Gender  Weight Transfer
Name                                                                           
Mei Sun        Shanghai Jiao Tong University     Senior   Male    89.0        N
Gaojuan You                 Fudan University  Sophomore   Male    74.0        N
Xiaopeng Zhou  Shanghai Jiao Tong University   Freshman   Male    74.0        N
Xiaofeng Sun             Tsinghua University     Senior   Male    71.0        N
Qiang Zheng    Shanghai Jiao Tong University     Senior   Male    87.0        N

也可以通过 isin 方法返回的布尔列表等价写出前面所提到的传入元素列表:

df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head()  # 选出所有大一和大四的同学信息
## 结果
                                       School     Grade  Gender  Weight Transfer
Name                                                                            
Gaopeng Yang    Shanghai Jiao Tong University  Freshman  Female    46.0        N
Changqiang You              Peking University  Freshman    Male    70.0        N
Mei Sun         Shanghai Jiao Tong University    Senior    Male    89.0        N
Xiaoli Qian               Tsinghua University  Freshman  Female    51.0        N
Qiang Chu       Shanghai Jiao Tong University  Freshman  Female    52.0        N

对于复合条件而言,可以用 |(或), &(且), ~(取反) 的组合来实现:

condition_1_1 = df_demo.School == 'Fudan University'  # 复旦大学

condition_1_2 = df_demo.Grade == 'Senior'  # 大四学生

condition_1_3 = df_demo.Weight > 70  # 体重超过 70kg

condition_1 = condition_1_1 & condition_1_2 & condition_1_3  # 复旦大学中体重超过70kg的大四学生

condition_2_1 = df_demo.School == 'Peking University'

condition_2_2 = df_demo.Grade == 'Senior'

condition_2_3 = df_demo.Weight > 80

condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3  # 北大男生中体重超过80kg的非大四的学生

df_demo.loc[condition_1 | condition_2]  # 组合后:选出复旦大学中体重超过70kg的大四学生,或者北大男生中体重超过80kg的非大四的学生
## 结果
                           School     Grade Gender  Weight Transfer
Name                                                               
Qiang Han       Peking University  Freshman   Male    87.0        N
Chengpeng Zhou   Fudan University    Senior   Male    81.0        N
Changpeng Zhao  Peking University  Freshman   Male    83.0        N
Chengpeng Qian   Fudan University    Senior   Male    73.0        Y

练一练:

select_dtypes 是一个实用函数,它能够从表中选出相应类型的列,若要选出所有数值型的列,只需使用 .select_dtypes('number') ,请利用布尔列表选择的方法结合 DataFrame 的 dtypes 属性在 learn_pandas 数据集上实现这个功能。

ex = pd.read_csv('joyful-pandas-master/data/learn_pandas.csv', index_col='Name')

ex.select_dtypes('number').head()
## 结果
Height	Weight	Test_Number
0	158.9	46.0	1
1	166.5	70.0	1
2	188.9	89.0	2
3	NaN	41.0	2
4	174.0	74.0	2

(5)* 为函数

这里的函数,必须以前面的四种合法形式之一为返回值,并且函数的输入值为 DataFrame 本身。假设仍然是上述复合条件筛选的例子,可以把逻辑写入一个函数中再返回,需要注意的是函数的形式参数 x 本质上即为 df_demo :

def condition(x):
     condition_1_1 = x.School == 'Fudan University'
     condition_1_2 = x.Grade == 'Senior'
     condition_1_3 = x.Weight > 70
     condition_1 = condition_1_1 & condition_1_2 & condition_1_3
     condition_2_1 = x.School == 'Peking University'
     condition_2_2 = x.Grade == 'Senior'
     condition_2_3 = x.Weight > 80
     condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3
     result = condition_1 | condition_2
     return result 

df_demo.loc[condition]
## 结果 
                           School     Grade Gender  Weight Transfer
Name                                                               
Qiang Han       Peking University  Freshman   Male    87.0        N
Chengpeng Zhou   Fudan University    Senior   Male    81.0        N
Changpeng Zhao  Peking University  Freshman   Male    83.0        N
Chengpeng Qian   Fudan University    Senior   Male    73.0        Y

也支持使用 lambda 表达式,其返回值也同样必须是先前提到的四种形式之一

df_demo.loc[lambda x:'Quan Zhao', lambda x:'Gender']
## 结果
'Female'

由于函数无法返回如 start: end: step 的切片形式,故返回切片时要用 slice 对象进行包装:

df_demo.loc[lambda x: slice('Gaojuan You', 'Gaoqiang Qian')]
## 结果 
                                      School      Grade  Gender  Weight Transfer
Name                                                                            
Gaojuan You                 Fudan University  Sophomore    Male    74.0        N
Xiaoli Qian              Tsinghua University   Freshman  Female    51.0        N
Qiang Chu      Shanghai Jiao Tong University   Freshman  Female    52.0        N
Gaoqiang Qian            Tsinghua University     Junior  Female    50.0        N

补充:

slice() 函数实现切片对象,主要用在切片操作函数里的参数传递。

用法见此链接:slice() 

slice 语法:

class slice(stop)
class slice(start, stop[, step])
参数说明:

start -- 起始位置
stop -- 结束位置
step -- 间距
返回值
返回一个切片对象。

最后需要指出的是,对于 Series 也可以使用 loc 索引,其遵循的原则与 DataFrame 中用于行筛选的 loc[*] 完全一致。

注意:

不要使用链式赋值,在对表或者序列赋值时,应当在使用一层索引器后直接进行赋值操作,这样做是由于进行多次索引后赋值是赋在临时返回的 copy 副本上的,而没有真正修改元素从而报出 SettingWithCopyWarning 警告。

4. iloc索引器

iloc 的使用与 loc 完全类似,只不过是针对位置进行筛选,在相应的 * 位置处一共也有五类合法对象,分别是:

  • 整数
  • 整数列表
  • 整数切片
  • 布尔列表
  • 函数

而且函数的返回值必须是前面的四类合法对象中的一个,其输入同样也为 DataFrame 本身。

df_demo.iloc[1, 1] # 第二行第二列
## 结果
'Freshman'

df_demo.iloc[[0, 1], [0, 1]] # 前两行前两列
## 结果
                                       School     Grade
Name                                                   
Gaopeng Yang    Shanghai Jiao Tong University  Freshman
Changqiang You              Peking University  Freshman

df_demo.iloc[1: 4, 2:4] # 切片不包含结束端点
## 结果
                Gender  Weight
Name                          
Changqiang You    Male    70.0
Mei Sun           Male    89.0
Xiaojuan Sun    Female    41.0

df_demo.iloc[lambda x: slice(1, 4)] # 传入切片为返回值的函数,第1,2,3列
## 结果
                                       School      Grade  Gender  Weight Transfer
Name                                                                             
Changqiang You              Peking University   Freshman    Male    70.0        N
Mei Sun         Shanghai Jiao Tong University     Senior    Male    89.0        N
Xiaojuan Sun                 Fudan University  Sophomore  Female    41.0        N

在使用布尔列表的时候要特别注意,不能传入 Series 而必须传入序列的 values ,否则会报错。因此,在使用布尔筛选的时候还是应当优先考虑 loc 的方式。

df_demo.iloc[(df_demo.Weight>80).values].head()  # 选出体重超过80kg的学生
## 结果
                                       School      Grade Gender  Weight Transfer
Name                                                                            
Mei Sun         Shanghai Jiao Tong University     Senior   Male    89.0        N
Qiang Zheng     Shanghai Jiao Tong University     Senior   Male    87.0        N
Qiang Han                   Peking University   Freshman   Male    87.0        N
Chengpeng Zhou               Fudan University     Senior   Male    81.0        N
Feng Han        Shanghai Jiao Tong University  Sophomore   Male    82.0        N


(df_demo.Weight>80).values   # 为一系列的布尔值,取出 True 这一列的值
## 结果
array([False, False,  True, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
        ...
       ])

对 Series 而言同样也可以通过 iloc 返回相应位置的值或子序列:

df_demo.School.iloc[1]
## 结果
'Peking University'

df_demo.School.iloc[1:5:2]
## 结果
Name
Changqiang You    Peking University
Xiaojuan Sun       Fudan University
Name: School, dtype: object

5. query方法

在 pandas 中,支持把字符串形式的查询表达式传入 query 方法来查询数据,其表达式的执行结果必须返回布尔列表。在进行复杂索引时,由于这种检索方式无需像普通方法一样重复使用 DataFrame 的名字来引用列名,一般而言会使代码长度在不降低可读性的前提下有所减少。

# 选出复旦大学中体重超过70kg的大四学生,或者北大男生中体重超过80kg的非大四的学生
df.query('((School == "Fudan University")&'
   ....:          ' (Grade == "Senior")&'
   ....:          ' (Weight > 70))|'
   ....:          '((School == "Peking University")&'
   ....:          ' (Grade != "Senior")&'
   ....:          ' (Weight > 80))') 
## 结果
                School     Grade            Name Gender  Weight Transfer
38   Peking University  Freshman       Qiang Han   Male    87.0        N
66    Fudan University    Senior  Chengpeng Zhou   Male    81.0        N
99   Peking University  Freshman  Changpeng Zhao   Male    83.0        N
131   Fudan University    Senior  Chengpeng Qian   Male    73.0        Y

在 query 表达式中,帮用户注册了所有来自 DataFrame 的列名,所有属于该 Series 的方法都可以被调用,和正常的函数调用并没有区别:

df.query('Weight > Weight.mean()').head()
## 结果 
                           School      Grade            Name  Gender  Weight Transfer
1               Peking University   Freshman  Changqiang You    Male    70.0        N
2   Shanghai Jiao Tong University     Senior         Mei Sun    Male    89.0        N
4                Fudan University  Sophomore     Gaojuan You    Male    74.0        N
10  Shanghai Jiao Tong University   Freshman   Xiaopeng Zhou    Male    74.0        N
14            Tsinghua University     Senior    Xiaomei Zhou  Female    57.0  

注意:

query中引用带空格的列名:对于含有空格的列名,需要使用 `col name` 的方式进行引用(英文键盘下的 Shift + ~)。

而且在 query 中还注册了若干英语的字面用法,帮助提高可读性。

# 筛选出男生中不是大一大二的学生
df.query('(Grade not in ["Freshman", "Sophomore"]) and''(Gender=="Male")').head()  # 注意 and 的位置,若放在单引号外边,虽然能够运行不报错,但是结果是错误的

## 结果 
                           School   Grade           Name Gender  Weight Transfer
2   Shanghai Jiao Tong University  Senior        Mei Sun   Male    89.0        N
16            Tsinghua University  Junior  Xiaoqiang Qin   Male    68.0        N
17            Tsinghua University  Junior      Peng Wang   Male    65.0        N
18            Tsinghua University  Senior   Xiaofeng Sun   Male    71.0        N
21  Shanghai Jiao Tong University  Senior  Xiaopeng Shen   Male    62.0      NaN

此外,在字符串中出现与列表的比较时:

  •  == :表示元素出现在列表 ,等价于 is in 
  • != :表示元素没有出现在列表 ,等价于 not in
df.query('Grade == ["Junior", "Senior"]').head()  # 查询所有大三和大四的学生
## 结果
                           School   Grade           Name  Gender  Weight Transfer
2   Shanghai Jiao Tong University  Senior        Mei Sun    Male    89.0        N
7             Tsinghua University  Junior  Gaoqiang Qian  Female    50.0        N
9               Peking University  Junior        Juan Xu  Female     NaN        N
11            Tsinghua University  Junior    Xiaoquan Lv  Female    43.0        N
12  Shanghai Jiao Tong University  Senior       Peng You  Female    48.0      NaN

对于 query 中的字符串,在变量名前加 @ 符号作为引用外部变量:

low, high =70, 80

df.query('Weight.between(@low, @high)').head()  # 取出体重位于70kg到80kg之间的学生
## 结果
                           School      Grade            Name Gender  Weight Transfer
1               Peking University   Freshman  Changqiang You   Male    70.0        N
4                Fudan University  Sophomore     Gaojuan You   Male    74.0        N
10  Shanghai Jiao Tong University   Freshman   Xiaopeng Zhou   Male    74.0        N
18            Tsinghua University     Senior    Xiaofeng Sun   Male    71.0        N
35              Peking University   Freshman      Gaoli Zhao   Male    78.0        N

6. 随机抽样

把 DataFrame 的每一行看作一个样本,或把每一列看作一个特征,再把整个 DataFrame 看作总体,想要对样本或特征进行随机抽样就可以用 sample 函数。

sample 函数中的主要参数为

  • n:抽样数量
  • axis:抽样的方向(0为行、1为列)
  • frac:抽样比例(0.3则为从总体中抽出30%的样本)
  • replace:是否放回抽样,当 replace = True 则表示有放回抽样
  • weights:每个样本的抽样相对概率
# 构造的 df_sample 以 value 值的相对大小为抽样概率进行有放回抽样,抽样数量为3
df_sample = pd.DataFrame({'id': list('abcde'), 'value': [1, 2, 3, 4, 90]})

df_sample
## 结果 
  id  value
0  a      1
1  b      2
2  c      3
3  d      4
4  e     90

df_sample.sample(3, replace = True, weights = df_sample.value)  # 有放回抽样
## 结果 
  id  value
4  e     90
4  e     90
4  e     90

二、多级索引

1. 多级索引及其表的结构

相当于 表格 的行和列,都具有多级索引。

上图通过颜色区分,标记了 DataFrame 的结构。与单层索引的表一样,具备元素值、行索引和列索引三个部分。其中,这里的行索引和列索引都是 MultiIndex 类型,只不过 索引中的一个元素是元组 而不是单层索引中的标量。例如,行索引的第四个元素为 ("B", "Male") ,列索引的第二个元素为 ("Height", "Senior") ,这里需要注意,外层连续出现相同的值时,第一次之后出现的会被隐藏显示,使结果的可读性增强。

与单层索引类似, MultiIndex 也具有名字属性,图中的 School 和 Gender 分别对应了表的第一层和第二层行索引的名字, Indicator 和 Grade 分别对应了第一层和第二层列索引的名字。

  • 索引的名字和值属性分别可以通过 names 和 values 获得:
df_multi.index.names
## 结果
FrozenList(['School', 'Gender'])

df_multi.columns.names
## 结果
FrozenList(['Indicator', 'Grade'])

df_multi.index.values
## 结果 
array([('A', 'Female'), ('A', 'Male'), ('B', 'Female'), ('B', 'Male'),
       ('C', 'Female'), ('C', 'Male'), ('D', 'Female'), ('D', 'Male')],
      dtype=object)

df_multi.columns.values
## 结果
array([('Height', 'Freshman'), ('Height', 'Senior'),
       ('Height', 'Sophomore'), ('Height', 'Junior'),
       ('Weight', 'Freshman'), ('Weight', 'Senior'),
       ('Weight', 'Sophomore'), ('Weight', 'Junior')], dtype=object)
  • 通过 get_level_values 得到某一层的索引:
df_multi.index.get_level_values(0)
## 结果
Index(['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D'], dtype='object', name='School')

注意:

对于索引而言,无论是单层还是多层,用户都无法通过 index_obj[0] = item 的方式来修改元素,也不能通过 index_name[0] = new_name 的方式来修改名字。

2. 多级索引中的loc索引器

将学校和年级设为索引,此时的行为多级索引列为单级索引,由于默认状态的列索引不含名字,因此对应于图中 Indicator 和 Grade 的索引名位置是空缺的。

df_multi = df.set_index(['School', 'Grade'])  # 将学校和年级设为索引

df_multi.head()
## 结果
                                                   Name  Gender  Weight Transfer
School                        Grade                                             
Shanghai Jiao Tong University Freshman     Gaopeng Yang  Female    46.0        N
Peking University             Freshman   Changqiang You    Male    70.0        N
Shanghai Jiao Tong University Senior            Mei Sun    Male    89.0        N
Fudan University              Sophomore    Xiaojuan Sun  Female    41.0        N
                              Sophomore     Gaojuan You    Male    74.0        N

多级索引中的单个元素是以元组为单位,所以完全可以使用在第一节的 loc 和 iloc 方法,但是需把标量的位置替换成对应的元组。

注意:

当使用切片时,在单级别索引中只要切片端点元素是唯一的,那么就可以进行切片,但在多级索引中,无论元组在索引中是否重复出现,都必须经过排序才能使用切片,否则报错。

多级索引中的元组有一种特殊的用法,可以对多层的元素进行交叉组合后索引,但同时需要指定 loc 的列,全选则用 : 表示。其中,每一层需要选中的元素用列表存放,传入 loc 的形式为 [(level_0_list, level_1_list), cols] 。

res = df_multi.loc[(['Peking University', 'Fudan University'],['Sophomore', 'Junior']), :]  # 想要得到所有北大和复旦的大二大三学生

res.head()
## 结果
                                     Name  Gender  Weight Transfer
School            Grade                                           
Peking University Sophomore   Changmei Xu  Female    43.0        N
                  Sophomore  Xiaopeng Qin    Male     NaN        N
                  Sophomore        Mei Xu  Female    39.0        N
                  Sophomore   Xiaoli Zhou  Female    55.0        N
                  Sophomore      Peng Han  Female    34.0      NaN

res.shape
## 结果
(33, 4)

下面的语句和上面类似,但仍然传入的是元素(这里为元组)的列表,它们的意义是不同的:

res = df_multi.loc[[('Peking University', 'Junior'),('Fudan University', 'Sophomore')]]  # 选出北大的大三学生和复旦的大二学生

res.head()
## 结果
                                   Name  Gender  Weight Transfer
School            Grade                                         
Peking University Junior        Juan Xu  Female     NaN        N
                  Junior  Changjuan You  Female    47.0        N
                  Junior       Gaoli Xu  Female    48.0        N
                  Junior   Gaoquan Zhou    Male    70.0        N
                  Junior      Qiang You  Female    56.0        N

res.shape
## 结果
(16, 4)

3. IndexSlice对象

  • IndexSlice 对象:对每层进行切片,而且允许将切片和布尔列表混合使用

即使在索引不重复的时候,也只能对元组整体进行切片,而不能对每层进行切片,也不允许将切片和布尔列表混合使用,引入 IndexSlice 对象就能解决这个问题。

 Slice 对象一共有两种形式:

  • 第一种为loc[idx[*,*]] 型
  • 第二种为 loc[idx[*,*],idx[*,*]] 型

使用 silce 对象时,先要进行定义:

idx = pd.IndexSlice

(1)loc[idx[*,*]] 型

这种情况并不能进行多层分别切片,前一个 * 表示行的选择,后一个 * 表示列的选择,与单纯的 loc 是类似的:

df_ex.loc[idx['C':, ('D', 'f'):]]
## 结果 
Big          D  E        F      
Small        f  d  e  f  d  e  f
Upper Lower                     
C     a      2  5  9 -9  5 -6  3
      b     -5 -3 -5  6 -6  3 -5
      c      6 -6  6  4  7  8 -4

也支持布尔序列的索引:

df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0
## 结果
Big          D     F
Small        d  e  e
Upper Lower         
A     a      3  6  9
      b     -3  3 -4
      c     -1  0  9

 (2)loc[idx[*,*],idx[*,*]] 型

这种情况能够分层进行切片,前一个 idx 指代的是行索引,后一个是列索引。

df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
## 结果
Big          E     F   
Small        e  f  e  f
Upper Lower            
A     b     -2  5 -4  4
      c      6  6  9 -6

4. 多级索引的构造

使用多级索引表的结构和切片,除了使用 set_index 之外,如何自己构造多级索引呢?能够使用常用的三种自己构造多级索引的方法:

  • from_tuples():根据传入由元组组成的列表进行构造
  • from_arrays():根据传入列表中,对应层的列表进行构造
  • from_product():根据给定多个列表的笛卡尔积进行构造

并且它们都是 pd.MultiIndex 对象下的函数。

## from_tuples 根据传入由元组组成的列表进行构造
my_tuple = [('a','cat'),('a','dog'),('b','cat'),('b','dog')]

pd.MultiIndex.from_tuples(my_tuple, names=['First','Second'])
## 结果
MultiIndex([('a', 'cat'),
            ('a', 'dog'),
            ('b', 'cat'),
            ('b', 'dog')],
           names=['First', 'Second'])


## from_arrays 根据传入列表中,对应层的列表进行构造
my_array = [list('aabb'), ['cat', 'dog']*2]

pd.MultiIndex.from_arrays(my_array, names=['First','Second'])
## 结果 
MultiIndex([('a', 'cat'),
            ('a', 'dog'),
            ('b', 'cat'),
            ('b', 'dog')],
           names=['First', 'Second'])


## from_product 根据给定多个列表的笛卡尔积进行构造
my_list1 = ['a','b']

my_list2 = ['cat','dog']

pd.MultiIndex.from_product([my_list1, my_list2], names=['First','Second'])

## 结果
MultiIndex([('a', 'cat'),
            ('a', 'dog'),
            ('b', 'cat'),
            ('b', 'dog')],
           names=['First', 'Second'])

三、索引的常用方法

1. 索引层的交换和删除

构造一个三级索引:

np.random.seed(0)

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', 'Other')) 

df_ex = pd.DataFrame(np.random.randint(-9,10,(8,8)),
                         index=mul_index1,
                         columns=mul_index2)
 

df_ex
## 结果 
Big                 C               D            
Small               c       d       c       d    
Other             cat dog cat dog cat dog cat dog
Upper Lower Extra                                
A     a     alpha   3   6  -9  -6  -6  -2   0   9
            beta   -5  -3   3  -8  -3  -2   5   8
      b     alpha  -4   4  -1   0   7  -4   6   6
            beta   -9   9  -6   8   5  -2  -9  -8
B     a     alpha   0  -9   1  -6   2   9  -7  -9
            beta   -9  -5  -4  -3  -1   8   6  -5
      b     alpha   0   1  -8  -8  -2   0  -6  -3
            beta    2   5   9  -9   5  -6   3   1

索引层的交换由 swaplevel 和 reorder_levels 完成

  •  swaplevel :只能交换两个层
  •  reorder_levels :可以交换任意层

并且两者都可以指定交换的是轴是哪一个,即行索引或列索引:

df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一层和第三层交换
## 结果 
Other             cat dog cat dog cat dog cat dog
Small               c   c   d   d   c   c   d   d
Big                 C   C   C   C   D   D   D   D
Upper Lower Extra                                
A     a     alpha   3   6  -9  -6  -6  -2   0   9
            beta   -5  -3   3  -8  -3  -2   5   8
      b     alpha  -4   4  -1   0   7  -4   6   6
            beta   -9   9  -6   8   5  -2  -9  -8
B     a     alpha   0  -9   1  -6   2   9  -7  -9

df_ex.reorder_levels([2,0,1],axis=0).head() # 列表数字指代原来索引中的层
## 结果
Big                 C               D            
Small               c       d       c       d    
Other             cat dog cat dog cat dog cat dog
Extra Upper Lower                                
alpha A     a       3   6  -9  -6  -6  -2   0   9
beta  A     a      -5  -3   3  -8  -3  -2   5   8
alpha A     b      -4   4  -1   0   7  -4   6   6
beta  A     b      -9   9  -6   8   5  -2  -9  -8
alpha B     a       0  -9   1  -6   2   9  -7  -9

注意:

轴之间的索引交换:这里指的是行或列索引内部的交换,没有说明不同方向索引之间的交换。

使用 droplevel 方法,删除某一层的索引:

df_ex.droplevel(1,axis=1)
## 结果 
Big                 C               D            
Other             cat dog cat dog cat dog cat dog
Upper Lower Extra                                
A     a     alpha   3   6  -9  -6  -6  -2   0   9
            beta   -5  -3   3  -8  -3  -2   5   8
      b     alpha  -4   4  -1   0   7  -4   6   6
            beta   -9   9  -6   8   5  -2  -9  -8
B     a     alpha   0  -9   1  -6   2   9  -7  -9
            beta   -9  -5  -4  -3  -1   8   6  -5
      b     alpha   0   1  -8  -8  -2   0  -6  -3
            beta    2   5   9  -9   5  -6   3   1

df_ex.droplevel([0,1],axis=0)
## 结果
Big     C               D            
Small   c       d       c       d    
Other cat dog cat dog cat dog cat dog
Extra                                
alpha   3   6  -9  -6  -6  -2   0   9
beta   -5  -3   3  -8  -3  -2   5   8
alpha  -4   4  -1   0   7  -4   6   6
beta   -9   9  -6   8   5  -2  -9  -8
alpha   0  -9   1  -6   2   9  -7  -9
beta   -9  -5  -4  -3  -1   8   6  -5
alpha   0   1  -8  -8  -2   0  -6  -3
beta    2   5   9  -9   5  -6   3   1

2. 索引属性的修改

  •  rename_axis()可以对索引层的名字进行修改,常用的修改方式是传入字典的映射:
df_ex.rename_axis(index={'Upper':'Changed_row'}, columns={'Other':'Changed_Col'}).head()
## 结果
Big                       C               D            
Small                     c       d       c       d    
Changed_Col             cat dog cat dog cat dog cat dog
Changed_row Lower Extra                                
A           a     alpha   3   6  -9  -6  -6  -2   0   9
                  beta   -5  -3   3  -8  -3  -2   5   8
            b     alpha  -4   4  -1   0   7  -4   6   6
                  beta   -9   9  -6   8   5  -2  -9  -8
B           a     alpha   0  -9   1  -6   2   9  -7  -9
  • rename() 可以对索引的值进行修改,如果是多级索引需要指定修改的层号 level :
df_ex.rename(columns={'cat':'not_cat'}, level=2).head()

## 结果
Big                     C                       D                
Small                   c           d           c           d    
Other             not_cat dog not_cat dog not_cat dog not_cat dog
Upper Lower Extra                                                
A     a     alpha       3   6      -9  -6      -6  -2       0   9
            beta       -5  -3       3  -8      -3  -2       5   8
      b     alpha      -4   4      -1   0       7  -4       6   6
            beta       -9   9      -6   8       5  -2      -9  -8
B     a     alpha       0  -9       1  -6       2   9      -7  -9

传入参数也可以是函数( lambda ),其输入值就是索引元素:

df_ex.rename(index=lambda x:str.upper(x), level=2).head()

## 结果 
Big                 C               D            
Small               c       d       c       d    
Other             cat dog cat dog cat dog cat dog
Upper Lower Extra                                
A     a     ALPHA   3   6  -9  -6  -6  -2   0   9
            BETA   -5  -3   3  -8  -3  -2   5   8
      b     ALPHA  -4   4  -1   0   7  -4   6   6
            BETA   -9   9  -6   8   5  -2  -9  -8
B     a     ALPHA   0  -9   1  -6   2   9  -7  -9

对于整个索引的元素替换,可以利用迭代器实现:

new_values = iter(list('abcdefgh'))

df_ex.rename(index=lambda x:next(new_values), level=2)

## 结果 
Big                 C               D            
Small               c       d       c       d    
Other             cat dog cat dog cat dog cat dog
Upper Lower Extra                                
A     a     a       3   6  -9  -6  -6  -2   0   9
            b      -5  -3   3  -8  -3  -2   5   8
      b     c      -4   4  -1   0   7  -4   6   6
            d      -9   9  -6   8   5  -2  -9  -8
B     a     e       0  -9   1  -6   2   9  -7  -9
            f      -9  -5  -4  -3  -1   8   6  -5
      b     g       0   1  -8  -8  -2   0  -6  -3
            h       2   5   9  -9   5  -6   3   1

对某个位置的元素进行修改,在单层索引时容易实现,即先取出索引的 values 属性,再给对得到的列表进行修改,最后再对 index 对象重新赋值。但是如果是多级索引的话就有些麻烦,一个解决的方案是先把某一层索引临时转为表的元素,然后再进行修改,最后重新设定为索引。

  • map() 函数,它是定义在 Index 上的方法,与 rename() 方法中层的函数式用法是类似的,只不过它传入的不是层的标量值,而是直接传入索引的元组,这为用户进行跨层的修改提供了遍历。
df_temp = df_ex.copy()

new_idx = df_temp.index.map(lambda x: (x[0], x[1], str.upper(x[2])))


df_temp.index = new_idx

df_temp.head()
## 结果
Big                 C               D            
Small               c       d       c       d    
Other             cat dog cat dog cat dog cat dog
Upper Lower Extra                                
A     a     ALPHA   3   6  -9  -6  -6  -2   0   9
            BETA   -5  -3   3  -8  -3  -2   5   8
      b     ALPHA  -4   4  -1   0   7  -4   6   6
            BETA   -9   9  -6   8   5  -2  -9  -8
B     a     ALPHA   0  -9   1  -6   2   9  -7  -9
  •  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() # 单层索引
## 结果
Big         C               D            
Small       c       d       c       d    
Other     cat dog cat dog cat dog cat dog
A-a-alpha   3   6  -9  -6  -6  -2   0   9
A-a-beta   -5  -3   3  -8  -3  -2   5   8
A-b-alpha  -4   4  -1   0   7  -4   6   6
A-b-beta   -9   9  -6   8   5  -2  -9  -8
B-a-alpha   0  -9   1  -6   2   9  -7  -9

也可以反向地展开(类似于 解压):

new_idx = df_temp.index.map(lambda x:tuple(x.split('-')))

df_temp.index = new_idx

df_temp.head() # 三层索引
## 结果 
Big         C               D            
Small       c       d       c       d    
Other     cat dog cat dog cat dog cat dog
A a alpha   3   6  -9  -6  -6  -2   0   9
    beta   -5  -3   3  -8  -3  -2   5   8
  b alpha  -4   4  -1   0   7  -4   6   6
    beta   -9   9  -6   8   5  -2  -9  -8
B a alpha   0  -9   1  -6   2   9  -7  -9

3. 索引的设置与重置

构造一个新表:

df_new = pd.DataFrame({'A':list('aacd'), 'B':list('PQRT'), 'C':[1,2,3,4]})
 
df_new
## 结果 
   A  B  C
0  a  P  1
1  a  Q  2
2  c  R  3
3  d  T  4

索引的设置可以使用 set_index ,它的主要参数是 append ,表示是否来保留原来的索引,直接把新设定的添加到原索引的内层:

df_new.set_index('A')
## 结果
   B  C
A      
a  P  1
a  Q  2
c  R  3
d  T  4

df_new.set_index('A', append=True)
## 结果
     B  C
  A      
0 a  P  1
1 a  Q  2
2 c  R  3
3 d  T  4

同时指定多个列作为索引:

df_new.set_index(['A', 'B'])  # 将 A  B 作为索引
## 结果
     C
A B   
a P  1
  Q  2
c R  3
d T  4

可以直接在参数中传入相应的 Series ,通过  Series  添加想要的索引的列。

my_index = pd.Series(list('WXYZ'), name='D')

df_new = df_new.set_index(['A', my_index])

df_new
## 结果
     B  C
A D      
a W  P  1
  X  Q  2
c Y  R  3
d Z  T  4

reset_index 是 set_index 的逆函数,其主要参数是 drop ,表示是否要把去掉的索引层丢弃,而不是添加到列中:

df_new.reset_index(['D'])
## 结果
   D  B  C
A         
a  W  P  1
a  X  Q  2
c  Y  R  3
d  Z  T  4

df_new.reset_index(['D'], drop=True)
## 结果
   B  C
A      
a  P  1
a  Q  2
c  R  3
d  T  4

注意:

如果重置了所有的索引,那么 pandas 会直接重新生成一个默认索引:

df_new.reset_index()
## 结果 
   A  D  B  C
0  a  W  P  1
1  a  X  Q  2
2  c  Y  R  3
3  d  Z  T  4

4. 索引的变形

对索引做一些扩充或者剔除:给定一个新的索引,把原表中相应的索引对应元素填充到新索引构成的表中。

## 所给的表中列出了员工信息,需要重新制作一张新的表,要求增加一名员工的同时去掉身高列并增加性别列
df_reindex = pd.DataFrame({"Weight":[60,70,80],
                            "Height":[176,180,179]},
                            index=['1001','1003','1002'])
 

df_reindex
## 结果
      Weight  Height
1001      60     176
1003      70     180
1002      80     179

df_reindex.reindex(index=['1001','1002','1003','1004'],  # 增加一名员工的同时去掉身高列并增加性别列
                    columns=['Weight','Gender'])
 
## 结果 
      Weight  Gender
1001    60.0     NaN
1002    80.0     NaN
1003    70.0     NaN
1004     NaN     NaN

这种情况常出现在时间序列索引的时间点填充以及 ID 编号的扩充。

而且, reindex 中会根据元素对其,与位置无关。说的就是,在原来表中的数据和新表中会根据索引自动对其,例如原来的1002号位置在1003号之后,而新表中相反, 通过 reindex 会根据元素对齐,变成 1002号位置在1003号之前(对齐)。

reindex_like() 与 reindex 功能类似。即仿照传入的表的索引来进行被调用表索引的变形。

# 等价于 reindex() 功能
df_existed = pd.DataFrame(index=['1001','1002','1003','1004'],
                          columns=['Weight','Gender'])

df_reindex.reindex_like(df_existed)
## 结果
      Weight  Gender
1001    60.0     NaN
1002    80.0     NaN
1003    70.0     NaN
1004     NaN     NaN

四、索引运算

1. 集合的运算法则

利用集合运算来取出符合条件行。

常见的四种集合运算

2. 一般的索引运算

集合的元素是互异的,但是索引中可能有相同的元素,先用 unique 去重后再进行运算。

# 构造两张简单的示例表
df_set_1 = pd.DataFrame([[0,1],[1,2],[3,4]],
                         index = pd.Index(['a','b','a'],name='id1'))
 

df_set_2 = pd.DataFrame([[4,5],[2,6],[7,1]],
                         index = pd.Index(['b','b','c'],name='id2'))
 

id1, id2 = df_set_1.index.unique(), df_set_2.index.unique()

id1.intersection(id2)
## 结果
Index(['b'], dtype='object')

id1.union(id2)
## 结果
Index(['a', 'b', 'c'], dtype='object')

id1.difference(id2)
## 结果
Index(['a'], dtype='object')

id1.symmetric_difference(id2)
## 结果
Index(['a', 'c'], dtype='object')

四类运算还可以用等价的符号表示:

id1 & id2
## 结果
Index(['b'], dtype='object')

id1 | id2
## 结果
Index(['a', 'b', 'c'], dtype='object')

(id1 ^ id2) & id1
## 结果
Index(['a'], dtype='object')

id1 ^ id2 # ^符号即对称差
## 结果
Index(['a', 'c'], dtype='object')

两张表需要做集合运算的列并没有被设置索引,一种办法是先转成索引,运算后再恢复,另一种方法是利用 isin 函数。

# 在重置索引的第一张表中选出id列交集的所在行
df_set_in_col_1 = df_set_1.reset_index()

df_set_in_col_2 = df_set_2.reset_index()

df_set_in_col_1
## 结果
  id1  0  1
0   a  0  1
1   b  1  2
2   a  3  4

df_set_in_col_2
## 结果
  id2  0  1
0   b  4  5
1   b  2  6
2   c  7  1

df_set_in_col_1[df_set_in_col_1.id1.isin(df_set_in_col_2.id2)]
## 结果
  id1  0  1
1   b  1  2

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值