MultiIndex

MultiIndex,即具有多个层次的索引,有些类似于根据索引进行分组的形式。通过多层次索引,我们就可以使用高层次的索引,来操作整个索引组的数据。

创建方式

第一种

我们在创建Series或DataFrame时,可以通过给index(columns)参数传递多维数组,进而构建多维索引。【数组中每个维度对应位置的元素,组成每个索引值】
多维索引的也可以设置名称(names属性),属性的值为一维数组,元素的个数需要与索引的层数相同(每层索引都需要具有一个名称)。

第二种

我们可以通过MultiIndex类的相关方法,预先创建一个MultiIndex对象,然后作为Series与DataFrame中的index(或columns)参数值。同时,可以通过names参数指定多层索引的名称。

  • from_arrays:接收一个多维数组参数,高维指定高层索引,低维指定底层索引。
  • from_tuples:接收一个元组的列表,每个元组指定每个索引(高维索引,低维索引)。
  • from_product:接收一个可迭代对象的列表,根据多个可迭代对象元素的笛卡尔积进行创建索引。

from_product相对于前两个方法而言,实现相对简单,但是,也存在局限。

import numpy as np
import pandas as pd
# 创建多层索引的第一种方式
# 创建Series对象,具有单层索引。
# s = pd.Series([1, 2, 3], index=["a", "b", "c"])
# 创建多层索引。多层索引需要一个二维的数组,每个元素(一维数组)来指定每个层级的索引。顺序
# 由高层(左边)到底层(右边)。
s = pd.Series([1, 2, 3, 4], index=[["a", "a", "b", "b"], ["c", "d", "e", "f"], ["m", "m", "k", "t"]])
# display(s)
df = pd.DataFrame(np.random.random(size=(4, 4)), index=[["上半年", "上半年", "下半年", "下半年"], 
            ["第一季度", "第二季度", "第三季度", "第四季度"]], 
            columns=[["水果", "水果", "蔬菜", "蔬菜"], ["苹果", "葡萄", "白菜", "萝卜"]])
# display(df)
# display(df.index)
# display(df.columns)
# 如果是单层索引,我们可以通过索引对象的name属性来设置索引的名称。
# s = pd.Series([1, 2, 3, 4], index=["a", "b", "c", "d"])
# s.index.name = "索引名称"
# 对于多层索引,也可以设置索引的名称,此时,设置名称的属性为names(通过一维数组来设置)。
# 每层索引都具有名称。
# s.index.names = ["x", "y", "z"]
# display(s)
df.index.names = ["年度", "季度"]
df.columns.names = ["大类别", "小类别"]
display(df)
# 创建多层索引的第二种方式
# from_arrays 参数为一个二维数组,每个元素(一维数组)来分别制定每层索引的内容。
# mindex = pd.MultiIndex.from_arrays([["上半年", "上半年", "下半年", "下半年"], ["1季度", "2季度", "3季度", "4季度"]])
# from_tuples 参数为一个(嵌套的)可迭代对象,元素为元祖类型。元祖的格式为:(高层索引内容,低层索引内容)
# mindex = pd.MultiIndex.from_tuples([("上半年", "1季度"), ("上半年", "2季度"), ("下半年", "3季度"), ("下半年", "4季度")])
# 使用笛卡尔积的方式来创建多层索引。参数为嵌套的可迭代对象。结果为使用每个一维数组中的元素与其他一维数组中的元素来生成
# 索引内容。
mindex = pd.MultiIndex.from_product([["a", "b"], ["c", "d"]], names=["outer", "inner"])
# pd.MultiIndex.from_arrays([["a", "a", "b", "b"], ["c", "d", "c", "d"]])
# pd.MultiIndex.from_arrays([["a", "a", "b"], ["c", "d", "d"]])
# mindex
# MultiIndex的三个类方法,可以创建MultiIndex类型的对象。三种方式相比,第三种方式(笛卡尔积的方式)更加简便,但是,
# 其也具有一定的局限:两两组合必须都存在,否则,就不能使用这种方式。
# 在创建多层索引对象时,可以通过names参数来指定每个索引层级的名称。

df = pd.DataFrame(np.random.random(size=(4, 4)),index=mindex)
# df.index

# 还有第三种方式(因为繁琐,所以不用),最直接的方式:
mindex = pd.MultiIndex(levels=[['a', 'b'], ['c', 'd']],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]],
           names=['outer', 'inner'])
mindex

多层索引操作

对于多层索引,同样也支持单层索引的相关操作,例如,索引元素,切片,索引数组选择元素等。我们也可以根据多级索引,按层次逐级选择元素。
多层索引的优势:通过创建多层索引,我们就可以使用高层次的索引,来操作整个索引组的数据。
格式:

  • s[操作]
  • s.loc[操作]
  • s.iloc[操作]

其中,操作可以是索引,切片,数组索引,布尔索引。

Series多层索引

  • 通过loc(标签索引)操作,可以通过多层索引,获取该索引所对应的一组值。
  • 通过iloc(位置索引)操作,会获取对应位置的元素值(与是否多层索引无关)。
  • 通过s[操作]的行为有些诡异,建议不用。
  • 对于索引(单级),首先按照标签选择,如果标签不存在,则按照位置选择。
  • 对于多级索引,则按照标签进行选择。
  • 对于切片,如果提供的是整数,则按照位置选择,否则按照标签选择。
  • 对于数组索引, 如果数组元素都是整数,则根据位置进行索引,否则,根据标签进行索引(此时如果标签不存在,也不会出现错误)。
s = pd.Series([1, 2, 3, 4], index=[["a", "a", "b", "b"], ["c", "d", "e", "f"]])
display(s)
# 多层索引的优势,可以一次获取一组元素(值)
# s.loc["a"]
# 也可以沿着索引层次进行访问。
# s.loc["a", "d"
# 通过位置索引访问元素,与多层索引没有任何关系。
# s.iloc[0]

# 切片
# s.loc["a":"b"]
s.iloc[0:2]

DataFrame多层索引

  • 通过loc(标签索引)操作,可以通过多层索引,获取该索引所对应的一组值。
  • 通过iloc(位置索引)操作,会获取对应位置的一行(与是否多层索引无关)。
  • 通过s[操作]的行为有些诡异,建议不用。
  • 对于索引,根据标签获取相应的列(如果是多层索引,则可以获得多列)。
  • 对于数组索引, 根据标签,获取相应的列(如果是多层索引,则可以获得多列)。
  • 对于切片,首先按照标签进行索引,然后再按照位置进行索引(取行)。
df = pd.DataFrame(np.random.random(size=(4, 4)), index=[["a", "a", "b", "b"], ["c", "d", "c", "d"]],
                 columns=[["a", "a", "b", "b"], ["c", "d", "c", "d"]])
display(df)
# df.loc["a"]
# df.loc["a", "c"]
# df["a", "d"]

# 通过位置访问元素与是否多层索引无关。
df.iloc[0]

交换索引

我们可以调用DataFrame对象的swaplevel方法来交换两个层级索引。该方法默认对倒数第2层与倒数第1层进行交换。我们也可以指定交换的层。层次从0开始,由外向内递增(或者由上到下递增),也可以指定负值,负值表示倒数第n层。除此之外,我们也可以使用层次索引的名称来进行交换。

索引排序

我们可以使用sort_index方法对索引进行排序处理。

  • level:指定根据哪一层进行排序,默认为最外(上)层。该值可以是数值,索引名,或者是由二者构成的列表。
  • inplace:是否就地修改。默认为False。
# 交换索引的层级,可以以一种不同的方式来进行展示(统计)。
df = pd.DataFrame(np.random.random(size=(4, 4)), index=[["a", "a", "b", "b"], 
            ["north", "north", "south", "south"], [2017, 2018, 2017, 2018]])
display(df)
# 层级:从外层到内层,值为0, 1, 2……,同时,层级也可以为负值,表示倒数第n个层级(由内层到外层)。
# 例如,-1表示最内层。
# 如果没有显式指定交换的层级,则默认交换最内层的两个层级。
# df = df.swaplevel(0, 2)
# 索引名字的作用。
# 除了数值来指定索引的层级外,我们也可以通过索引的名字来指定索引的层级。
df.index.names = ["x", "area", "year"]
df.swaplevel("area", "year")
# display(df)
# df.sort_index()
# 层级索引的排序
df = pd.DataFrame(np.random.random(size=(4, 4)), index=[["b", "a", "c", "c"], 
            ["c", "y", "k", "k"], [3, -2, 5, 2]])
display(df)
# 在对索引进行排序时,可以通过level参数指定索引的层级(排序的层级)。
# 如果没有显式指定,则默认为最外层的层级(层级为0)。
# 当我们对某个层级进行排序时,该层级的所有内层层级也会进行排序。
# df.sort_index()
df.sort_index(level=1)

索引堆叠

通过DataFrame对象的stack方法,可以进行索引堆叠,即将指定层级的列转换成行。
level:指定转换的层级,默认为-1。

取消堆叠

通过DataFrame对象的unstack方法,可以取消索引堆叠,即将指定层级的行转换成列。
level:指定转换的层级,默认为-1。
fill_value:指定填充值。默认为NaN。

df = pd.DataFrame(np.random.random(size=(4, 4)), index=[["a", "a", "b", "b"], ["c", "d", "c", "d"]],
                columns=[["x", "x", "y", "y"], ["m", "n", "m", "n"]])
# display(df)
# 索引堆叠:列索引变成行索引。
# df.stack()
# 可以通过level参数指定堆叠的层级,默认为-1(最里面的层级)。
# df.stack(0)

df = pd.DataFrame(np.random.random(size=(3, 4)), index=[["a", "a", "b"], ["c", "d", "c"]],
                columns=[["x", "x", "y", "y"], ["m", "n", "m", "n"]])
display(df)
# 索引取消堆叠:行索引变成列索引
# df.unstack(level=-1)
# df.stack()
# 如果我们的索引不是一一对应的,在堆叠或者取消迭代的时候,就可能会产生空值NaN。我们
# 可以使用fill_value参数来填充空值。
df.unstack(level=-1, fill_value=0)

设置索引

在DataFrame中,如果我们需要将现有的某一(几)列作为索引列,可以调用set_index方法来实现。

  • drop:是否丢弃作为新索引的列,默认为True。
  • append:是否以追加的方式设置索引,默认为False。
  • inplace:是否就地修改,默认为False。

重置索引

调用在DataFrame对象的reset_index,可以重置索引。该操作与set_index正好相反。

  • level:重置索引的层级,默认重置所有层级的索引。如果重置所有索引,将会创建默认整数序列索引。
  • drop:是否丢弃重置的索引列,默认为False。
  • inplace:是否就地修改,默认为False。
# 设置索引
# 我们可以将当前的DataFrame中的一列(或多列)设置为索引列。
df = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=["a", "b", "c"])
display(df)
# 将当前的列(a)设置为索引列。
# df.set_index("a")
# 将当前的多列设置为索引列(层级索引)
# df.set_index(["a", "c"])
# 默认情况下,设置为索引的列,会从当前DataFrame中删除,我们可以通过drop
# 参数来进行设置,是否删除,默认为True。
# df.set_index("a", drop=False)
# 默认,我们设置的索引列,会替换掉之前的索引,我们可以通过append参数来设置
# 是否替换(追加)。默认为False(替换)。
# df.set_index("a", append=True)
# 重置索引。重置索引可以看做是设置索引的逆运算。
# 设置索引,是将当前列去充当索引。而重置索引是将索引充当列。
df = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=["a", "b", "c"])
df.set_index(["a", "b"], inplace=True)
display(df)
# 重置索引
# df.reset_index()
# 重置索引时,默认重置所有的层级。我们可以通过level参数指定重置的索引层级。
# 如果重置了所有的层级(或者重置索引后,当前的DataFrame中已没有索引列),则会
# 自动生成从0开始,增量为1的索引。
# df.reset_index(level=1)
# df.reset_index(level="b")
# 默认情况下,当重置索引时,被重置的索引会返回到列中,我们可以通过drop参数来设置是否回到列中,默认为False。
df = df.reset_index(level="b", drop=True)