本文是DataWhale组队学习pandas的学习总结。
import numpy as np
import pandas as pd
一、索引器
1. 表的列索引
列索引是最常见的索引形式,一般通过[]来实现。通过[列名]可以从DataFrame中取出相应的列,返回值为Series,例如从表中取出姓名一列:
df = pd.read_csv('../data/learn_pandas.csv', usecols = ['School', 'Grade', 'Name', 'Gender', 'Weight', 'Transfer'])
df['Name'].head()
如果要取出多个列,则可以通过[列名组成的列表],其返回值为一个DataFrame,例如从表中取出性别和姓名两列:
df[['Gender', 'Name']].head()
此外,若要取出单列,且列名中不包含空格,则可以用.列名取出,这和[列名]是等价的:
df.Name.head()
2. 序列的行索引
【a】以字符串为索引的Series
如果取出单个索引的对应元素,则可以使用[item],若Series只有单个值对应,则返回这个标量值,如果有多个值对应,则返回一个Series:
s = pd.Series([1, 2, 3, 4, 5, 6], index=['a', 'b', 'a', 'a', 'a', 'c'])
s['a']
如果取出多个索引的对应元素,则可以使用[items的列表]:
s[['c', 'b']]
【b】以整数为索引的Series
在使用数据的读入函数时,如果不特别指定所对应的列作为索引,那么会生成从0开始的整数索引作为默认索引。当然,任意一组符合长度要求的整数都可以作为索引。
和字符串一样,如果使用[int]或[int_list],则可以取出对应索引元素的值:
s = pd.Series(['a', 'b', 'c', 'd', 'e', 'f'], index=[1, 3, 1, 2, 5, 4])
s[1]
s[[2,3]]
3. loc索引器
前面讲到了对DataFrame的列进行选取,下面要讨论其行的选取。对于表而言,有两种索引器,一种是基于元素的loc索引器,另一种是基于位置的iloc索引器。
为了演示相应操作,先利用set_index方法把Name列设为索引.
df_demo = df.set_index('Name')
df_demo.head()
【a】loc参数为单个元素
df_demo.loc['Qiang Sun'] # 多个人叫此名字
df_demo.loc['Quan Zhao'] # 名字唯一
也可以同时选择行和列:
df_demo.loc['Qiang Sun', 'School'] # 返回Series
df_demo.loc['Quan Zhao', 'School'] # 返回单个元素
【b】loc参数为元素列表
此时,取出列表中所有元素值对应的行或列:
df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]
【c】loc参数为切片
df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']
【d】loc参数为布尔列表
在实际的数据处理中,根据条件来筛选行是极其常见的,此处传入loc的布尔列表与DataFrame长度相同,且列表为True的位置所对应的行会被选中,False则会被剔除。
例如,选出体重超过70kg的学生:
df_demo.loc[df_demo.Weight>70].head()
前面所提到的传入元素列表,也可以通过isin方法返回的布尔列表等价写出,例如选出所有大一和大四的同学信息:
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]
【e】loc参数为函数
这里的函数,必须以前面的四种合法形式之一为返回值,并且函数的输入值为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]
4. iloc索引器
iloc的使用与loc完全类似,只不过是针对位置进行筛选,在相应的*位置处一共也有五类合法对象,分别是:整数、整数列表、整数切片、布尔列表以及函数,函数的返回值必须是前面的四类合法对象中的一个,其输入同样也为DataFrame本身。
df_demo.iloc[1, 1] # 第二行第二列
df_demo.iloc[[0, 1], [0, 1]] # 前两行前两列
df_demo.iloc[1: 4, 2:4] # 切片不包含结束端点
df_demo.iloc[lambda x: slice(1, 4)] # 传入切片为返回值的函数
在使用布尔列表的时候要特别注意,不能传入Series而必须传入序列的values,否则会报错。因此,在使用布尔筛选的时候还是应当优先考虑loc的方式。
例如,选出体重超过80kg的学生:
df_demo.iloc[(df_demo.Weight>80).values].head()
对Series而言同样也可以通过iloc返回相应位置的值或子序列:
df_demo.School.iloc[1]
df_demo.School.iloc[1:5:2]
5. query方法
在pandas中,支持把字符串形式的查询表达式传入query方法来查询数据,其表达式的执行结果必须返回布尔列表。在进行复杂索引时,由于这种检索方式无需像普通方法一样重复使用DataFrame的名字来引用列名,一般而言会使代码长度在不降低可读性的前提下有所减少。
例如,将loc一节中的复合条件查询例子可以如下改写:
df.query('((School == "Fudan University")&'
' (Grade == "Senior")&'
' (Weight > 70))|'
'((School == "Peking University")&'
' (Grade != "Senior")&'
' (Weight > 80))')
在query表达式中,帮用户注册了所有来自DataFrame的列名,所有属于该Series的方法都可以被调用,和正常的函数调用并没有区别,例如查询体重超过均值的学生:
df.query('Weight > Weight.mean()').head()
同时,在query中还注册了若干英语的字面用法,帮助提高可读性,例如:or, and, or, is in, not in。例如,筛选出男生中不是大一大二的学生:
df.query('(Grade not in ["Freshman", "Sophomore"]) and (Gender == "Male")').head()
此外,在字符串中出现与列表的比较时,==和!=分别表示元素出现在列表和没有出现在列表,等价于is in和not in,例如查询所有大三和大四的学生:
df.query('Grade == ["Junior", "Senior"]').head()
对于query中的字符串,如果要引用外部变量,只需在变量名前加@符号。例如,取出体重位于70kg到80kg之间的学生:
low, high =70, 80
df.query('Weight.between(@low, @high)').head()
6. 随机抽样
如果把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
df_sample.sample(3, replace = True, weights = df_sample.value)
二、多级索引
1. 多级索引及其表的结构
为了更加清晰地说明具有多级索引的DataFrame结构,下面新构造一张表.
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
下图通过颜色区分,标记了DataFrame的结构。与单层索引的表一样,具备元素值、行索引和列索引三个部分。其中,这里的行索引和列索引都是MultiIndex类型,只不过索引中的一个元素是元组而不是单层索引中的标量。例如,行索引的第四个元素为(“B”, “Male”),列索引的第二个元素为(“Height”, “Senior”),这里需要注意,外层连续出现相同的值时,第一次之后出现的会被隐藏显示,使结果的可读性增强。
与单层索引类似,MultiIndex也具有名字属性,图中的School和Gender分别对应了表的第一层和第二层行索引的名字,Indicator和Grade分别对应了第一层和第二层列索引的名字。
索引的名字和值属性分别可以通过names和values获得:
df_multi.index.names
df_multi.columns.names
df_multi.index.values
df_multi.columns.values
2. 多级索引中的loc索引器
熟悉了结构后,现在回到原表,将学校和年级设为索引,此时的行为多级索引,列为单级索引,由于默认状态的列索引不含名字,因此对应于刚刚图中Indicator和Grade的索引名位置是空缺的。
df_multi = df.set_index(['School', 'Grade'])
df_multi.head()
由于多级索引中的单个元素以元组为单位,因此之前在第一节介绍的loc和iloc方法完全可以照搬,只需把标量的位置替换成对应的元组,不过在索引前最好对MultiIndex进行排序以避免性能警告:
df_multi = df_multi.sort_index()
df_multi.loc[('Fudan University', 'Junior')].head()
df_multi.loc[[('Fudan University', 'Senior'), ('Shanghai Jiao Tong University', 'Freshman')]].head()
df_multi.loc[df_multi.Weight > 70].head() # 布尔列表也是可用的
df_multi.loc[lambda x:('Fudan University','Junior')].head()
此外,在多级索引中的元组有一种特殊的用法,可以对多层的元素进行交叉组合后索引,但同时需要指定loc的列,全选则用:表示。其中,每一层需要选中的元素用列表存放,传入loc的形式为[(level_0_list, level_1_list), cols]。例如,想要得到所有北大和复旦的大二大三学生,可以如下写出:
res = df_multi.loc[(['Peking University', 'Fudan University'], ['Sophomore', 'Junior']), :]
res.head()
res.shape # --->(33,4)
下面的语句和上面类似,但仍然传入的是元素(这里为元组)的列表,它们的意义是不同的,表示的是选出北大的大三学生和复旦的大二学生:
res = df_multi.loc[[('Peking University', 'Junior'), ('Fudan University', 'Sophomore')]]
res.head()
res.shape #---->(16,4)