Pandas学习——索引

索引器

在这里插入图片描述

DataFrame 的列索引

列索引是最常见的索引形式,一般通过 [] 来实现。通过 [列名] 可以从 DataFrame 中取出相应的列,返回值为 Series ,例如从表中取出姓名一列:

df = pd.read_csv('G:\代码\joyful-pandas-master\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

如果要取出多个列,则可以通过 [列名组成的列表] ,其返回值为一个 DataFrame ,例如从表中取出性别和姓名两列:

df[['Gender', 'Name']].head()
GenderName
0FemaleGaopeng Yang
1MaleChangqiang You
2MaleMei Sun
3FemaleXiaojuan Sun
4MaleGaojuan You

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

 df.Name.head()
0      Gaopeng Yang
1    Changqiang You
2           Mei Sun
3      Xiaojuan Sun
4       Gaojuan You
Name: Name, dtype: object

Series 的行索引

以字符串为索引的 Series

如果取出单个索引的对应元素,则可以使用 [item] ,若 Series 只有单个值对应,则返回这个标量值,如果有多个值对应,则返回一个 Series:

s = pd.Series([1, 2, 3, 4, 5, 6],index=['a', 'b', 'a', 'a', 'a', 'c'])
s 
a    1
b    2
a    3
a    4
a    5
c    6
dtype: int64
 s['a']
a    1
a    3
a    4
a    5
dtype: int64

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

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

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

s['c': 'b': -2]
c    6
a    4
b    2
dtype: int64

以整数为索引的 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] # index第1,3个位置对应的值
3    b
2    d
dtype: object

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

loc索引器( DataFrame 的行索引)

前面讲到了对 DataFrame 的列进行选取,下面要讨论其行的选取。对于表而言,有两种索引器,一种是基于元素loc索引器,另一种是基于位置iloc索引器。

loc 索引器的一般形式是 loc[*,*] ,其中第一个 * 代表行的选择,第二个 * 代表列的选择,如果省略第二个位置写作 loc[*] ,这个 * 是指行的筛选。其中, * 的位置一共有五类合法对象,分别是:单个元素、元素列表、元素切片、布尔列表以及函数,下面将依次说明。

df_demo = df.set_index('Name') # 把 Name 列设为索引
df_demo.head()
SchoolGradeGenderWeightTransfer
Name
Gaopeng YangShanghai Jiao Tong UniversityFreshmanFemale46.0N
Changqiang YouPeking UniversityFreshmanMale70.0N
Mei SunShanghai Jiao Tong UniversitySeniorMale89.0N
Xiaojuan SunFudan UniversitySophomoreFemale41.0N
Gaojuan YouFudan UniversitySophomoreMale74.0N

* 为单个元素

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

df_demo.loc['Qiang Sun'] # 多个人叫此名字
SchoolGradeGenderWeightTransfer
Name
Qiang SunTsinghua UniversityJuniorFemale53.0N
Qiang SunTsinghua UniversitySophomoreFemale40.0N
Qiang SunShanghai Jiao Tong UniversityJuniorFemaleNaNN
df_demo.loc['Quan Zhao'] # 名字唯一
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[['Qiang Sun','Quan Zhao'], ['School','Gender']]
SchoolGender
Name
Qiang SunTsinghua UniversityFemale
Qiang SunTsinghua UniversityFemale
Qiang SunShanghai Jiao Tong UniversityFemale
Quan ZhaoShanghai Jiao Tong UniversityFemale

* 为切片

如果是唯一值的起点和终点字符,那么就可以使用切片,并且包含两个端点,如果不唯一则报错:

df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender'] 
SchoolGradeGender
Name
Gaojuan YouFudan UniversitySophomoreMale
Xiaoli QianTsinghua UniversityFreshmanFemale
Qiang ChuShanghai Jiao Tong UniversityFreshmanFemale
Gaoqiang QianTsinghua UniversityJuniorFemale

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

df_loc_slice_demo = df_demo.copy()
df_loc_slice_demo.index = range(df_demo.shape[0],0,-1)
df_loc_slice_demo.loc[5:3]
SchoolGradeGenderWeightTransfer
5Fudan UniversityJuniorFemale46.0N
4Tsinghua UniversitySeniorFemale50.0N
3Shanghai Jiao Tong UniversitySeniorFemale45.0N
df_loc_slice_demo.loc[3:5] # 没有返回,说明不是整数位置切片
SchoolGradeGenderWeightTransfer

* 为布尔列表

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

例如,选出体重超过70kg的学生:

df_demo.loc[df_demo.Weight>70].head()
SchoolGradeGenderWeightTransfer
Name
Mei SunShanghai Jiao Tong UniversitySeniorMale89.0N
Gaojuan YouFudan UniversitySophomoreMale74.0N
Xiaopeng ZhouShanghai Jiao Tong UniversityFreshmanMale74.0N
Xiaofeng SunTsinghua UniversitySeniorMale71.0N
Qiang ZhengShanghai Jiao Tong UniversitySeniorMale87.0N

前面所提到的传入元素列表,也可以通过 isin 方法返回的布尔列表等价写出,例如选出所有大一和大四的同学信息:

df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head()
SchoolGradeGenderWeightTransfer
Name
Gaopeng YangShanghai Jiao Tong UniversityFreshmanFemale46.0N
Changqiang YouPeking UniversityFreshmanMale70.0N
Mei SunShanghai Jiao Tong UniversitySeniorMale89.0N
Xiaoli QianTsinghua UniversityFreshmanFemale51.0N
Qiang ChuShanghai Jiao Tong UniversityFreshmanFemale52.0N

对于复合条件而言,可以用 |(或), &(且), ~(取反) 的组合来实现,例如选出复旦大学中体重超过70kg的大四学生,或者北大男生中体重超过80kg的非大四的学生:

condition_1_1 = df_demo.School == 'Fudan University'
condition_1_2 = df_demo.Grade == 'Senior'
condition_1_3 = df_demo.Weight > 70
condition_1 = condition_1_1 & condition_1_2 & condition_1_3

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

df_demo.loc[condition_1 | condition_2]
SchoolGradeGenderWeightTransfer
Name
Qiang HanPeking UniversityFreshmanMale87.0N
Chengpeng ZhouFudan UniversitySeniorMale81.0N
Changpeng ZhaoPeking UniversityFreshmanMale83.0N
Chengpeng QianFudan UniversitySeniorMale73.0Y

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

df_demo.select_dtypes('number').head()
Weight
Name
Gaopeng Yang46.0
Changqiang You70.0
Mei Sun89.0
Xiaojuan Sun41.0
Gaojuan You74.0
df_demo[df_demo.columns[df_demo.dtypes == 'float64']].head() 
Weight
Name
Gaopeng Yang46.0
Changqiang You70.0
Mei Sun89.0
Xiaojuan Sun41.0
Gaojuan You74.0

* 为函数

这里的函数,必须以前面的四种合法形式之一为返回值,并且函数的输入值为 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]
SchoolGradeGenderWeightTransfer
Name
Qiang HanPeking UniversityFreshmanMale87.0N
Chengpeng ZhouFudan UniversitySeniorMale81.0N
Changpeng ZhaoPeking UniversityFreshmanMale83.0N
Chengpeng QianFudan UniversitySeniorMale73.0Y

此外,还支持使用 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')]
SchoolGradeGenderWeightTransfer
Name
Gaojuan YouFudan UniversitySophomoreMale74.0N
Xiaoli QianTsinghua UniversityFreshmanFemale51.0N
Qiang ChuShanghai Jiao Tong UniversityFreshmanFemale52.0N
Gaoqiang QianTsinghua UniversityJuniorFemale50.0N

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

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

df_chain = pd.DataFrame([[0,0],[1,0],[-1,0]], columns=list('AB'))
df_chain 
AB
000
110
2-10
import warnings

with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
        df_chain[df_chain.A!=0].B = 1 # 使用方括号列索引后,再使用点的列索引
    except Warning as w:
        Warning_Msg = w

print(Warning_Msg)
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
df_chain # df_chain的值没有改变
AB
000
110
2-10
df_chain.loc[df_chain.A!=0,'B'] = 1 # df_chain的值改变了
df_chain 
AB
000
111
2-11

iloc索引器

iloc 的使用与 loc 完全类似,只不过是针对位置进行筛选,在相应的 * 位置处一共也有五类合法对象,分别是:整数、整数列表、整数切片、布尔列表以及函数,函数的返回值必须是前面的四类合法对象中的一个,其输入同样也为 DataFrame 本身。

 df_demo.iloc[1, 1] # 第二行第二列
'Freshman'
df_demo.iloc[[0, 1], [0, 1]] # 前两行前两列
SchoolGrade
Name
Gaopeng YangShanghai Jiao Tong UniversityFreshman
Changqiang YouPeking UniversityFreshman
 df_demo.iloc[1: 4, 2:4] # 切片不包含结束端点
GenderWeight
Name
Changqiang YouMale70.0
Mei SunMale89.0
Xiaojuan SunFemale41.0
 df_demo.iloc[lambda x: slice(1, 4)] # 传入切片为返回值的函数(同样不包含结束端点)
SchoolGradeGenderWeightTransfer
Name
Changqiang YouPeking UniversityFreshmanMale70.0N
Mei SunShanghai Jiao Tong UniversitySeniorMale89.0N
Xiaojuan SunFudan UniversitySophomoreFemale41.0N

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

例如,选出体重超过 80kg 的学生:

df_demo.iloc[(df_demo.Weight>80).values].head() # 如果不加.values会报错
SchoolGradeGenderWeightTransfer
Name
Mei SunShanghai Jiao Tong UniversitySeniorMale89.0N
Qiang ZhengShanghai Jiao Tong UniversitySeniorMale87.0N
Qiang HanPeking UniversityFreshmanMale87.0N
Chengpeng ZhouFudan UniversitySeniorMale81.0N
Feng HanShanghai Jiao Tong UniversitySophomoreMale82.0N

对 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

query 方法

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

例如,将 loc 一节中的复合条件查询例子可以如下改写:

df.query('((School == "Fudan University")&'
         ' (Grade == "Senior")&'
         ' (Weight > 70))|'
         '((School == "Peking University")&'
         ' (Grade != "Senior")&'
         ' (Weight > 80))') 
SchoolGradeNameGenderWeightTransfer
38Peking UniversityFreshmanQiang HanMale87.0N
66Fudan UniversitySeniorChengpeng ZhouMale81.0N
99Peking UniversityFreshmanChangpeng ZhaoMale83.0N
131Fudan UniversitySeniorChengpeng QianMale73.0Y

在 query 表达式中,系统为用户注册了所有来自 DataFrame 的列名,所有属于该 Series 的方法都可以被调用,和正常的函数调用并没有区别,例如查询体重超过均值的学生:

df.query('Weight > Weight.mean()').head()
SchoolGradeNameGenderWeightTransfer
1Peking UniversityFreshmanChangqiang YouMale70.0N
2Shanghai Jiao Tong UniversitySeniorMei SunMale89.0N
4Fudan UniversitySophomoreGaojuan YouMale74.0N
10Shanghai Jiao Tong UniversityFreshmanXiaopeng ZhouMale74.0N
14Tsinghua UniversitySeniorXiaomei ZhouFemale57.0N

**Remark:**对于含有空格的列名,需要使用 col name 的方式进行引用

同时,在 query 中还注册了若干英语的字面用法,帮助提高可读性,例如: or, and, or, is in, not in。例如,筛选出男生中不是大一大二的学生:

df.query('(Grade not in ["Freshman", "Sophomore"]) and'
         '(Gender == "Male")').head()
SchoolGradeNameGenderWeightTransfer
2Shanghai Jiao Tong UniversitySeniorMei SunMale89.0N
16Tsinghua UniversityJuniorXiaoqiang QinMale68.0N
17Tsinghua UniversityJuniorPeng WangMale65.0N
18Tsinghua UniversitySeniorXiaofeng SunMale71.0N
21Shanghai Jiao Tong UniversitySeniorXiaopeng ShenMale62.0NaN

此外,在字符串中出现与列表的比较时, == 和 != 分别表示元素出现在列表和没有出现在列表,等价于 is in 和 not in,例如查询所有大三和大四的学生:

df.query('Grade == ["Junior", "Senior"]').head()
SchoolGradeNameGenderWeightTransfer
2Shanghai Jiao Tong UniversitySeniorMei SunMale89.0N
7Tsinghua UniversityJuniorGaoqiang QianFemale50.0N
9Peking UniversityJuniorJuan XuFemaleNaNN
11Tsinghua UniversityJuniorXiaoquan LvFemale43.0N
12Shanghai Jiao Tong UniversitySeniorPeng YouFemale48.0NaN

对于 query 中的字符串,如果要引用外部变量,只需在变量名前加 @ 符号。例如,取出体重位于70kg到80kg之间的学生:

low, high =70, 80

df.query('Weight.between(@low, @high)').head()
SchoolGradeNameGenderWeightTransfer
1Peking UniversityFreshmanChangqiang YouMale70.0N
4Fudan UniversitySophomoreGaojuan YouMale74.0N
10Shanghai Jiao Tong UniversityFreshmanXiaopeng ZhouMale74.0N
18Tsinghua UniversitySeniorXiaofeng SunMale71.0N
35Peking UniversityFreshmanGaoli ZhaoMale78.0N

随机抽样

如果把 DataFrame 的每一行看作一个样本,或把每一列看作一个特征,再把整个 DataFrame 看作总体,想要对样本或特征进行随机抽样就可以用 sample 函数。有时在拿到大型数据集后,想要对统计特征进行计算来了解数据的大致分布,但是这很费时间。同时,由于许多统计特征在等概率不放回的简单随机抽样条件下,是总体统计特征的无偏估计,比如样本均值和总体均值,那么就可以先从整张表中抽出一部分来做近似估计。

sample 函数中的主要参数为 n, axis, frac, replace, weights,前三个分别是指抽样数量、抽样的方向(0为行、1为列)和抽样比例(0.3则为从总体中抽出30%的样本)。replace 和 weights 分别是指是否放回和每个样本的抽样相对概率,当 replace = True 则表示有放回抽样。

例如,对下面构造的 df_sample 以 value 值的相对大小为抽样概率进行有放回抽样,抽样数量为3。

df_sample = pd.DataFrame({'id': list('abcde'),'value': [1, 2, 3, 4, 90]})
df_sample 
idvalue
0a1
1b2
2c3
3d4
4e90
df_sample.sample(3, replace = True, weights = df_sample.value) 
idvalue
2c3
4e90
4e90

多级索引

多级索引及其表的结构

np.random.seed(0)
multi_index = pd.MultiIndex.from_product([list('ABCD'),df.Gender.unique()], names=('School', 'Gender'))
multi_column = pd.MultiIndex.from_product([['Height', 'Weight'],df.Grade.unique()], names=('Indicator', 'Grade'))
df_multi = pd.DataFrame(np.c_[(np.random.randn(8,4)*5 + 163).tolist(),(np.random.randn(8,4)*5 + 65).tolist()],
                               index = multi_index,columns = multi_column).round(1) 

df_multi 
IndicatorHeightWeight
GradeFreshmanSeniorSophomoreJuniorFreshmanSeniorSophomoreJunior
SchoolGender
AFemale171.8165.0167.9174.260.655.163.365.8
Male172.3158.1167.8162.271.271.063.163.5
BFemale162.5165.1163.7170.359.857.956.574.8
Male166.8163.6165.2164.762.562.858.768.9
CFemale170.5162.0164.6158.756.963.960.566.9
Male150.2166.3167.3159.362.459.164.967.1
DFemale174.3155.7163.2162.165.366.561.863.2
Male170.7170.3163.8164.961.663.260.956.4

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

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

索引的名字和值属性分别可以通过 names 和 values 获得:

print('df_multi.index.names:')
print(df_multi.index.names)
print('\n')
print('df_multi.columns.names:')
print(df_multi.columns.names)
print('\n')
print('df_multi.index.values:')
print(df_multi.index.values)
print('\n')
print('df_multi.columns.values:') 
print(df_multi.columns.values)  
df_multi.index.names:
['School', 'Gender']


df_multi.columns.names:
['Indicator', 'Grade']


df_multi.index.values:
[('A', 'Female') ('A', 'Male') ('B', 'Female') ('B', 'Male')
 ('C', 'Female') ('C', 'Male') ('D', 'Female') ('D', 'Male')]


df_multi.columns.values:
[('Height', 'Freshman') ('Height', 'Senior') ('Height', 'Sophomore')
 ('Height', 'Junior') ('Weight', 'Freshman') ('Weight', 'Senior')
 ('Weight', 'Sophomore') ('Weight', 'Junior')]

如果想要得到某一层的索引,则需要通过 get_level_values 获得:

df_multi.index.get_level_values(0) 
Index(['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D'], dtype='object', name='School')
df_multi.columns.get_level_values(1)  
Index(['Freshman', 'Senior', 'Sophomore', 'Junior', 'Freshman', 'Senior',
       'Sophomore', 'Junior'],
      dtype='object', name='Grade')

但对于索引而言,无论是单层还是多层,用户都无法通过 index_obj[0] = item 的方式来修改元素,也不能通过 index_name[0] = new_name 的方式来修改名字,关于如何修改这些属性的话题将在第三节进行讨论。

多级索引中的loc索引器

熟悉了结构后,现在回到原表,将学校和年级设为索引,此时的行为多级索引,列为单级索引,由于默认状态的列索引不含名字,因此对应于刚刚图中 Indicator 和 Grade 的索引名位置是空缺的。

df_multi = df.set_index(['School', 'Grade']) 
df_multi.head() 
NameGenderWeightTransfer
SchoolGrade
Shanghai Jiao Tong UniversityFreshmanGaopeng YangFemale46.0N
Peking UniversityFreshmanChangqiang YouMale70.0N
Shanghai Jiao Tong UniversitySeniorMei SunMale89.0N
Fudan UniversitySophomoreXiaojuan SunFemale41.0N
SophomoreGaojuan YouMale74.0N

由于多级索引中的单个元素以元组为单位,因此之前在第一节介绍的 loc 和 iloc 方法完全可以照搬,只需把标量的位置替换成对应的元组,不过在索引前最好对 MultiIndex 进行排序以避免性能警告:

df_multi = df_multi.sort_index()
df_multi.loc[('Fudan University', 'Junior')].head() 
NameGenderWeightTransfer
SchoolGrade
Fudan UniversityJuniorYanli YouFemale48.0N
JuniorChunqiang ChuMale72.0N
JuniorChangfeng LvMale76.0N
JuniorYanjuan LvFemale49.0NaN
JuniorGaoqiang ZhouFemale43.0N
df_multi.loc[[('Fudan University', 'Senior'),('Shanghai Jiao Tong University', 'Freshman')]].head() # 传入一个列表
NameGenderWeightTransfer
SchoolGrade
Fudan UniversitySeniorChengpeng ZhengFemale38.0N
SeniorFeng ZhouFemale47.0N
SeniorGaomei LvFemale34.0N
SeniorChunli LvFemale56.0N
SeniorChengpeng ZhouMale81.0N
df_multi.loc[df_multi.Weight > 70].head() # 布尔列表也是可用的 
NameGenderWeightTransfer
SchoolGrade
Fudan UniversityFreshmanFeng WangMale74.0N
JuniorChunqiang ChuMale72.0N
JuniorChangfeng LvMale76.0N
SeniorChengpeng ZhouMale81.0N
SeniorChengpeng QianMale73.0Y
df_multi.loc[lambda x:('Fudan University','Junior')].head() # 匿名函数
NameGenderWeightTransfer
SchoolGrade
Fudan UniversityJuniorYanli YouFemale48.0N
JuniorChunqiang ChuMale72.0N
JuniorChangfeng LvMale76.0N
JuniorYanjuan LvFemale49.0NaN
JuniorGaoqiang ZhouFemale43.0N

Question: 与单层索引类似,若存在重复元素,则不能使用切片,请去除重复索引后给出一个元素切片的例子。

df_multi_uni = df.drop_duplicates(['School','Grade']).set_index(['School', 'Grade'])  
df_multi_uni = df_multi_uni.sort_index()
df_multi_uni.loc[('Shanghai Jiao Tong University','Freshman'):('Tsinghua University','Junior')]  
NameGenderWeightTransfer
SchoolGrade
Shanghai Jiao Tong UniversityFreshmanGaopeng YangFemale46.0N
JuniorFeng ZhengFemale51.0N
SeniorMei SunMale89.0N
SophomoreYanfeng QianFemale48.0N
Tsinghua UniversityFreshmanXiaoli QianFemale51.0N
JuniorGaoqiang QianFemale50.0N

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

例如,想要得到所有北大和复旦的大二大三学生,可以如下写出:

res = df_multi.loc[(['Peking University', 'Fudan University'],['Sophomore', 'Junior']), :]
res.head() 
NameGenderWeightTransfer
SchoolGrade
Peking UniversitySophomoreChangmei XuFemale43.0N
SophomoreXiaopeng QinMaleNaNN
SophomoreMei XuFemale39.0N
SophomoreXiaoli ZhouFemale55.0N
SophomorePeng HanFemale34.0NaN
res.shape
(33, 4)

下面的语句和上面类似,但仍然传入的是元素(这里为元组)的列表,它们的意义是不同的,表示的是选出北大的大三学生和复旦的大二学生:

res = df_multi.loc[[('Peking University', 'Junior'),('Fudan University', 'Sophomore')]]
res.head() 
NameGenderWeightTransfer
SchoolGrade
Peking UniversityJuniorJuan XuFemaleNaNN
JuniorChangjuan YouFemale47.0N
JuniorGaoli XuFemale48.0N
JuniorGaoquan ZhouMale70.0N
JuniorQiang YouFemale56.0N
res.shape 
(16, 4)

IndexSlice对象

前面介绍的方法,即使在索引不重复的时候,也只能对元组整体进行切片,而不能对每层进行切片,也不允许将切片和布尔列表混合使用,引入 IndexSlice 对象就能解决这个问题。 Slice 对象一共有两种形式,第一种为 loc[idx[*,*]] 型,第二种为 loc[idx[*,*],idx[*,*]] 型,下面将进行介绍。为了方便演示,下面构造一个 索引不重复的 DataFrame :

np.random.seed(0)
L1,L2 = ['A','B','C'],['a','b','c']
mul_index1 = pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))
L3,L4 = ['D','E','F'],['d','e','f']
mul_index2 = pd.MultiIndex.from_product([L3,L4],names=('Big', 'Small'))
df_ex = pd.DataFrame(np.random.randint(-9,10,(9,9)),index=mul_index1,columns=mul_index2)

df_ex 
BigDEF
Smalldefdefdef
UpperLower
Aa36-9-6-6-209-5
b-33-8-3-258-44
c-107-466-99-6
Ba85-2-9-80-91-6
b29-7-9-9-5-4-3-1
c86-501-8-8-20
Ca-6-3259-95-63
b12-5-3-56-63-5
c-156-66478-4

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

idx = pd.IndexSlice 

loc[idx[*,*]] 型

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

idx['C':, ('D', 'f'):]
(slice('C', None, None), slice(('D', 'f'), None, None))
df_ex.loc[idx['C':, ('D', 'f'):]] 
BigDEF
Smallfdefdef
UpperLower
Ca259-95-63
b-5-3-56-63-5
c6-66478-4

另外,也支持布尔序列的索引:

df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0
BigDF
Smalldee
UpperLower
Aa369
b-33-4
c-109

loc[idx[*,*],idx[*,*]] 型

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

df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]] 
BigEF
Smallefef
UpperLower
Ab-25-44
c669-6

多级索引的构造

前面提到了多级索引表的结构和切片,那么除了使用 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'])

索引的常用方法

索引层的交换和删除

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
BigCD
Smallcdcd
Othercatdogcatdogcatdogcatdog
UpperLowerExtra
Aaalpha36-9-6-6-209
beta-5-33-8-3-258
balpha-44-107-466
beta-99-685-2-9-8
Baalpha0-91-629-7-9
beta-9-5-4-3-186-5
balpha01-8-8-20-6-3
beta259-95-631

索引层的交换由 swaplevelreorder_levels 完成,前者只能交换两个层,而后者可以交换任意层,两者都可以指定交换的是轴是哪一个,即行索引或列索引:

df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一层和第三层交换
Othercatdogcatdogcatdogcatdog
Smallccddccdd
BigCCCCDDDD
UpperLowerExtra
Aaalpha36-9-6-6-209
beta-5-33-8-3-258
balpha-44-107-466
beta-99-685-2-9-8
Baalpha0-91-629-7-9
df_ex.reorder_levels([2,0,1],axis=0).head() # 列表数字指代原来索引中的层
BigCD
Smallcdcd
Othercatdogcatdogcatdogcatdog
ExtraUpperLower
alphaAa36-9-6-6-209
betaAa-5-33-8-3-258
alphaAb-44-107-466
betaAb-99-685-2-9-8
alphaBa0-91-629-7-9

若想要删除某一层的索引,可以使用 droplevel 方法:

df_ex.droplevel(1,axis=1) 
BigCD
Othercatdogcatdogcatdogcatdog
UpperLowerExtra
Aaalpha36-9-6-6-209
beta-5-33-8-3-258
balpha-44-107-466
beta-99-685-2-9-8
Baalpha0-91-629-7-9
beta-9-5-4-3-186-5
balpha01-8-8-20-6-3
beta259-95-631
df_ex.droplevel([0,1],axis=0) 
BigCD
Smallcdcd
Othercatdogcatdogcatdogcatdog
Extra
alpha36-9-6-6-209
beta-5-33-8-3-258
alpha-44-107-466
beta-99-685-2-9-8
alpha0-91-629-7-9
beta-9-5-4-3-186-5
alpha01-8-8-20-6-3
beta259-95-631

索引属性的修改

通过 rename_axis 可以对索引层的名字进行修改,常用的修改方式是传入字典的映射

df_ex.rename_axis(index={'Upper':'Changed_row'},columns={'Other':'Changed_Col'}).head()
BigCD
Smallcdcd
Changed_Colcatdogcatdogcatdogcatdog
Changed_rowLowerExtra
Aaalpha36-9-6-6-209
beta-5-33-8-3-258
balpha-44-107-466
beta-99-685-2-9-8
Baalpha0-91-629-7-9

通过 rename 可以对索引的值进行修改,如果是多级索引需要指定修改的层号 level

df_ex.rename(columns={'cat':'not_cat'},level=2).head() 
BigCD
Smallcdcd
Othernot_catdognot_catdognot_catdognot_catdog
UpperLowerExtra
Aaalpha36-9-6-6-209
beta-5-33-8-3-258
balpha-44-107-466
beta-99-685-2-9-8
Baalpha0-91-629-7-9

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

df_ex.rename(index=lambda x:str.upper(x),level=2).head() 
BigCD
Smallcdcd
Othercatdogcatdogcatdogcatdog
UpperLowerExtra
AaALPHA36-9-6-6-209
BETA-5-33-8-3-258
bALPHA-44-107-466
BETA-99-685-2-9-8
BaALPHA0-91-629-7-9

**Question:**尝试在 rename_axis 中使用函数完成与例子中一样的功能。

df_ex.rename_axis(index=lambda x:'Changed_Row' if x=='Upper' else x,
                  columns=lambda x:'Changed_Col' if x=='Other' else x ).head() 
BigCD
Smallcdcd
Changed_Colcatdogcatdogcatdogcatdog
Changed_RowLowerExtra
Aaalpha36-9-6-6-209
beta-5-33-8-3-258
balpha-44-107-466
beta-99-685-2-9-8
Baalpha0-91-629-7-9

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

new_values = iter(list('abcdefgh'))
df_ex.rename(index=lambda x:next(new_values),level=2) 
BigCD
Smallcdcd
Othercatdogcatdogcatdogcatdog
UpperLowerExtra
Aaa36-9-6-6-209
b-5-33-8-3-258
bc-44-107-466
d-99-685-2-9-8
Bae0-91-629-7-9
f-9-5-4-3-186-5
bg01-8-8-20-6-3
h259-95-631

若想要对某个位置的元素进行修改,在单层索引时容易实现,即先取出索引的 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() 
BigCD
Smallcdcd
Othercatdogcatdogcatdogcatdog
UpperLowerExtra
AaALPHA36-9-6-6-209
BETA-5-33-8-3-258
bALPHA-44-107-466
BETA-99-685-2-9-8
BaALPHA0-91-629-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() # 单层索引 
BigCD
Smallcdcd
Othercatdogcatdogcatdogcatdog
A-a-alpha36-9-6-6-209
A-a-beta-5-33-8-3-258
A-b-alpha-44-107-466
A-b-beta-99-685-2-9-8
B-a-alpha0-91-629-7-9

同时,也可以反向地展开:

new_idx = df_temp.index.map(lambda x:tuple(x.split('-')))
df_temp.index = new_idx
df_temp.head() # 三层索引
BigCD
Smallcdcd
Othercatdogcatdogcatdogcatdog
Aaalpha36-9-6-6-209
beta-5-33-8-3-258
balpha-44-107-466
beta-99-685-2-9-8
Baalpha0-91-629-7-9

索引的设置与重置

为了说明本节的函数,下面构造一个新表:

df_new = pd.DataFrame({'A':list('aacd'),'B':list('PQRT'),'C':[1,2,3,4]}) 
df_new 
ABC
0aP1
1aQ2
2cR3
3dT4

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

df_new.set_index('A') 
BC
A
aP1
aQ2
cR3
dT4
df_new.set_index('A', append=True)
BC
A
0aP1
1aQ2
2cR3
3dT4

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

df_new.set_index(['A', 'B']) 
C
AB
aP1
Q2
cR3
dT4

如果想要添加索引的列没有出现再其中,那么可以直接在参数中传入相应的 Series :

my_index = pd.Series(list('WXYZ'), name='D')
df_new = df_new.set_index(['A', my_index])
df_new 
BC
AD
aWP1
XQ2
cYR3
dZT4

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

df_new.reset_index(['D']) 
DBC
A
aWP1
aXQ2
cYR3
dZT4
df_new.reset_index(['D'], drop=True) 
BC
A
aP1
aQ2
cR3
dT4

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

df_new.reset_index() 
ADBC
0aWP1
1aXQ2
2cYR3
3dZT4

索引的变形

在某些场合下,需要对索引做一些扩充或者剔除,更具体地要求是给定一个新的索引,把原表中相应的索引对应元素填充到新索引构成的表中

例如,下面的表中给出了员工信息,需要重新制作一张新的表,要求增加一名员工的同时去掉身高列并增加性别列:

df_reindex = pd.DataFrame({"Weight":[60,70,80],"Height":[176,180,179]},index=['1001','1003','1002'])
df_reindex 
WeightHeight
100160176
100370180
100280179
df_reindex.reindex(index=['1001','1002','1003','1004'],columns=['Weight','Gender']) 
WeightGender
100160.0NaN
100280.0NaN
100370.0NaN
1004NaNNaN

这种需求常出现在时间序列索引的时间点填充以及 ID 编号的扩充。另外,需要注意的是原来表中的数据和新表中会根据索引自动对其,例如原先的1002号位置在1003号之后,而新表中相反,那么 reindex 中会根据元素对其,与位置无关。

还有一个与 reindex 功能类似的函数是 reindex_like ,其功能是仿照传入的表的索引来进行被调用表索引的变形。例如,现在以及存在一张表具备了目标索引的条件,那么上述功能可以如下等价地写出:

df_existed = pd.DataFrame(index=['1001','1002','1003','1004'],columns=['Weight','Gender'])

df_reindex.reindex_like(df_existed)
WeightGender
100160.0NaN
100280.0NaN
100370.0NaN
1004NaNNaN

索引运算

集合的运算法则

经常会有一种利用集合运算来取出符合条件行的需求,例如有两张表 A 和 B ,它们的索引都是员工编号,现在需要筛选出两表索引交集的所有员工信息,此时通过 Index 上的运算操作就很容易实现。

S A . i n t e r s e c t i o n ( S B ) = S A ∩ S B ⇔ { x ∣ x ∈ S A   a n d   x ∈ S B } \rm S_A.intersection(S_B) = \rm S_A \cap S_B \Leftrightarrow \rm \{x|x\in S_A\, and\, x\in S_B\} SA.intersection(SB)=SASB{xxSAandxSB}
S A . u n i o n ( S B ) = S A ∪ S B ⇔ { x ∣ x ∈ S A   o r   x ∈ S B } \rm S_A.union(S_B) = \rm S_A \cup S_B \Leftrightarrow \rm \{x|x\in S_A\, or\, x\in S_B\} SA.union(SB)=SASB{xxSAorxSB}
S A . d i f f e r e n c e ( S B ) = S A − S B ⇔ { x ∣ x ∈ S A   a n d   x ∉ S B } \rm S_A.difference(S_B) = \rm S_A - S_B \Leftrightarrow \rm \{x|x\in S_A\, and\, x\notin S_B\} SA.difference(SB)=SASB{xxSAandx/SB}
S A . s y m m e t r i c _ d i f f e r e n c e ( S B ) = S A △ S B ⇔ { x ∣ x ∈ S A ∪ S B − S A ∩ S B } \rm S_A.symmetric\_difference(S_B) = \rm S_A\triangle S_B\Leftrightarrow \rm \{x|x\in S_A\cup S_B - S_A\cap S_B\} SA.symmetric_difference(SB)=SASB{xxSASBSASB}

一般的索引运算

由于集合的元素是互异的,但是索引中可能有相同的元素,先用 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() 
print(df_set_1)
print(df_set_2)
print(id1)
print(id2) 
     0  1
id1      
a    0  1
b    1  2
a    3  4
     0  1
id2      
b    4  5
b    2  6
c    7  1
Index(['a', 'b'], dtype='object', name='id1')
Index(['b', 'c'], dtype='object', name='id2')
print('id1.intersection(id2):')
print(id1.intersection(id2))
print('\n')
print('id1.union(id2)')
print(id1.union(id2))
print('\n')
print('id1.difference(id2):')
print(id1.difference(id2))
print('\n')
print('id1.symmetric_difference(id2):')
print(id1.symmetric_difference(id2)) 
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')

上述的四类运算还可以用等价的符号表示代替如下:

print('id1 & id2:')
print(id1 & id2)
print('\n')
print('id1 | id2:')
print(id1 | id2)
print('\n')
print('(id1 ^ id2) & id1:')
print((id1 ^ id2) & id1)
print('\n')
print('id1 ^ id2:')
print(id1 ^ id2) 
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 
id101
0a01
1b12
2a34
df_set_in_col_2 
id201
0b45
1b26
2c71
df_set_in_col_1[df_set_in_col_1.id1.isin(df_set_in_col_2.id2)]
id101
1b12
df_set_in_col_1.id1.isin(df_set_in_col_2.id2) 
0    False
1     True
2    False
Name: id1, dtype: bool

练习

公司员工数据集

现有一份公司员工数据集:

df = pd.read_csv('G:\代码\joyful-pandas-master\data\company.csv')
df.head() 
EmployeeIDbirthdate_keyagecity_namedepartmentjob_titlegender
013181/3/195461VancouverExecutiveCEOM
113191/3/195758VancouverExecutiveVP StoresF
213201/2/195560VancouverExecutiveLegal CounselF
313211/2/195956VancouverExecutiveVP Human ResourcesM
413221/9/195857VancouverExecutiveVP FinanceM
  1. 分别只使用 query 和 loc 选出年龄不超过四十岁且工作部门为 Dairy 或 Bakery 的男性。
# 使用 query 
df.query('(age<=40)&' '((department=="Dairy")|(department=="Bakery"))').head()  
EmployeeIDbirthdate_keyagecity_namedepartmentjob_titlegender
360857881/12/197540ChilliwackDairyDairy PersonF
360957891/13/197540ChilliwackDairyDairy PersonF
361057901/13/197540KelownaDairyDairy PersonF
361157911/14/197540KelownaDairyDairy PersonM
361357931/22/197540RichmondBakeryBakerM
# 使用 loc 
con1 = df.age <= 40 
con2_1 = df.department == 'Dairy'
con2_2 = df.department == 'Bakery'
con2 = con2_1 | con2_2
df.loc[con1 & con2].head() 
EmployeeIDbirthdate_keyagecity_namedepartmentjob_titlegender
360857881/12/197540ChilliwackDairyDairy PersonF
360957891/13/197540ChilliwackDairyDairy PersonF
361057901/13/197540KelownaDairyDairy PersonF
361157911/14/197540KelownaDairyDairy PersonM
361357931/22/197540RichmondBakeryBakerM

选出员工 ID 号 为奇数所在行的第1、第3和倒数第2列。

df[['EmployeeID','age','job_title']].loc[df.EmployeeID%2!=0].head() 
EmployeeIDagejob_title
1131958VP Stores
3132156VP Human Resources
5132353Exec Assistant, VP Stores
6132551Exec Assistant, Legal Counsel
8132948Store Manager
  1. 按照以下步骤进行索引操作:

    • 把后三列设为索引后交换内外两层

    • 恢复中间一层

    • 修改外层索引名为 Gender

    • 用下划线合并两层行索引

    • 把行索引拆分为原状态

    • 修改索引名为原表名称

    • 恢复默认索引并将列保持为原表的相对位置

# 把后三列设为索引后交换内外两层
df_copy = df.copy()
df_copy = df_copy.set_index(['department','job_title','gender']).swaplevel(0,2,axis=0)
df_copy.head() 
EmployeeIDbirthdate_keyagecity_name
genderjob_titledepartment
MCEOExecutive13181/3/195461Vancouver
FVP StoresExecutive13191/3/195758Vancouver
Legal CounselExecutive13201/2/195560Vancouver
MVP Human ResourcesExecutive13211/2/195956Vancouver
VP FinanceExecutive13221/9/195857Vancouver
# 恢复中间一层
df_copy = df_copy.reset_index(level=1)
df_copy.head() 
job_titleEmployeeIDbirthdate_keyagecity_name
genderdepartment
MExecutiveCEO13181/3/195461Vancouver
FExecutiveVP Stores13191/3/195758Vancouver
ExecutiveLegal Counsel13201/2/195560Vancouver
MExecutiveVP Human Resources13211/2/195956Vancouver
ExecutiveVP Finance13221/9/195857Vancouver
# 修改外层索引名为 Gender
df_copy = df_copy.rename_axis(index={'gender':'Gender'})
df_copy.head()
job_titleEmployeeIDbirthdate_keyagecity_name
Genderdepartment
MExecutiveCEO13181/3/195461Vancouver
FExecutiveVP Stores13191/3/195758Vancouver
ExecutiveLegal Counsel13201/2/195560Vancouver
MExecutiveVP Human Resources13211/2/195956Vancouver
ExecutiveVP Finance13221/9/195857Vancouver
# 用下划线合并两层行索引
new_idx = df_copy.index.map(lambda x:(x[0]+'_'+x[1]))
df_copy.index = new_idx
df_copy.head() 
job_titleEmployeeIDbirthdate_keyagecity_name
M_ExecutiveCEO13181/3/195461Vancouver
F_ExecutiveVP Stores13191/3/195758Vancouver
F_ExecutiveLegal Counsel13201/2/195560Vancouver
M_ExecutiveVP Human Resources13211/2/195956Vancouver
M_ExecutiveVP Finance13221/9/195857Vancouver
# 把行索引拆分为原状态
new_idx = df_copy.index.map(lambda x:tuple(x.split('_')))
df_copy.index = new_idx
df_copy.head() 
job_titleEmployeeIDbirthdate_keyagecity_name
MExecutiveCEO13181/3/195461Vancouver
FExecutiveVP Stores13191/3/195758Vancouver
ExecutiveLegal Counsel13201/2/195560Vancouver
MExecutiveVP Human Resources13211/2/195956Vancouver
ExecutiveVP Finance13221/9/195857Vancouver
# 修改索引名为原表名称
df_copy = df_copy.rename_axis(index=['gender', 'department'])
df_copy.head() 
job_titleEmployeeIDbirthdate_keyagecity_name
genderdepartment
MExecutiveCEO13181/3/195461Vancouver
FExecutiveVP Stores13191/3/195758Vancouver
ExecutiveLegal Counsel13201/2/195560Vancouver
MExecutiveVP Human Resources13211/2/195956Vancouver
ExecutiveVP Finance13221/9/195857Vancouver
# 恢复默认索引并将列保持为原表的相对位置
df_copy = df_copy.reset_index().reindex(df.columns, axis=1)
df_copy.equals(df)   
True

巧克力数据集

df = pd.read_csv('G:\代码\joyful-pandas-master\data\chocolate.csv')
df.head() 
CompanyReview\nDateCocoa\nPercentCompany\nLocationRating
0A. Morin201663%France3.75
1A. Morin201570%France2.75
2A. Morin201570%France3.00
3A. Morin201570%France3.50
4A. Morin201570%France3.50
  1. 把列索引名中的 \n 替换为空格
df_copy = df.copy() 
df_copy.columns = df_copy.columns .map(lambda x:x.replace('\n',' ')) 
df_copy.head() 
CompanyReview DateCocoa PercentCompany LocationRating
0A. Morin201663%France3.75
1A. Morin201570%France2.75
2A. Morin201570%France3.00
3A. Morin201570%France3.50
4A. Morin201570%France3.50
  1. 巧克力 Rating 评分为1至5,每0.25分一档,请选出2.75分及以下且可可含量 Cocoa Percent 高于中位数的样本。
# 方法一
con1 = df_copy['Rating'] < 2.75 
con2 = df_copy['Cocoa Percent'].map(lambda x:float(x.replace('%',''))) > df_copy['Cocoa Percent'].map(lambda x:float(x.replace('%',''))).median()
df_copy[con1 & con2].head() 
CompanyReview DateCocoa PercentCompany LocationRating
38Alain Ducasse201375%France2.50
39Alain Ducasse201375%France2.50
96Ara201472%France2.50
125Artisan du Chocolat2010100%U.K.1.75
130Artisan du Chocolat200975%U.K.2.50
# 方法二
df_copy['Cocoa Percent'] = df_copy['Cocoa Percent'].apply(lambda x:float(x[:-1])/100)
df_copy.query('(Rating<2.75)&(`Cocoa Percent`>`Cocoa Percent`.median())').head() 
CompanyReview DateCocoa PercentCompany LocationRating
38Alain Ducasse20130.75France2.50
39Alain Ducasse20130.75France2.50
96Ara20140.72France2.50
125Artisan du Chocolat20101.00U.K.1.75
130Artisan du Chocolat20090.75U.K.2.50
  1. 将 Review Date 和 Company Location 设为索引后,选出 Review Date 在2012年之后且 Company Location 不属于 France, Canada, Amsterdam, Belgium 的样本。
idx = pd.IndexSlice
df_copy = df_copy.set_index(['Review Date', 'Company Location']).sort_index(level=0)
df_copy.loc[idx[2012:,~df_copy.index.get_level_values(1).isin(['France', 'Canada', 'Amsterdam', 'Belgium'])],:].head()
CompanyCocoa PercentRating
Review DateCompany Location
2012AustraliaBahen & Co.0.703.00
AustraliaBahen & Co.0.702.50
AustraliaBahen & Co.0.702.50
AustraliaCravve0.753.25
AustraliaCravve0.653.25
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值