一、索引器
导入相关库:
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