一、索引器
对于DataFrame
对象最常见的索引方式为df['column name']
或者df[column list]
,前者返回的是一个Series
,后者是DataFrame
。Series
对象索引的方式大概也是类似的,不再赘述
Pandas
中还提供了一些特殊的函数和索引器来满足不同场景的需要
1.loc索引器
loc
索引器一般形式为loc[*,*]
,前一个是行的选择,后一个是列的选择,后一个*
的位置可以省略,A和B有五类合法对象:(后面为示例)
- 单个元素:
df.loc['index name']
- 元素列表:
df.loc[[index list], [column list]]
- 元素切片:
df.loc[:,:]
前一个为行切片,后一个为列切片 - 布尔列表以及函数:
df.loc[df.Weight > 70]
筛选体重大于70的数据所在行,需要注意的是,这里支持多个条件,使用逻辑运算符号即可
多级索引loc
的用法和单级索引类似,只需要把标量换成对应的元组即可,还有一点需要注意的是:多级索引切片需要对索引进行排序,否则会报错
练一练
select_dtypes
是一个实用函数,它能够从表中选出相应类型的列,若要选出所有数值型的列,只需使用.select_dtypes('number')
,请利用布尔列表选择的方法结合DataFrame
的dtypes
属性在learn_pandas
数据集上实现这个功能。
df_demo = df.set_index('Name')
df_demo.loc[:,df_demo.dtypes==object].head()
2.iloc索引器
iloc
和loc
索引器类似,不同的是,iloc
是针对位置进行筛选,相应的,也有五类合法对象 ,分别是:
- 整数:
df.iloc[1,1]
第二行第二列 - 整数列表:
df.iloc[[0,1],[0,1]]
前两行前两列 - 整数切片:
df.iloc[[0:2,0:2]]
前两行前两列(左闭右开区间) - 函数:
df.iloc[func]
:根据func逻辑执行 - 布尔列表:
df.iloc[(df.Weight>80).values]
:体重大于80的行,需要注意的是,这里不能传入Series
,需要传入values
,因为values
其实相当于一个列表
3.IndexSlice对象
IndexSlice
对象针对的是多级索引使用,作用是在索引重复的情况下也可以针对每层进行切片,并且可以将元组和布尔列表混合使用。
IndexSlice
对象有两种使用形式,第一种为 loc[idx[*,*]]
,第二种为 loc[idx[*,*],idx[*,*]]
,具体用法如下:
idx = pd.IndexSlice
## loc[idx[*,*]]型,前一个表示行的选择,后一个表示列的选择,和传统的loc是一样的
df_ex.loc[idx['C':, ('D', 'f'):]] # :表示全选
df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0
## loc[idx[*,*],idx[*,*]] 型,前一个 idx 指代的是行索引,后一个是列索引
df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
上述的idx
语法可以类比列表的切片,不过需要说明的是,这是闭区间
二、索引常用方法
1.query方法
query
方法还是挺有意思的,由用户将需要的数据转换成逻辑运算的形式,和SQL
很相似,直接给出一个例子
## 筛选出姓名为a,体重小于70的数据所在行
df.query('(name == 'a') & (weight < 70))')
并且在其中还可以调用Series
中的方法
## 筛选出体重大于均值的行
df.query('Weight > Weight.mean()').head()
除此之外,pandas
还提供了一些关键字代替逻辑运算符:or
、and
、is in
、not in
## 查找所有大三和大四的学生
df.query('Grade is in ["Junior", "Senior"]')
如果需要引用外部变量,需要在变量名前加@
符号
## 找到体重位于low和high之间的数据行
low, high =70, 80
df.query('Weight.between(@low, @high)')
2.随机抽样
对样本或特征进行随机抽样就可以用 sample
函数,sample
函数中的主要参数为 n, axis, frac, replace, weights
,前三个分别是指抽样数量、抽样的方向(0为行、1为列)和抽样比例(0.3则为从总体中抽出30%的样本)。
3.索引的交换和删除
交换由swaplevel
和reorder_levels
完成,前者只能交换两个层,后者可以交换任意层,两个函数都可以指定axis
参数,代表操作的是行还是列,0
为行,1
为列
df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一层和第三层交换
df_ex.reorder_levels([2,0,1],axis=0).head() # 列表数字指代原来索引中的层
删除某一层的索引,可以使用droplevel
方法,该方法需要指定删除的层级,并且可以通过axis
参数指定操作的是行索引还是列索引
df_ex.droplevel(1,axis=1)
4.索引属性的修改
通过 rename_axis
可以对索引层的名字进行修改,常用的修改方式是传入字典的映射:
df_ex.rename_axis(index={'Upper':'Changed_row'},columns={'Other':'Changed_Col'}).head()
通过 rename
可以对索引的值进行修改,如果是多级索引需要指定修改的层号 level
,同时该函数也能接受函数作为参数,例子如下:
df_ex.rename(columns={'cat':'not_cat'},level=2).head()
df_ex.rename(index=lambda x:str.upper(x),level=2).head()
练一练
尝试在
rename_axis
中使用函数完成与例子中一样的功能。
df_multi_demo.rename_axis(index=lambda x:str.upper(x))
map
函数也可以实现索引属性的修改,参数为函数,例子如下:
## 实现某一属性名转大写
new_idx = df_temp.index.map(lambda x: (x[0],x[1],str.upper(x[2])))
4.索引的设置与重置
构造索引方式常见的有set_index[index column]
,set_index
可以构造单级索引和多级索引,通过reset_index
可以重置索引,该函数的drop=True/False
参数可以选择是否丢弃去掉的索引。
除此之外还有一些构造多级索引的方式:
pd.MultiIndex.from_tuples
:根据传入的由元组组成的列表进行构造pd.MultiIndex.from_arrays
:根据传入的列表构造- ``pd.MultiIndex.from_product`:根据给定的多个列表的笛卡尔积进行构造
例子如下:
my_tuple = [('a','cat'),('a','dog'),('b','cat'),('b','dog')]
my_array = [list('aabb'), ['cat', 'dog']*2]
my_list1 = ['a','b']
my_list2 = ['cat','dog']
pd.MultiIndex.from_product([my_list1,my_list2],names=['First','Second'])
5.索引的变形
reindex_like
与函数其类似的功能,实现是仿照传入表的索引来对索引变形
df_existed = pd.DataFrame(index=['1001','1002','1003','1004'],columns['Weight','Gender'])
df_reindex.reindex_like(df_existed)
三、索引运算
经常会有一种利用集合运算来取出符合条件行的需求,例如有两张表 A
和 B
,它们的索引都是员工编号,现在需要筛选出两表索引交集的所有员工信息,此时通过 Index
上的运算操作就很容易实现。
大概的运算方式为求交集
、求并集
、求差集
等,对应的关系如下(id1
和id2
分别代表):
id1.intersection(id2)
:id1
和id2
的交集id1.union(id2)
:id1
和id2
并集id1.different(id2)
:属于id1
不属于id2
id1.symmetric_difference(id2)
:id1
和id2
并集减去id1
和id2
的交集
上述运算还可以用符号代替,比如
&
,|
,^
若两张表需要做集合运算的列并没有被设置索引,一种办法是先转成索引,运算后再恢复,另一种方法是利用 isin
函数,例如在重置索引的第一张表中选出id列交集的所在行:
## id1和id2都为列名
df_set_in_col_1[df_set_in_col_1.id1.isin(df_set_in_col_2.id2)]
四、练习
Ex1:
1.分别只使用 query
和 loc
选出年龄不超过四十岁且工作部门为 Dairy
或 Bakery
的男性。
## 使用loc
df.loc[(df.age <= 40) & (df.department.isin(['Dairy','Bakery'])) & (df.gender=='M')]
## 使用query
df.query('(age <= 40) and (department==["Dairy", "Bakery"]) and (gender == "M")')
2.选出员工 ID
号 为奇数所在行的第1、第3和倒数第2列。
df_demo = df.loc[df.EmployeeID.values % 2 == 1]
df_demo.iloc[:,[1,3,-2]]
3.按照以下步骤进行索引操作:
- 把后三列设为索引后交换内外两层
- 恢复中间一层
- 修改外层索引名为
Gender
- 用下划线合并两层行索引
- 把行索引拆分为原状态
- 修改索引名为原表名称
- 恢复默认索引并将列保持为原表的相对位置
第一问:
## 第一问
df_demo = df.set_index(['department','job_title','gender'])
df_demo.swaplevel(0,2,axis=0).head()
第二问:
df_demo.reset_index([1])
第三问:
df_demo.rename_axis(index={'gender':'Gender'})
第四问:
new_idx = df_demo.index.map(lambda x:(x[0]+'_'+x[1]))
df_demo.index = new_idx
第五问:
new_idx = df_demo.index.map(lambda x:tuple(x.split('_')))
df_demo.index = new_idx
第六问:
df_demo.rename_axis(['gender', 'department'],axis=0)
第七问:
df_demo.reset_index()
df_demo.reindex(columns=df.columns)
Ex2
In [169]: df = pd.read_csv('data/chocolate.csv')
In [170]: df.head(3)
Out[170]:
Company Review\nDate Cocoa\nPercent Company\nLocation Rating
0 A. Morin 2016 63% France 3.75
1 A. Morin 2015 70% France 2.75
2 A. Morin 2015 70% France 3.00
- 把列索引名中的
\n
替换为空格。 - 巧克力
Rating
评分为1至5,每0.25分一档,请选出2.75分及以下且可可含量Cocoa Percent
高于中位数的样本。 - 将
Review Date
和Company Location
设为索引后,选出Review Date
在2012年之后且Company Location
不属于France, Canada, Amsterdam, Belgium
的样本。
第一问:
df_demo = df.copy()
df_demo = df_demo.rename(columns=lambda x:str.replace(x,'\n',' '))
第二问:
df_demo['Cocoa Percent'] = df_demo['Cocoa Percent'].apply(lambda x:float(x[:-1]))
median = df_demo['Cocoa Percent'].median()
df_demo.loc[(df_demo.Rating <= 2.75) &(df_demo['Cocoa Percent'] > median)]
第三问:
idx = pd.IndexSlice
df_demo = df_demo.sort_index()
Location_diff = df_demo.index.get_level_values(1).unique().difference(['France','Canada','Amsterdam','Belgium'])
df_demo.loc[idx[2012:,Location_diff],:]