索引

import numpy as np
import pandas as pd

索引器

表的列索引

一般通过[]来实现,通过[列名]可以从DataFrame中取出相应的列,返回值为Series

df=pd.read_csv('',usecols=['School', 'Grade', 'Name', 'Gender','Weight','Transfer'])
df['Name'].head()
#如果要取出多个列,可以通过`[列名组成的列表]`,返回值为一个DataFrame
#从表中取出姓名和性别两列
df[['Gender','Name']].head()
#取出单列可以使用`.列名`取出,与`[列名]`是等价的
df.Name.head

2. 序列的行索引

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

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

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

#取出两个索引之间的元素,并且这两个索引是在整个索引中唯一出现,则可以使用切片
s['c':'b':-2]
#这里的切片包含两个端点

如果前后端点的值存在重复,即非唯一值,需要经过排序才能使用切片

try:
	s['a':'b']
except Exception as e:
		Err_Msg=e
	
Err_Msg
s.sort_index()['a':'b']

[b]以整数为索引的Series
在使用数据的读入函数的时候,如果不特别指定所对应的列作为索引,那么会生成从0开始的整数索引作为默认索引

s=pd.Series(['a','b','c','d','e','f'],index=[1,3,1,2,5,4])
s[1]
s[[2,3]]

如果使用整数切片则会取出对应索引位置的值
这里的切片不包含右端点

s[1:-1:2]
3 b
2 d

不要把纯浮点以及任何混合类型作为索引,否则可能会在具体的操作时报错或者返回非预期的结果

3. loc索引器

前面讲到了对DataFrame的列进行选取
下面进行对行的选取
对表而言有两种索引器

  • 基于元素的loc索引器
  • 基于位置的iloc索引器

loc索引器一般形式是loc[*,*],第一个*表示行的选取,第二个*表示列的选取
*的位置一共有五类合法对象:单个元素,元素列表,元素切片,布尔列表以及函数

df_demo=df.set_index('Name')
df_demo.head()

[a]*为单个元素,如果该元素在索引中重复则结果为DataFrame,否则为Series

df_demo.loc['Qiang Sun']#多个人叫这个名字返回DataFrame
df_demo.loc['Quan Zhao']#名字唯一返回Series
#同时选择行
df_demo.loc['Qiang Sun','School']#返回Series
df_demo.loc['Quan Zhao','School']#返回单个元素

[b]*为元素列表

df_demo.loc[['Qiang Sun','Quan Zhao'],['School','Gender']]

[c]*为切片

  • 如果字符串索引是唯一值的起点和终点字符则可以使用切片,并且包含两个端点,如果不唯一则报错
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]#没有返回,说明不是整数位置切片
df_demo.loc[df_demo.Weight > 70].head()
  • 前面提到的传入元素列表,也可以通过isin方法返回的布尔列表等价写出
    exp:列出所有大一和大四的同学的信息
df_demo.loc[df_demo.Grade.isin(['Freshman','Senior'])].head()
  • 对于复合条件而言,可以用|(或),&(且),~(取反)的组合来实现
    exp:选出复旦大学中体重超过70kg的大四学生,北大男生中体重超过80kg的非大四的学生
#分别求出每个条件的同学
condition_1_1=df_demo.School=='Fudan University'
condition_2_1=df_demo.Grade=='Senior'
condition_3_1=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]
【练一练】

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

[e]*为函数
这里的函数,必须以前面的四种合法形式之一为返回值,并且函数的输入值为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=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
	result=condition_1|condition_2
	return result
df_demo.loc[condition]

lambda表达式

df_demo.loc[lambda x:'Quan Zhao',lambda x:'Gender']
#由于函数无法返回如start:end:step的切片形式,故返回切片时要用slice对象进行包装
df_demo.loc[lambda x:slice('Gaojuan You','Gaoqiang Qian')]

#series可以使用`loc`索引

【Warning】不要用链式赋值
在对表或者序列赋值时,应当在使用一层索引器后直接进行赋值操作,这样由于进行多次索引后赋值是赋在临时返回的copy副本上的,而没有真正修改元素从而报出SettingWithCopyWarning警告

df_chain=pd.DataFrame([[0,0],[1,0],[-1,0]],column=list('AB'))
df_chain
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)

df_chain.loc[df.chain.A!=0,'B']=1

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[1:4,2:4]
#切片不包含结束端点
df_demo.iloc[lambda x:slice(1,4)]
#传入切片为返回值的函数
#使用布尔筛选的时候应当优先考虑loc的方式
#选出体重超过80kg的学生
df_demo.iloc[(df_demo.Weight>80).values].head()
df_demo.School.iloc[1]
df_demo.School.iloc[1:5:2]

5.query方法

在pandas中,支持把字符串形式的查询表达式传入query方法来查询数据,其表达式的执行结果必须返回布尔列表。

df.query('((School == "Fudan University"&'
'(Grade == Senior)&'
'(Weight > 70 ))|'
'((School == "Peking University"&'
'(Grade != "Senior")&'
'(Weight > 80))')

df.query('Weight > Weight.mean()').head()
【NOTE】query中引用带空格的列名

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

【END】

同时,在query中还注册了若干英语的字面用法,帮助提高可用性
例如or , and , in ,not in==等价于in
!=等价于not in

df.query('(Grade not in ["Freshman", "Sophomore"]) and (Gender == "Male")').head()

df.query('Grade == ["Junior","Senior"]').head()

#对于query中的字符串,如果要引用外部变量,只需要在变量名前加@符号

low,high=70,80
df.query('(Weight >= @low)&(Weight <= @high)')

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.sample(3,replace=True,weights=df_sample.value)

二、多级索引

1. 多级索引及其表的结构

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

NativeSearchQuery 根据根据索引名查询 根据索引名得到索引号_pandas

这里的行索引和列索引都是MultiIndex类型,只不过索引中的一个元素是元组而不是单层索引中的标量。
与单层索引类似,MultiIndex也具有名字属性
索引的名字和值属性分别可以通过names和values获得
如果想要得到某一层的索引,则需要通过get_level_values获得

df_multi.index.names
df_multi.columns.names
df_multi.index.values
df_multi.columns.values
df_multi.index.get_level_values(0)

无论是单层还是多层,用户都无法通过index_obj[0] = item的方式来修改元素,也不能通过index_name[0] = new_name的方式来修改名字

2. 多级索引中的loc索引器

将学校和年级设为索引,此时的行为多级索引,列为单级索引,由于默认状态的列索引不含名字,因此对应于刚刚图中IndicatorGrade的索引名位置是空缺的。

df_multi = df.set_index(['School', 'Grade'])
df_multi.head()

由于多级索引中的单个元素以元组为单位,因此之前在第一节介绍的 loc 和 iloc 方法完全可以照搬,只需把标量的位置替换成对应的元组。

当传入元组列表或单个元组或返回前二者的函数时,需要先进行索引排序以避免性能警告

with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
        df_multi.loc[('Fudan University', 'Junior')].head()
    except Warning as w:
        Warning_Msg = w
Warning_Msg

df_sorted = df_multi.sort_index()
df_sorted.loc[('Fudan University', 'Junior')].head()

df_sorted.loc[[('Fudan University', 'Senior'), ('Shanghai Jiao Tong University', 'Freshman')]].head()
df_sorted.loc[df_sorted.Weight > 70].head() # 布尔列表也是可用的
df_sorted.loc[lambda x:('Fudan University','Junior')].head()

在单级索引中只要切片端点元素是唯一的,那么就可以进行切片,但在多级索引中,无论元组在索引中是否重复出现,都必须经过排序才能使用切片,否则报错

try:
    df_multi.loc[('Fudan University', 'Senior'):].head()
except Exception as e:
    Err_Msg = e
Err_Msg
df_sorted.loc[('Fudan University', 'Senior'):].head()
df_unique = df.drop_duplicates(subset=['School','Grade']).set_index(['School', 'Grade'])
df_unique.head()

try:
    df_unique.loc[('Fudan University', 'Senior'):].head()
except Exception as e:
    Err_Msg = e
Err_Msg

df_unique.sort_index().loc[('Fudan University', 'Senior'):].head()

可以对多层的元素进行交叉组合后索引,
但同时需要指定loc的列,全选则用:表示。
其中,每一层需要选中的元素用列表存放,
传入loc的形式为[(level_0_list, level_1_list), cols]。
#例如,想要得到所有北大和复旦的大二大三学生,可以如下写出:
res = df_multi.loc[(['Peking University', 'Fudan University'], ['Sophomore', 'Junior']), :]
res.head()
#选出北大的大三学生和复旦的大二学生:
res = df_multi.loc[[('Peking University', 'Junior'), ('Fudan University', 'Sophomore')]]
res.head()

3. IndexSlice对象

即使在索引不重复的时候,也只能对元组整体进行切片,而不能对每层进行切片,也不允许将切片和布尔列表混合使用,引入IndexSlice对象就能解决这个问题。
Slice对象一共有两种形式

  • 第一种为loc[idx[*,*]]型,
  • 第二种为loc[idx[*,*],idx[*,*]]
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

NativeSearchQuery 根据根据索引名查询 根据索引名得到索引号_索引器_02


定义slice对象

idx = pd.IndexSlice

【a】loc[idx[*,*]]
前一个*表示行的选择,后一个*表示列的选择,与单纯的loc是类似的:

df_ex.loc[idx['C':, ('D', 'f'):]]
df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0

【b】loc[idx[*,*],idx[*,*]]

df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
#但需要注意的是,此时不支持使用函数:
try:
    df_ex.loc[idx[:'A', lambda x: 'b'], idx['E':, 'e':]]
except Exception as e:
    Err_Msg = e
Err_Msg

4. 多级索引的构造

除了使用set_index之外,
常用的有from_tuples, from_arrays, from_product三种方法,它们都是pd.MultiIndex对象下的函数。

  1. from_tuple
my_tuple = [('a','cat'),('a','dog'),('b','cat'),('b','dog')]
pd.MultiIndex.from_tuples(my_tuple, names=['First','Second'])
  1. from_arrays
    指根据传入列表中,对应层的列表进行构造:
my_array = [list('aabb'), ['cat', 'dog']*2]
pd.MultiIndex.from_arrays(my_array, names=['First','Second'])
  1. from_product
    指根据给定多个列表的笛卡尔积进行构造:
my_list1 = ['a','b']
my_list2 = ['cat','dog']
pd.MultiIndex.from_product([my_list1, my_list2], names=['First','Second'])

三、索引的常用方法

1. 索引层的交换和删除

构造一个三级索引

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

索引层的交换由swaplevel和reorder_levels完成,前者只能交换两个层,而后者可以交换任意层

df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一层和第三层交换
df_ex.reorder_levels([2,0,1],axis=0).head() # 列表数字指代原来索引中的层
若想要删除某一层的索引,可以使用droplevel方法:
df_ex.droplevel(1,axis=1)
df_ex.droplevel([0,1],axis=0)

2. 索引属性的修改

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

df_ex.rename_axis(index={'Upper':'Changed_row'}, 
columns={'Other':'Changed_Col'}).head()

如果是多级索引需要指定修改的层号level:

df_ex.rename(columns={'cat':'not_cat'}, level=2).head()

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

df_ex.rename(index=lambda x:str.upper(x), level=2).head()

3. 索引的设置与重置

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

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

4. 索引的变形

还有一个与reindex功能类似的函数是reindex_like,其功能是仿照传入的表索引来进行被调用表索引的变形。

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

四、索引运算

1. 集合的运算法则

NativeSearchQuery 根据根据索引名查询 根据索引名得到索引号_数据分析_03
NativeSearchQuery 根据根据索引名查询 根据索引名得到索引号_索引器_04
NativeSearchQuery 根据根据索引名查询 根据索引名得到索引号_多级_05
NativeSearchQuery 根据根据索引名查询 根据索引名得到索引号_索引器_06

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)
id1.union(id2)
id1.difference(id2)
id1.symmetric_difference(id2)

若两张表需要做集合运算的列并没有被设置索引,一种办法是先转成索引,运算后再恢复,另一种方法是利用isin函数,例如在重置索引的第一张表中选出id列交集的所在行

df_set_in_col_1[df_set_in_col_1.id1.isin(df_set_in_col_2.id2)]