前言:此文知识点是由Datawhale提供的pandas学习材料中总结而来,感谢闫大佬的分享。Pandas学习项目github链接
单级索引
在数据处理中,我们比较常用的索引方法有三种:
- loc 标签索引
- iloc 位置索引
- [] 一种便利的索引方式(列标签索引)
loc【对标签索引操作】
loc中传入的值只能是布尔列表和索引子集构成的列表,主要是对行标签操作。loc的切片操作时包含右端点的,左右全闭,这样我们就不需要知道后一列的名字。当dataframe没有做索引时,使用的是默认整数索引,若启动了新索引,那么采用新索引
- 以下是loc的常用例子:
"读取数据,同时指定某一列为索引"
df = pd.read_csv('data/table.csv',index_col='ID')
"同等效果的语句:"
df1 = pd.read_csv('data/table.csv')
df2=df1.set_index(['ID'])
#对单行的索引
df.loc[1103]
#多行索引
df.loc[[1102,2304]]
#切片也可以
df.loc[2402::-1].head()
- 函数索引
本质上这是一个布尔索引,通过函数判断,返回布尔值。根据待筛选的行或者列对给定的筛选条件是否为真来决定是否返回该行或该列
df.loc[lambda x:x['Gender']=='M'].head()
def f(x):
return [1101,1103]
df.loc[f]
“传入f的参数为df,返回1101和1103的行”
- 布尔索引
df.loc[df['Address'].isin(['street_7','street_4'])].head()
iloc【对默认索引】
iloc使用的是整数索引,所以iloc中接收的参数只能为整数或整数列表,不能使用布尔索引。并且切片中,采用左闭右开
#单行索引
df.iloc[3]
#多行索引
df.iloc[3:5]
#单列索引
df.iloc[:,3].head()
#多列索引
df.iloc[:,7::-2].head()
#混合索引
df.iloc[3::4,7::-2].head()
#函数索引
df.iloc[lambda x:range(3)]
[] 操作符
默认是采用建立好的索引,如果没建立索引,就采用默认整数索引。切片中使用的是绝对位置的整数切片,与元素值无关,例如0到4行,df[0:4]。
Series 的[]操作
在Series中,请不要在行索引为浮点时使用[]操作符,因为并不是进行位置比较,而是值比较
s = pd.Series(df['Math'],index=df.index)
s[1101] 同等“s.loc[1101]”
s.iloc[1] #取第二行
s[0:4] #多行索引
s[lambda x: x[16::-6].index] #函数索引
因为lambda函数返回值是索引, 索引通过方括号传递给s,
就可以取回s的相应索引位置的元素。
s[s>80] #布尔索引,取那些值大于80的行
Dataframe的[]操作
单行索引使用的是绝对位置切片,而不是标签索引
df[1:2]
#多行索引
df[3:5]
但是我们采用标签索引也不会报错,是因为先创建一个空的索引集合去取数,取不到就返回一个空集合,所以没有报错。我们也可以采用get_loc获取标签索引的整数索引
row = df.index.get_loc(1102)
df[row:row+1]
我们也可以直接采用loc就可以获得特定标签的行,但是直接传入单个标签,返回的是Series,所以要加个区间,或者换写成列表
df.loc[1102,:] ,返回sereis
df.loc[[1102],:] 返回dataframe
df.loc[1102:1102,:] 返回dataframe
单列索引,多列索引,函数索引,布尔索引
df['School'].head()
df[['School','Math']].head()
df[lambda x:['Math','Physics']].head()
df[df['Gender']=='F'].head()
布尔索引
布尔符号:’&’,’|’,’~’:分别代表和and,或or,取反not
df.loc[df['Math']>60,(df[:8]['Address']=='street_6').values].head()
后半部是布尔判断值,df的出来的是series,values就是布尔值,所以要取出来
- isin方法
df[df['Address'].isin(['street_1','street_4'])&df['Physics'].isin(['A','A+'])]
'上面也可以用字典方式写:'
df[df[['Address','Physics']].isin({'Address':['street_1','street_4'],'Physics':['A','A+']}).all(1)]
'all与&的思路是类似的,其中的1代表按照跨列方向判断是否全为True'
快速标量索引
at 和 iat方法,快速获取一个元素
display(df.at[1101,'School'])
display(df.loc[1101,'School'])
display(df.iat[0,0])
display(df.iloc[0,0])
#可尝试去掉注释对比时间
%timeit df.at[1101,'School']
%timeit df.loc[1101,'School']
%timeit df.iat[0,0]
%timeit df.iloc[0,0]
区间索引
划分区间的interval_range方法
还有closed参数,代表是否闭合,有left, right, both, neither可选,默认左开右闭,即right
pd.interval_range(start=0,end=5)
periods参数控制区间个数,freq控制步长
pd.interval_range(start=0,periods=8,freq=5)
用cut将数值列转为区间的分类元素
math_interval = pd.cut(df['Math'],bins=[0,40,60,80,100])
此时获得的类型不是interval,是category类型
区间索引的选取
df_i = df.join(math_interval,rsuffix="_interval")[['Math','Math_interval']].reset_index().set_idnex('Math_interval')
把math_interval连接上df中,取math和mathinterval列,本身有ID作为index,重新设置回默认的整数index,然后再设置Math_interval为标签
```javascript
df_i.loc[65] #65在哪个范围内,那么这个范围的行都显示出来
df_i.iloc[[3]].head() #取第四行,为dataframe
- 如果想要选取某个区间,先要把分类变量转为区间变量,再使用overlap方法:
df_i[df_i.index.astype('interval').overlaps(pd.Interval(70, 85))].head()
overlaps 用于判断一个由区间构成的类list的表里的元素是否与给定的区间有重合,有重合则返回True—本质上还是传递一个布尔值list给df_i。返回所有的行索引(转换为区间后)与给定区间有重叠的行。
多级索引
创建多级索引
方法一:from_tuple
tuples = [('A','a'),('A','b'),('B','a'),('B','b')]
mul_index = pd.MultiIndex.from_tuples(tuples, names=('Upper', 'Lower'))
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index)
#tuple的另一种创建方式
L1 = list('AABB')
L2 = list('abab')
tuples = list(zip(L1,L2))
#一次性创建
dftemp=pd.DataFrame(np.random.randn(20).reshape(10,2), index=pd.MultiIndex.from_tuples(list(arr),names=('left','right'))).sort_index()
若tuple是未排序的,那么创建的dataframe也未排序,可以使用sort_index()排序
arrays = [['A','a'],['B','a'],['A','b'],['B','b']]
mul_index = pd.MultiIndex.from_tuples(arrays, names=('Upper', 'Lower'))
即使内层是list,也会自动转换成元组
from_product
本质是笛卡尔乘积的所有结果作为索引,用得不多
L1 = ['A','B']
L2 = ['a','b']
pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))
指定列创建索引
df_using_mul = df.set_index(['Class','Address'])
多层索引切片
- 一般切片
df_using_mul.loc['C_2','street_5'] #未排序,会报出性能警告
df_using_mul.index.is_lexsorted() # 检查是否排序了,返回布尔值
df_using_mul.sort_index().loc['C_2','street_5']
#排序后,可以再次调用上面第二句查看是否排序
- 多层切片
当不排序时,无法使用多层切片,会报错
非元组也是合法的,表示选中该层所有元素
df_using_mul.sort_index().loc[('C_2','street_6'):('C_3','street_4')]
df_using_mul.sort_index().loc[('C_2','street_7'):'C_3'].head()
- 特殊情况:由元组组成列表
在元组外套一个列表,表示选出某几个元素,精确到最内层索引.
df_using_mul.sort_index().loc[[('C_2','street_7'),('C_3','street_2')]]
带[],第一个传入元素,不会报错;但是传入元组,那就报错了
df_using_mul.sort_index().loc[['C_2',('C_3','street_2')]] #good
#df_using_mul.sort_index().loc[[('C_3','street_2'),'C_4']] #bad,报错
如果第一个位置是元组,那就默认是按照元组的相应位置去对应相应层级的索引的值;如果第一个位置是元素, 那就默认直接对应第一层索引的相应取值。
df_using_mul.sort_index().loc['C_2':('C_3','street_2')]
df_using_mul.sort_index().loc[('C_3','street_2'):'C_4']
对比这两句,可知道元素和元组构成的切片,不会报错,重点是不能在外面加“[]”,加[]是为了指定某些东西才加上的
- 特殊情况:由列表构成元组
这里与前面不相同,列表是按照同一个index划分,而上面是index之间的组合作为元组划分的。
df_using_mul.sort_index().loc[(['C_2','C_3'],['street_4','street_7']),:]
#选出第一层在‘C_2’和'C_3'中且第二层在'street_4'和'street_7'中的行
下面语句不等价于使用zip将两个list绑定,而是等价于将两个list做笛卡尔乘积。
df_using_mul.sort_index().loc[zip(['C_2','C_3'],['street_4','street_7']),:]
多层索引中的slice
行和列都有两层的索引
L1,L2 = ['A','B'],['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_s = pd.DataFrame(np.random.rand(6,9),index=mul_index1,columns=mul_index2)
df_s
IndexSlice的使用:
df_s.loc[pd.IndexSlice['B':,df_s['D']['d']>0.3],pd.IndexSlice[df_s.sum()>4]]
看上去很复杂,我们需要分解来看,前半部是找Upper>B或者D下的d大于0.3的值,同时对于列索引,找出列的和大于4的列.
IndexSlice本质上是对多个Slice对象的包装
索引层的交换
本来的行索引是[‘class’,‘address’],使用swaplevel方法就可以互换位置,[‘address’,‘class’]
df_using_mul.swaplevel(i=1,j=0,axis=0).sort_index().head()
- reorder_levels方法(多层交换)
换成[‘Address’,‘School’,‘Class’]
df_muls = df.set_index(['School','Class','Address'])
df_muls.reorder_levels([2,0,1],axis=0).sort_index().head()
#如果索引有name,可以直接使用name
df_muls.reorder_levels(['Address','School','Class'],axis=0).sort_index().head()
索引设定
读取数据时的index_col参数
可以快速地做到索引的设置
pd.read_csv('data/table.csv',index_col=['Address','School']).head()
reindex和reindex_like
reindex是指重新索引,它的重要特性在于索引对齐,很多时候用于重新排序。
就是当我们处理完数据的时候,可以做一下reindex,把排序重新排列一下
df.reindex()
df.reindex(index=[1101,1203,1206,2402])
#获取到这4个index的行
df.reindex(columns=['Height','Gender','Average']).head()
#对列名也可以操作
df.reindex(index=[1101,1203,1206,2402],method='bfill')
#选择缺失值的填充方法
- reindex里面有个method参数,可以用于填充缺失值,但是这个参数要求索引必须单调,即索引已经排序了。有三个选择:
- bfill : 表示找当前缺失值的后一个有效行填充
- ffill:表示填充前一个有效行
- nearest:表示最近的有效行,如果前后都有效,那么选择前一个有效行
- 除了method参数,也可以采用fill_value参数,这个比较简单,会直接把所有缺失值填充为你所填写的值
reindex_like作用是生成一个横纵索引完全与参数列表一致的DataFrame,数据内容是对应的表
df_temp = pd.DataFrame({'Weight':np.zeros(5),
'Height':np.zeros(5),
'ID':[1,3,4,5,6]}).set_index('ID')
df_temp.reindex_like(df[0:5][['Weight','Height']])
- 使用方法如上,有个df_temp希望模仿另一个df的索引,就使用reindex_like去嫩它<_<
set_index和reset_index
set_index
- 设置某列作为索引
df.set_index('ID').head()
- append参数可以不改变当前索引,再添加新索引
df.set_index('Class',append=True)
- 重设默认索引,即使用与表长相同的列作为索引,需要先换为Series
df.set_index(pd.Series(range(df.shape[0]))).head()
- 直接添加多级索引
df.set_index([pd.Series(range(df.shape[0])),pd.Series(np.ones(df.shape[0]))]).head()
reset_index
- 不添加参数的默认情况下,直接恢复到自然数索引
df.reset_index()
- 面对多层索引,需要使用level参数指定哪一层reset,col_level指定set到哪一层. 下面的行和列索引各有两层,我把第二层的Lower行索引设置回到列明的第二层去,第二层是(d,e,f)的那一层
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_temp = pd.DataFrame(np.random.rand(9,9),index=mul_index1,columns=mul_index2)
df_temp.head()
df_temp1 = df_temp.reset_index(level=1,col_level=0)
df_temp1.head()
rename_axis和rename
rename_axis
针对多级索引,修改某一层的索引名,而不是索引标签
df_temp.rename_axis(index={'Lower':'LowerLower'},columns={'Big':'BigBig'})
rename
而rename方法是用于修改列或者行索引标签,而不是索引名哦!不要搞混了
df_temp.rename(index={'A':'T'},columns={'e':'changed_e'}).head()
常用索引型函数
where函数
- 这是一个判断函数,对条件为False的单元进行填充
df.where(df['Gender']=='M').head() #默认情况,填充NaN
- 删掉不等于M的行
df.where(df['Gender']=='M').dropna().head()
"效果一致"
df[df.Gender == 'M']
- 第一个参数是判断布尔条件,第二个参数为填充值
df.where(df['Gender']=='M',np.random.rand(df.shape[0],df.shape[1])).head()
Mask函数
- mask函数与where是刚好相反的, 即对条件为True的单元进行填充
df.mask(df['Gender']=='M').dropna().head()
query函数
query函数中的布尔表达式中,下面的符号都是合法的:行列索引名、字符串、and/not/or/&/|/~/not in/in/==/!=、四则运算符
df.query('(Address in ["street_6","street_7"])&(Weight>(70+10))&(ID in [1303,2304,2402])')
重复元素处理
duplicated方法.
返回是否重复的布尔列表 , 重复就返回True,不重复返回False
df.duplicated('Class').head()
可选参数keep的默认值为first,即首次出现设为不重复,可以设为last,则最后一次设为不重复,若为False,则所有重复项为True
drop_duplicate方法
删除重复项,也有keep这个参数。
在传入多列时等价于将多列共同视作一个多级索引,比较重复项
df.drop_duplicates('Class')
df.drop_duplicates(['School','Class'])
抽样函数
- n : 样本量
- frac : 抽样比,按照比例抽多少个
- replace : 是否放回
- axis : 抽样维度,默认为0,即抽行
- weights为样本权重,自动归一化
df.sample(n=5)
df.sample(frac=0.05)
df.sample(n=df.shape[0],replace=True).head()
df.sample(n=3,axis=1).head() #抽出3列
#抽到的概率与Math数值成正比
df.sample(n=3,weights=df['Math']).head()