DataWhale & Pandas(三、索引)
学习大纲:
目录
三、索引
首先需要导入numpy和pandas库
import numpy as np
import pandas as pd
3. 索引器
在dataframe里面分为行索引和列索引。
- 行索引:index
- 列索引:columns
3.1 列索引
列索引呢,比较常见,一般通过
[]
来实现,就比方下面这个例子从数据集中取出姓名这一列:
df = pd.read_csv('../data/learn_pandas.csv',
usecols = ['School', 'Grade', 'Name',
'Gender', 'Weight', 'Transfer']) #导入数据集
df['Name'].head() #取出姓名这列
那同理呢,取出学校这列就是:
我们学会了提取一列的方法,这个时候我们提取多列试一下,我们提取性别和姓名两列试一下:
注意:
- 若要取出单列,且列名中不包含空格,则可以用
.列名
取出,这和[列名]
是等价的.NAME = [NAME]
3.2 行索引
以字符串为索引的
Series
- 如果取出单个索引的对应元素,则可以使用
[item]
,若Series
只有单个值对应,则返回这个标量值,如果有多个值对应,则返回一个Series
- 如果取出多个索引的对应元素,则可以使用
[items的列表]
- 如果想要取出某两个索引之间的元素,并且这两个索引是在整个索引中唯一出现,则可以使用切片,,同时需要注意这里的切片会包含两个端点
以整数为索引的
Series
- 在使用数据的读入函数时,如果不特别指定所对应的列作为索引,那么会生成从0开始的整数索引作为默认索引。当然,任意一组符合长度要求的整数都可以作为索引。
- 和字符串一样,如果使用
[int]
或[int_list]
,则可以取出对应索引元素的值- 如果使用整数切片,则会取出对应索引位置的值,注意这里的整数切片同
Python
中的切片一样不包含右端点
注意:
不要把纯浮点以及任何混合类型(字符串、整数、浮点类型等的混合)作为索引,否则可能会在具体的操作时报错或者返回非预期的结果
3.3 loc索引器
对于表而言,有两种索引器,
- 一种是基于元素的
loc
索引器,- 另一种是基于位置的
iloc
索引器。
loc
索引器的一般形式是loc[*, *]
,其中:
- 第一个
*
代表行的选择- 第二个
*
代表列的选择如果省略第二个位置写作
loc[*]
,这个*
是指行的筛选。其中,
*
的位置一共有五类合法对象,分别是:单个元素、元素列表、元素切片、布尔列表以及函数
这里利用
set_index
方法把Name
列设为索引
df_demo = df.set_index('Name')
df_demo.head()
1. *
为单个元素此时,直接取出相应的行或列,如果该元素在索引中重复则结果为
DataFrame
,否则为Series
df_demo.loc['Qiang Sun'] # 多个人叫此名字 df_demo.loc['Quan Zhao'] # 名字唯一
也可以同时选择行和列
df_demo.loc['Qiang Sun', 'School'] # 返回Series df_demo.loc['Quan Zhao', 'School'] # 返回单个元素
2. *
为元素列表取出列表中所有元素值对应的行或列
df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]
3. *
为切片之前的
Series
使用字符串索引时提到,如果是唯一值的起点和终点字符,那么就可以使用切片,并且包含两个端点,如果不唯一则报错df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']
需要注意的是,如果
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] df_loc_slice_demo.loc[3:5] # 没有返回,说明不是整数位置切片
4. *
为布尔列表在实际的数据处理中,根据条件来筛选行是极其常见的,此处传入
loc
的布尔列表与DataFrame
长度相同,且列表为True
的位置所对应的行会被选中,False
则会被剔除。例如,选出体重超过70kg的学生
df_demo.loc[df_demo.Weight>70].head() #选出体重超过70kg的学生 df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head() #选出所有大一和大四的同学信息 # 选出复旦大学中体重超过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] ##同时使用多个条件时可以通过定义布尔值变量进行多条件筛选
5.
*
为函数这里的函数,必须以前面的四种合法形式之一为返回值,并且函数的输入值为
DataFrame
本身。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] ##这里的函数返回值必须是前面要求的布尔值列表
注意:
不要使用链式赋值
在对表或者序列赋值时,应当在使用一层索引器后直接进行赋值操作,这样做是由于进行多次索引后赋值是赋在临时返回的
copy
副本上的,而没有真正修改元素从而报出SettingWithCopyWarning
警告。
3.4.iloc
.iloc() 是基于整数的索引,利用元素在各个轴上的索引序号进行选择,序号超过范围产生IndexError,切片时允许序号超过范围。
- 整数,类似于.loc,只使用一个维度,即对行选择,小标默认从 0 开始。例如:df.iloc[5],选择df第 6 行。
- 整数列表或者数组,例如df.iloc[[5, 1, 7]],选择df第 6 行, 第 2 行, 第 8 行。
- 元素为整数的切片操作,不同于.loc,则下标为 stop 的数据不被选择。如:df.iloc[0:3], 只包含 0,1,2行,不包含第 3 行。
- 也可以使用布尔数组进行筛选,例如 df.iloc[np.array(df.A>0.5)],df.iloc[list(df.A>0.5)]。
- 注意使用布尔数组进行筛选时,可以使用 list 或者 array,使用 Series会出错,NotImplementedError 和 ValueError,前者是 Series 的 index 与待切片 DataFrame的index 不同时报错,后置 index 相同时报错。与.loc使用布尔数组,可以使用 list, array, 也可以使用Series,使用Series时 index需要一致,否则会报 IndexError。
4. 多级索引
4.1 多级索引及其表的结构
1.隐式创建
在构造函数中给index、colunms等多个数组实现(datafarme与series都可以)
2.显式创建pd.MultiIndex
其中.from_arrays为参数,推荐使用简单的from_product函数
4.2 多级索引中的loc索引器
df_m=df.set_index(['School','Grade'])
注意:
- 索引的名字和值属性分别可以通过
names
和values
获得- 如果想要得到某一层的索引,则需要通过
get_level_values
获得- 但对于索引而言,无论是单层还是多层,用户都无法通过
index_obj[0] = item
的方式来修改元素,也不能通过index_name[0] = new_name
的方式来修改名字- 在索引前最好对
MultiIndex
进行排序以避免性能警告- 与单层索引类似,若存在重复元素,则不能使用切片,请去除重复索引后给出一个元素切片的例子
5、索引的常用方法
5.1. 索引层的交换和删除
5.2 索引属性的修改
通过
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()
5.3. 索引的设置与重置
new_df= pd.DataFrame({'H':list('hello'),'W':list('World'),'S':[1,2,3,4,5]})
# 索引的设置可以使用 set_index 完成,
# 其主要参数是 append ,表示是否来保留原来的索引,直接把新设定的添加到原索引的内层
df_new.set_index('A')
df_new.set_index('A', append=True)
#可以同时指定多个列作为索引
df_new.set_index(['A', 'B'])
#如果想要添加索引的列没有出现再其中,那么可以直接在参数中传入相应的Series
my_index = pd.Series(list('WXYZA'), name='D')
new_df=new_df.set_index(['H',my_index])
df_new
# reset_index 是 set_index 的逆函数,其主要参数是 drop ,
df_new.reset_index(['D'])
# 表示是否要把去掉的索引层丢弃,而不是添加到列中
df_new.reset_index(['D'],drop=True)
#如果重置了所有的索引,那么pandas会直接重新生成一个默认索引
df_new.reset_index()
5.4 索引的变形
在某些场合下,需要对索引做一些扩充或者剔除,更具体地要求是给定一个新的索引,把原表中相应的索引对应元素填充到新索引构成的表中。
#要求增加一名员工的同时去掉身高列并增加性别列
df_reindex = pd.DataFrame({"Weight":[60,70,80], "Height":[176,180,179]}, index=['1001','1003','1002'])
df_reindex.reindex(index=['1001','1002','1003','1004'], columns=['Weight','Gender'])
#这种需求常出现在时间序列索引的时间点填充以及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)
6.索引运算
6.1. 集合的运算法则
6.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_1
0 | 1 | |
---|---|---|
id1 | ||
a | 0 | 1 |
b | 1 | 2 |
a | 3 | 4 |
df_set_2
0 | 1 | |
---|---|---|
id2 | ||
b | 4 | 5 |
b | 2 | 6 |
c | 7 | 1 |
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[df_set_in_col_1.id1.isin(df_set_in_col_2.id2)]
id1 | 0 | 1 | |
---|---|---|---|
1 | b | 1 | 2 |
7.练习
Ex1:公司员工数据集
现有一份公司员工数据集:
df = pd.read_csv('data/company.csv')
df.head(3)
#
EmployeeID birthdate_key age city_name department job_title gender
0 1318 1/3/1954 61 Vancouver Executive CEO M
1 1319 1/3/1957 58 Vancouver Executive VP Stores F
2 1320 1/2/1955 60 Vancouver Executive Legal Counsel F
分别只使用
query
和loc
选出年龄不超过四十岁且工作部门为Dairy
或Bakery
的男性。condition_1 = df['age'] <= 40 condition_2 = df['department'].isin(['Dairy', 'Bakery']) condition_3 = df['gender'] == 'M' condition = condition_1 & condition_2 & condition_3 df.loc[condition].head()
df.query('(age<=40) and (department == ["Dairy", "Bakery"]) and gender == "M"').head()
选出员工
ID
号 为奇数所在行的第1、第3和倒数第2列。df.loc[df['EmployeeID']%2==1].iloc[:, [0, 2, -2]]
按照以下步骤进行索引操作:
- 把后三列设为索引后交换内外两层
df_cp = df.set_index(['department', 'job_title', 'gender']) df_cp = df_cp.swaplevel(0, 2, axis=0) df_cp.head()
- 恢复中间一层
df_cp = df_cp.reset_index(['job_title']) df_cp.head()
- 修改外层索引名为
Gender
df_cp = df_cp.rename_axis(index={'gender': 'Gender'}) df_cp.head()
- 用下划线合并两层行索引
new_idx = df_cp.index.map(lambda x: x[0] + '_' + x[1]) df_cp.index = new_idx df_cp.head()
- 把行索引拆分为原状态
new_idx = df_cp.index.map(lambda x: tuple(x.split('_'))) df_cp.index = new_idx df_cp.head()
- 修改索引名为原表名称
df_cp = df_cp.rename_axis(index=['gender', 'department']) df_cp.head()
- 恢复默认索引并将列保持为原表的相对位置
df_cp = df_cp.reindex(df.columns, axis=1) df_cp.head()
Ex2:巧克力数据集
现有一份关于巧克力评价的数据集:
df = pd.read_csv('data/company.csv')
df.head(3)
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
替换为空格。df.columns = [' '.join(i.split('\n')) for i in df.columns] df.head()
- 巧克力
Rating
评分为1至5,每0.25分一档,请选出2.75分及以下且可可含量Cocoa Percent
高于中位数的样本。df['Cocoa Percent'] = df['Cocoa Percent'].str.strip('%').astype('float64')/100 condition_1 = df['Rating'] <= 2.75 condition_2 = df['Cocoa Percent'] >= df['Cocoa Percent'].median() df[condition_1 & condition_2].head()
- 将
Review Date
和Company Location
设为索引后,选出Review Date
在2012年之后且Company Location
不属于France, Canada, Amsterdam, Belgium
的样本