前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据分析索引总结(中)Pandas多级索引

数据分析索引总结(中)Pandas多级索引

作者头像
Datawhale
修改2020-06-04 12:01:48
4.5K0
修改2020-06-04 12:01:48
举报
文章被收录于专栏:Datawhale专栏Datawhale专栏

作者:闫钟峰,Datawhale优秀学习者

寄语:本文介绍了创建多级索引、多层索引切片、多层索引中的slice对象、索引层的交换等内容。

创建多级索引

1. 通过from_tuple或from_arrays
① 直接从元组列表创建多重索引
代码语言:javascript
复制
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
② 利用zip创建元组

多重索引本质上的结构是一个由元组构成的list

代码语言:javascript
复制
L1 = list('AABB')
L2 = list('abab')
tuples = list(zip(L1,L2))
mul_index = pd.MultiIndex.from_tuples(tuples, names=('Upper', 'Lower'))
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index)

注意,如果用于创建多重索引的由tuple组成的list本身是未排序的, 那么创建的df也未排序。

代码语言:javascript
复制
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=pd.MultiIndex.from_tuples(list(zip(L2,L1)), names=('Lower', 'Upper')))

为了便于使用, 可以使用sort_index()进行排序

代码语言:javascript
复制
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=pd.MultiIndex.from_tuples(list(zip(L2,L1)), names=('Lower', 'Upper'))).sort_index()
③ 通过Array(或列表构成的列表)创建
内层的list会自动转成元组
代码语言:javascript
复制
arrays = [['A','a'],['A','b'],['B','a'],['B','b']]
mul_index = pd.MultiIndex.from_tuples(arrays, names=('Upper', 'Lower'))
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index)
代码语言:javascript
复制
arrays = [['A','a'],['B','a'],['A','b'],['B','b']]
mul_index = pd.MultiIndex.from_tuples(arrays, names=('Upper', 'Lower'))
pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index)

尽管多重索引内部是个由tuple组成的list, 但由于顺序不同, 并不能视为相等的多重索引。但直接比较两个顺序不同的多重索引, 返回值是一个布尔值array, 并不如预期的那样。

代码语言:javascript
复制
sorted_multi_index=pd.DataFrame({'Score':['perfect','good','fair','bad']},index=mul_index).sort_index().index
sorted_multi_index==mul_index
如果是两个list, 改变顺序后与原始list相比较, 返回值只有一个 False。
代码语言:javascript
复制
[('A', 'a'),  ('B', 'a'), ('A', 'b'), ('B', 'b')]==[('A', 'a'),  ('A', 'b'), ('B', 'a'), ('B', 'b')]
创建一个由二元list构成的list
代码语言:javascript
复制
arr=np.random.randint(1,5,20).reshape(-1,2)
必须把array转化为list才能用pd.MultiIndex.from_tuples 函数创建层次化索引。
代码语言:javascript
复制
pd.MultiIndex.from_tuples(list(arr),names=('left','right'))
使用上述多重索引创建df后,要记得多加一个sort_index(), 以使得df的结果看起来更整齐。
代码语言:javascript
复制
dftemp=pd.DataFrame(np.random.randn(20).reshape(10,2), index=pd.MultiIndex.from_tuples(list(arr),names=('left','right'))).sort_index()
多重索引构造器
代码语言:javascript
复制
pd.MultiIndex.from_tuples??
# tuples: list / sequence of tuple-likes Each tuple is the index of one row/column.

2. 通过from_product

笛卡尔乘积---可能很多时候并不需要用笛卡儿积的所有结果作为索引。
代码语言:javascript
复制
L1 = ['A','B']
L2 = ['a','b']
pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))
代码语言:javascript
复制
Make a MultiIndex from the cartesian product of multiple iterables.
# iterables : list / sequence of iterables  Each iterable has unique labels for each level of the index.
pd.MultiIndex.from_product??

3. 指定df中的列创建(set_index方法)

传入两个以上的列名时,必须以list的形式传入(tuple不行)。注意原来的索引'ID'已经被丢弃了--这是因为set_index的 drop 参数默认值 drop=True。
代码语言:javascript
复制
df_using_mul = df.set_index(['Class','Address']) 
df_using_mul.head()
由于drop参数默认值是True,上述语法并不等价于分别将两列设置为索引。
代码语言:javascript
复制
df1= df.set_index('Class')
第二次将某列设置为索引时,会丢弃原来的索引
代码语言:javascript
复制
df2 = df1.set_index('Address')
第二次指定索引时,必须指定参数 append=True 才会保留原来的索引---这个参数默认是False(丢弃原始索引)。
代码语言:javascript
复制
df3= df1.set_index('Address',append=True)
代码语言:javascript
复制
df.set_index??

多层索引切片

使用第一层的索引,会把该索引下的所有行都选中,除非该索引的二级索引只有一个,否则返回行数不会等于一行。
代码语言:javascript
复制
df_using_mul.loc['C_1']
如何获取次级索引为指定值的行??
方法1:交换索引层级
代码语言:javascript
复制
df_using_mul.swaplevel('Class','Address').loc['street_1']
方法2: 使用针对索引的 get_level_values 函数,指定索引层级为第二层
代码语言:javascript
复制
df_using_mul.loc[df_using_mul.index.get_level_values(1) == 'street_1']
代码语言:javascript
复制
df_using_mul.index.names
df_using_mul.index.get_level_values??
方法3:使用query方法,传入 次级索引名称等于指定值--需要使用引号
代码语言:javascript
复制
df_using_mul.query('Address == "street_1"')
相当于将内层索引当作列,等价于
代码语言:javascript
复制
select * from df_using_mul where Address = 'street_1'
对原始df使用query可以获取同样的行
代码语言:javascript
复制
df.query('Address=="street_1"')
方法4:使用pd.IndexSlice对层次索引按次级索引的值进行切片
代码语言:javascript
复制
df_using_mul.loc(axis=0)[pd.IndexSlice[:, 'street_1']]
代码语言:javascript
复制
pd.IndexSlice??

1. 一般切片

当索引不排序时,单个索引会报出性能警告
代码语言:javascript
复制
df_using_mul.loc['C_2','street_5']
该函数检查索引是否排序
代码语言:javascript
复制
df_using_mul.index.is_lexsorted()
根据索引排序后不再报出性能警告
代码语言:javascript
复制
df_using_mul.sort_index().loc['C_2','street_5']
当不排序时,不能使用多层切片
代码语言:javascript
复制
df_using_mul.loc[('C_2','street_5'):] 报错
故先要进行排序,注意此处由于使用了loc,因此仍然包含右端点
代码语言:javascript
复制
df_using_mul.sort_index().loc[('C_2','street_6'):('C_3','street_4')]
使用索引标签进行切片, 是个闭区间非元组也是合法的,表示选中该层所有元素
代码语言:javascript
复制
df_using_mul.sort_index().loc[('C_2','street_7'):'C_3'].head()
df_using_mul.sort_index().loc['C_1':'C_2']#.head(10)
2. 第一类特殊情况:由元组构成列表
选出某几个元素,每个元组的第一个元素是第一层索引的可能取值,元组的第二个元素是第二层索引的可能取值...精确到最内层索引
代码语言:javascript
复制
df_using_mul.sort_index().loc[[('C_2','street_7'),('C_3','street_2')]]
如果第一个本应该是元组的地方传入了一个元素, 不会报错, 但结果与预期不一样。
代码语言:javascript
复制
df_using_mul.sort_index().loc[['C_2',('C_3','street_2')]]
但是反过来, 第一个传入元组, 第二个传入一个元素, 就会报错TypeError: Expected tuple, got str。
所以这里大概是有一个自动推断的过程:如果第一个位置是元组,那就默认是按照元组的相应位置去对应相应层级的索引的值;如果第一个位置是元素, 那就默认直接对应第一层索引的相应取值。
代码语言:javascript
复制
df_using_mul.sort_index().loc[[('C_2','street_7'),'C_3']]
上边不会报错,但结果与预期不一样,是由于第一层索引没有以元组('C_3','street_2')为索引的。
使用元素和元组组成的切片时, 就不会报错了, 但这时候需注意传入的切片不应该再包含在[]内。
代码语言:javascript
复制
df_using_mul.sort_index().loc['C_2':('C_3','street_2')]
3. 第二类特殊情况:由列表构成元组
选出第一层在‘C_2’和'C_3'中且第二层在'street_4'和'street_7'中的行。
代码语言:javascript
复制
df_using_mul.sort_index().loc[(['C_2','C_3'],['street_4','street_7']),:]
注意,loc方法必须是先选行再选列,因此列表构成的元组后的逗号和冒号不能省略。
代码语言:javascript
复制
df_using_mul.sort_index().loc[(['C_2','C_3'],['street_4','street_7'])]
# KeyError: "None of [Index(['street_4', 'street_7'], dtype='object')] are in the [columns]"
下面语句不等价于使用zip将两个list绑定,而是等价于将两个list做笛卡尔乘积。
代码语言:javascript
复制
df_using_mul.sort_index().loc[zip(['C_2','C_3'],['street_4','street_7']),:]
定义一个为两个list做笛卡尔乘积的函数
代码语言:javascript
复制
def list_product(list1,list2):
    lst=[]
    for a in list1:
        for b in list2:
            lst.append((a,b))
    return lst
实际上, 传入两个列表,相当于将两个list做笛卡尔乘积--这说明两个列表并不需要长度相等。
代码语言:javascript
复制
df_using_mul.sort_index().loc[list_product(['C_2','C_3'], ['street_4','street_7']),:]
传入两个不等长的list的笛卡尔乘积
代码语言:javascript
复制
df_using_mul.sort_index().loc[list_product(['C_2','C_3'], ['street_1','street_4','street_7']),:]
和传入两个list组成的元组效果是一致的。
代码语言:javascript
复制
df_using_mul.sort_index().loc[(['C_2','C_3'], ['street_1','street_4','street_7']),:]

多层索引中的slice对象

行索引和列索引均有两个层级
代码语言:javascript
复制
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_s = pd.DataFrame(np.random.rand(9,9),index=mul_index1,columns=mul_index2)
df_s
代码语言:javascript
复制
# Create an object to more easily perform multi-index slicing.
pd.IndexSlice??
索引Slice的使用非常灵活
df_s.sum()默认为对列求和,因此返回一个长度为9的数值列表。
代码语言:javascript
复制
df_s.loc[pd.IndexSlice['B':,df_s['D']['d']>0.3],pd.IndexSlice[df_s.sum()>4]]
# 对行的筛选等价于 select * from (select * from df_s where (Upper>'B' or D_d>0.3) )
# 如果不使用连接等手段, sql无法实现类似的对列名的筛选---特别地,sql中没有层级索引
接下来使用pd.IndexSlice函数找出那些列的和大于4的列,分解开来看--列的筛选。
代码语言:javascript
复制
pd.IndexSlice[df_s.sum()>4]
分解开来看--行的筛选,注意观察发现,最终结果没有第一次行索引为A的, 但下边的结果中第一层索引为A的有等于True的--这是因为前边还有个slice,表示的是前边的从B开始的切片。
代码语言:javascript
复制
pd.IndexSlice['B':,df_s['D']['d']>0.3]
去掉切片
代码语言:javascript
复制
pd.IndexSlice[df_s['D']['d']>0.3]
最终,被传递给loc的是两个布尔值的Series,和原始的Series对齐,然后根据布尔值是否为真筛选出最终的结果。
代码语言:javascript
复制
df_s.loc[pd.IndexSlice['B':,df_s['D']['d']>0.3],pd.IndexSlice[df_s.sum()>4]]

索引层的交换

1. swaplevel方法(两层交换)
代码语言:javascript
复制
df_using_mul.head()
交换索引后, 使用sort_index使得显示结果更加整齐。
代码语言:javascript
复制
df_using_mul.swaplevel(i=1,j=0,axis=0).sort_index().head()
代码语言:javascript
复制
# Swap levels i and j in a MultiIndex on a particular axis.
# 有必要增加一个sort_index=True的参数, 使得可以通过该参数设置交换索引后是否按索引重新排序
df_using_mul.swaplevel??
2. reorder_levels方法(多层交换)
代码语言:javascript
复制
df_muls = df.set_index(['School','Class','Address'])
df_muls.head(10)
传入的第一个参数是一个类list的对象, 是原来索引层级(用默认整数表示)的一个排列。第二个参数指定需要重排索引的轴,0表示行轴,也就是重排行索引的层级。
代码语言:javascript
复制
df_muls.reorder_levels([2,0,1],axis=0).sort_index().head()
如果索引有name,可以直接使用name的一个排列
代码语言:javascript
复制
df_muls.reorder_levels(['Address','School','Class'],axis=0).sort_index().head()
代码语言:javascript
复制
# Rearrange index levels using input order. May not drop or duplicate levels.
# 注意后一句话--这使得该函数和df的类似函数 reindex 相比功能更弱
df_muls.reorder_levels??
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Datawhale 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建多级索引
    • 1. 通过from_tuple或from_arrays
      • ① 直接从元组列表创建多重索引
        • ② 利用zip创建元组
          • ③ 通过Array(或列表构成的列表)创建
            • 内层的list会自动转成元组
              • 尽管多重索引内部是个由tuple组成的list, 但由于顺序不同, 并不能视为相等的多重索引。但直接比较两个顺序不同的多重索引, 返回值是一个布尔值array, 并不如预期的那样。
                • 如果是两个list, 改变顺序后与原始list相比较, 返回值只有一个 False。
                • 创建一个由二元list构成的list
                • 必须把array转化为list才能用pd.MultiIndex.from_tuples 函数创建层次化索引。
                • 使用上述多重索引创建df后,要记得多加一个sort_index(), 以使得df的结果看起来更整齐。
                • 多重索引构造器
                • 笛卡尔乘积---可能很多时候并不需要用笛卡儿积的所有结果作为索引。
                • 传入两个以上的列名时,必须以list的形式传入(tuple不行)。注意原来的索引'ID'已经被丢弃了--这是因为set_index的 drop 参数默认值 drop=True。
                • 由于drop参数默认值是True,上述语法并不等价于分别将两列设置为索引。
                • 第二次将某列设置为索引时,会丢弃原来的索引
                • 第二次指定索引时,必须指定参数 append=True 才会保留原来的索引---这个参数默认是False(丢弃原始索引)。
                • 使用第一层的索引,会把该索引下的所有行都选中,除非该索引的二级索引只有一个,否则返回行数不会等于一行。
                • 如何获取次级索引为指定值的行??
                • 方法1:交换索引层级
                • 方法2: 使用针对索引的 get_level_values 函数,指定索引层级为第二层
                • 方法3:使用query方法,传入 次级索引名称等于指定值--需要使用引号
                • 相当于将内层索引当作列,等价于
                • 对原始df使用query可以获取同样的行
                • 方法4:使用pd.IndexSlice对层次索引按次级索引的值进行切片
                • 当索引不排序时,单个索引会报出性能警告
                • 该函数检查索引是否排序
                • 根据索引排序后不再报出性能警告
                • 当不排序时,不能使用多层切片
                • 故先要进行排序,注意此处由于使用了loc,因此仍然包含右端点
                • 使用索引标签进行切片, 是个闭区间非元组也是合法的,表示选中该层所有元素
                • 2. 第一类特殊情况:由元组构成列表
                • 选出某几个元素,每个元组的第一个元素是第一层索引的可能取值,元组的第二个元素是第二层索引的可能取值...精确到最内层索引
                • 如果第一个本应该是元组的地方传入了一个元素, 不会报错, 但结果与预期不一样。
                • 但是反过来, 第一个传入元组, 第二个传入一个元素, 就会报错TypeError: Expected tuple, got str。
                • 所以这里大概是有一个自动推断的过程:如果第一个位置是元组,那就默认是按照元组的相应位置去对应相应层级的索引的值;如果第一个位置是元素, 那就默认直接对应第一层索引的相应取值。
                • 上边不会报错,但结果与预期不一样,是由于第一层索引没有以元组('C_3','street_2')为索引的。
                • 使用元素和元组组成的切片时, 就不会报错了, 但这时候需注意传入的切片不应该再包含在[]内。
                • 3. 第二类特殊情况:由列表构成元组
                • 选出第一层在‘C_2’和'C_3'中且第二层在'street_4'和'street_7'中的行。
                • 注意,loc方法必须是先选行再选列,因此列表构成的元组后的逗号和冒号不能省略。
                • 下面语句不等价于使用zip将两个list绑定,而是等价于将两个list做笛卡尔乘积。
                • 定义一个为两个list做笛卡尔乘积的函数
                • 实际上, 传入两个列表,相当于将两个list做笛卡尔乘积--这说明两个列表并不需要长度相等。
                • 传入两个不等长的list的笛卡尔乘积
                • 和传入两个list组成的元组效果是一致的。
                • 行索引和列索引均有两个层级
                • 索引Slice的使用非常灵活
                • df_s.sum()默认为对列求和,因此返回一个长度为9的数值列表。
                • 接下来使用pd.IndexSlice函数找出那些列的和大于4的列,分解开来看--列的筛选。
                • 分解开来看--行的筛选,注意观察发现,最终结果没有第一次行索引为A的, 但下边的结果中第一层索引为A的有等于True的--这是因为前边还有个slice,表示的是前边的从B开始的切片。
                • 去掉切片
                • 最终,被传递给loc的是两个布尔值的Series,和原始的Series对齐,然后根据布尔值是否为真筛选出最终的结果。
                • 1. swaplevel方法(两层交换)
                • 交换索引后, 使用sort_index使得显示结果更加整齐。
                • 2. reorder_levels方法(多层交换)
                • 传入的第一个参数是一个类list的对象, 是原来索引层级(用默认整数表示)的一个排列。第二个参数指定需要重排索引的轴,0表示行轴,也就是重排行索引的层级。
                • 如果索引有name,可以直接使用name的一个排列
            • 2. 通过from_product
            • 多层索引切片
            • 多层索引中的slice对象
            • 索引层的交换
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档