1. 程式人生 > 實用技巧 >MultiIndex 層次化索引(hierarchical indexing)

MultiIndex 層次化索引(hierarchical indexing)

層次化索引(hierarchical indexing)在一個軸上擁有多個(兩個以上)索引級別,使使用者能以低維度形式處理高維度資料。

levels:每個等級上軸標籤的唯一值
labels:以整數來表示每個level上標籤的位置
sortorder:按照指定level上的標籤名稱的字典順序進行排序(可選引數)
names:index level的名稱
copy:布林值,預設為False。是否拷貝元資料產生新的物件
verify_integrity:布林值,預設為Ture。檢查levels/labels是否持續有效

入門級demo學習

import pandas as pd
import numpy as np
data = pd.Series(np.random.randn(10),
              index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd', 'd'],
                     [1, 2, 3, 1, 2, 3, 1, 2, 2, 3]])
print("----------print data---------------")
print(data)

a  1   -0.902378
   2   -1.512923
   3   -1.082350
b  1   -0.900975
   2   -1.723988
   3   -0.791613
c  1   -1.631530
   2    2.290227
d  2    0.530892
   3    1.199453
dtype: float64

print("----------print data.index-------------")
print(data.index)

MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]],
codes=[[0, 0, 0, 1, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 1, 2]])

print("----------print data.index.levels[0]----------------")
print(data.index.levels[0])

Index(['a', 'b', 'c', 'd'], dtype='object')

print("----------print data.index.levels[1]----------------")
print(data.index.levels[1])

Int64Index([1, 2, 3], dtype='int64')

總結分析如下:
level中的唯一標籤值集合分別為[‘a’, ‘b’, ‘c’, ‘d’]和[1, 2, 3]。
data.index.levels[0]上的標籤abcd對應的索引為0123。data.index.levels[1]上的標籤123對應的索引為012。
外層level的label值[0, 0, 0, 1, 1, 1, 2, 2, 3, 3]表示對應的標籤值分別出現幾次:a和b為3次,c和d為2次
內層level的label值[0, 1, 2, 0, 1, 2, 0, 1, 1, 2]按個數與外層label相對應。例如:外層a出現3次,則內層label的前3個值與a相對應,這三個索引值為0,1,2,分別對應1,2,3。
**MultiIndex建立的方式**
第一種
我們在建立Series或DataFrame時,可以通過給index(columns)引數傳遞多維陣列,進而構建多維索引。【陣列中每個維度對應位置的元素,組成每個索引值】
多維索引的也可以設定名稱(names屬性),屬性的值為一維陣列,元素的個數需要與索引的層數相同(每層索引都需要具有一個名稱)。

import pandas as pd
import numpy as np

建立多層索引的第一種方式

建立Series物件,具有單層索引。

s1 = pd.Series([1, 2, 3], index=["a", "b", "c"])
print(s1)

a 1
b 2
c 3
dtype: int64


# 建立多層索引。多層索引需要一個二維的陣列,每個元素(一維陣列)來指定每個層級的索引。順序
# 由高層(左邊)到底層(右邊)。

s2 = pd.Series([1, 2, 3, 4], index=[["a", "a", "b", "b"], ["c", "d", "e", "f"], ["m", "m", "k", "t"]])
print(s2)

a c m 1
d m 2
b e k 3
f t 4
dtype: int64

print("-------df--------")
df = pd.DataFrame(np.random.random(size=(4, 4)), index=[["上半年", "上半年", "下半年", "下半年"],
["第一季度", "第二季度", "第三季度", "第四季度"]],
columns=[["水果", "水果", "蔬菜", "蔬菜"], ["蘋果", "葡萄", "白菜", "蘿蔔"]])
print(df)

                水果                  蔬菜          
                蘋果        葡萄        白菜        蘿蔔
上半年       第一季度  0.356637  0.358602  0.402864  0.550727
            第二季度  0.963110  0.010293  0.378511  0.051015
下半年       第三季度  0.098882  0.394281  0.554502  0.676566
             第四季度  0.828770  0.506423  0.681128  0.542206

print(df.index)

MultiIndex(levels=[['上半年', '下半年'], ['第一季度', '第三季度', '第二季度', '第四季度']],
codes=[[0, 0, 1, 1], [0, 2, 1, 3]])

print(df.columns)

MultiIndex(levels=[['水果', '蔬菜'], ['白菜', '蘋果', '蘿蔔', '葡萄']],
codes=[[0, 0, 1, 1], [1, 3, 0, 2]])

如果是單層索引,我們可以通過索引物件的name屬性來設定索引的名稱。

s2 = pd.Series([1, 2, 3, 4], index=["a", "b", "c", "d"])
s2.index.name = "索引名稱"
print("--------s2----------")
print(s2)

索引名稱
a 1
b 2
c 3
d 4
dtype: int64

對於多層索引,也可以設定索引的名稱,此時,設定名稱的屬性為names(通過一維陣列來設定)。

每層索引都具有名稱。

修改df的索引

df.index.names = ["年度", "季度"]
df.columns.names = ["大類別", "小類別"]
print(df)

大類別 水果 蔬菜
小類別 蘋果 葡萄 白菜 蘿蔔
年度 季度
上半年 第一季度 0.078253 0.961293 0.770540 0.267522
第二季度 0.845138 0.239290 0.208779 0.347256
下半年 第三季度 0.869534 0.148100 0.046563 0.753004
第四季度 0.926966 0.305344 0.379041 0.467218


第二種
我們可以通過MultiIndex類的相關方法,預先建立一個MultiIndex物件,然後作為Series與DataFrame中的index(或columns)引數值。同時,可以通過names引數指定多層索引的名稱。
from_arrays:接收一個多維陣列引數,高維指定高層索引,低維指定底層索引。
from_tuples:接收一個元組的列表,每個元組指定每個索引(高維索引,低維索引)。
from_product:接收一個可迭代物件的列表,根據多個可迭代物件元素的笛卡爾積進行建立索引。
from_product相對於前兩個方法而言,實現相對簡單,但是,也存在侷限。

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.random(size=(4, 4)), index=[["上半年", "上半年", "下半年", "下半年"],
["第一季度", "第二季度", "第三季度", "第四季度"]],
columns=[["水果", "水果", "蔬菜", "蔬菜"], ["蘋果", "葡萄", "白菜", "蘿蔔"]])
print(df)

                水果                  蔬菜          
                蘋果        葡萄        白菜        蘿蔔
上半年 第一季度  0.645330  0.408014  0.121088  0.106337
       第二季度  0.671892  0.212711  0.792635  0.031329
下半年 第三季度  0.063841  0.513930  0.342464  0.885220
      第四季度  0.091936  0.745129  0.905859  0.760001

建立多層索引的第二種方式

------------from_arrays-------------------

from_arrays 引數為一個二維陣列,每個元素(一維陣列)來分別制定每層索引的內容。

mindex_arrsys = pd.MultiIndex.from_arrays([["上半年", "上半年", "下半年", "下半年"], ["1季度", "2季度", "3季度", "4季度"]])
df2 = pd.DataFrame(np.random.random(size=(4, 4)), index=mindex_arrsys)
print(df2)

                0         1         2         3
上半年 1季度  0.578044  0.636138  0.497155  0.389131
       2季度  0.195453  0.623200  0.769118  0.637451
下半年 3季度  0.562462  0.629691  0.684193  0.981682
      4季度  0.873525  0.489149  0.883518  0.252548

------------from_tuples-------------------

from_tuples 引數為一個(巢狀的)可迭代物件,元素為元祖型別。元祖的格式為:(高層索引內容,低層索引內容)

mindex_tuples = pd.MultiIndex.from_tuples([("上半年", "1季度"), ("上半年", "2季度"), ("下半年", "3季度"), ("下半年", "4季度")])
df3 = pd.DataFrame(np.random.random(size=(4, 4)), index=mindex_tuples)
print(df3)

                0         1         2         3
上半年 1季度  0.843825  0.242793  0.132814  0.024581
    2季度  0.404961  0.870869  0.134744  0.220976
下半年 3季度  0.196361  0.074073  0.588173  0.181438
    4季度  0.936489  0.246351  0.941209  0.144210

------------from_products-------------------

使用笛卡爾積的方式來建立多層索引。引數為巢狀的可迭代物件。結果為使用每個一維陣列中的元素與其他一維陣列中的元素來生成

索引內容。

mindex_products = pd.MultiIndex.from_product([["a", "b"], ["c", "d"]], names=["outer", "inner"])
print(mindex_products)

MultiIndex(levels=[['a', 'b'], ['c', 'd']],
           codes=[[0, 0, 1, 1], [0, 1, 0, 1]],
           names=['outer', 'inner'])

print(pd.MultiIndex.from_arrays([["a", "a", "b", "b"], ["c", "d", "c", "d"]]))

MultiIndex(levels=[['a', 'b'], ['c', 'd']],
           codes=[[0, 0, 1, 1], [0, 1, 0, 1]])

print(pd.MultiIndex.from_arrays([["a", "a", "b"], ["c", "d", "d"]]))

MultiIndex(levels=[['a', 'b'], ['c', 'd']],
           codes=[[0, 0, 1], [0, 1, 1]])

MultiIndex的三個類方法,可以建立MultiIndex型別的物件。三種方式相比,第三種方式(笛卡爾積的方式)更加簡便,但是,

其也具有一定的侷限:兩兩組合必須都存在,否則,就不能使用這種方式。

在建立多層索引物件時,可以通過names引數來指定每個索引層級的名稱。

df4 = pd.DataFrame(np.random.random(size=(4, 4)),index=mindex_products)
print(df4)
print(df4.index)

                    0         1         2         3
outer inner                                        
a     c      0.213218  0.561547  0.224423  0.764169
      d      0.296970  0.557486  0.809295  0.300886
b     c      0.134809  0.111138  0.619714  0.223240
      d      0.707181  0.872395  0.800698  0.676075
MultiIndex(levels=[['a', 'b'], ['c', 'd']],
           codes=[[0, 0, 1, 1], [0, 1, 0, 1]],
           names=['outer', 'inner'])

還有第三種方式(因為繁瑣,所以不用),最直接的方式:

mindex = pd.MultiIndex(levels=[['a', 'b'], ['c', 'd']],
codes=[[0, 0, 1, 1], [0, 1, 0, 1]],
names=['outer', 'inner'])
print(mindex)

MultiIndex(levels=[['a', 'b'], ['c', 'd']],
           codes=[[0, 0, 1, 1], [0, 1, 0, 1]],
           names=['outer', 'inner'])

多層索引操作
對於多層索引,同樣也支援單層索引的相關操作,例如,索引元素,切片,索引陣列選擇元素等。我們也可以根據多級索引,按層次逐級選擇元素。
多層索引的優勢:通過建立多層索引,我們就可以使用高層次的索引,來操作整個索引組的資料。
格式:
s[操作]
s.loc[操作]
s.iloc[操作]
其中,操作可以是索引,切片,陣列索引,布林索引。
Series多層索引
通過loc(標籤索引)操作,可以通過多層索引,獲取該索引所對應的一組值。
通過iloc(位置索引)操作,會獲取對應位置的元素值(與是否多層索引無關)。
通過s[操作]的行為有些詭異,建議不用。
對於索引(單級),首先按照標籤選擇,如果標籤不存在,則按照位置選擇。
對於多級索引,則按照標籤進行選擇。
對於切片,如果提供的是整數,則按照位置選擇,否則按照標籤選擇。
對於陣列索引, 如果陣列元素都是整數,則根據位置進行索引,否則,根據標籤進行索引(此時如果標籤不存在,也不會出現錯誤)。

!/usr/bin/python3

-- coding: utf-8 --

@Time : 2019-06-13 11:28

@Author : LiYahui

@Description : 位置選擇

import pandas as pd

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

a  c    1
   d    2
b  e    3
   f    4
dtype: int64

多層索引的優勢,可以一次獲取一組元素(值)

print(s.loc["a"])

c    1
d    2
dtype: int64

也可以沿著索引層次進行訪問。

print(s.loc["a", "d"]) # 訪問指定座標的元素值

2

通過位置索引訪問元素,與多層索引沒有任何關係。

print(s.iloc[0])

1

切片

print(s.loc["a":"b"])

a  c    1
   d    2
b  e    3
   f    4
dtype: int64

行index上的序號型別的切片

print(s.iloc[0:2])

a  c    1
   d    2
b  e    3
   f    4
dtype: int64

DataFrame多層索引
通過loc(標籤索引)操作,可以通過多層索引,獲取該索引所對應的一組值。
通過iloc(位置索引)操作,會獲取對應位置的一行(與是否多層索引無關)。
通過s[操作]的行為有些詭異,建議不用。
對於索引,根據標籤獲取相應的列(如果是多層索引,則可以獲得多列)。
對於陣列索引, 根據標籤,獲取相應的列(如果是多層索引,則可以獲得多列)。
對於切片,首先按照標籤進行索引,然後再按照位置進行索引(取行)。

!/usr/bin/python3

-- coding: utf-8 --

@Time : 2019-06-13 11:28

@Author : LiYahui

@Description : 位置選擇

import pandas as pd
data=[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
df=pd.DataFrame(data)
print(df)

    0   1   2   3
0   1   2   3   4
1   5   6   7   8
2   9  10  11  12
3  13  14  15  16

指定行索引

df.index=[["a", "a", "b", "b"], ["c", "d", "c", "d"]]

指定列索引

df.columns=[["x", "x", "y", "y"], ["z", "u", "z", "u"]]
print("-----------指定行和列索引之後的資料值---------------")
print(df)

      x       y    
      z   u   z   u
a c   1   2   3   4
  d   5   6   7   8
b c   9  10  11  12
  d  13  14  15  16

print('-------df.loc["a"]------------')
print(df.loc["a"]) # 對應的外層索引的切片

   x     y   
   z  u  z  u
c  1  2  3  4
d  5  6  7  8

print('--------df.loc["a", "c"]-----------')
print(df.loc["a", "c"]) #取的是對應的行資料

x  z    1
   u    2
y  z    3
   u    4

print('------df["x", "z"])--------')
print(df["x", "z"]) #取的是列資料

a  c     1
   d     5
b  c     9
   d    13
Name: (x, z), dtype: int64

通過位置訪問元素與是否多層索引無關。

print('----------df.iloc[0])-----------------')
print(df.iloc[0])

x  z    1
   u    2
y  z    3
   u    4
Name: (a, c), dtype: int64

交換索引
我們可以呼叫DataFrame物件的swaplevel方法來交換兩個層級索引。該方法預設對倒數第2層與倒數第1層進行交換。我們也可以指定交換的層。層次從0開始,由外向內遞增(或者由上到下遞增),也可以指定負值,負值表示倒數第n層。除此之外,我們也可以使用層次索引的名稱來進行交換。

!/usr/bin/python3

-- coding: utf-8 --

@Time : 2019-06-13 13:07

@Author : LiYahui

@Description :

交換索引的層級,可以以一種不同的方式來進行展示(統計)。

import pandas as pd
data=[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
df = pd.DataFrame(data, index=[["a", "a", "b", "b"],
["north", "north", "south", "south"], [2017, 2018, 2017, 2018]])
print(df)

               0   1   2   3
a north 2017   1   2   3   4
        2018   5   6   7   8
b south 2017   9  10  11  12
        2018  13  14  15  16

層級:從外層到內層,值為0, 1, 2……,同時,層級也可以為負值,表示倒數第n個層級(由內層到外層)。

例如,-1表示最內層。

如果沒有顯式指定交換的層級,則預設交換最內層的兩個層級。

df = df.swaplevel(0, 2)
print(df)

               0   1   2   3
2017 north a   1   2   3   4
2018 north a   5   6   7   8
2017 south b   9  10  11  12
2018 south b  13  14  15  16

索引名字的作用。

除了數值來指定索引的層級外,我們也可以通過索引的名字來指定索引的層級。

df.index.names = ["x", "area", "year"]
df.swaplevel("area", "year")
print(df)

                  0   1   2   3
x    area  year                
2017 north a      1   2   3   4
2018 north a      5   6   7   8
2017 south b      9  10  11  12
2018 south b     13  14  15  16

df.sort_index()
print(df)

                 0   1   2   3
x    area  year                
2017 north a      1   2   3   4
2018 north a      5   6   7   8
2017 south b      9  10  11  12
2018 south b     13  14  15  16

索引排序
我們可以使用sort_index方法對索引進行排序處理。

level:指定根據哪一層進行排序,預設為最外(上)層。該值可以是數值,索引名,或者是由二者構成的列表。
inplace:是否就地修改。預設為False。

層級索引的排序

df = pd.DataFrame(data, index=[["b", "a", "c", "c"],
["c", "y", "k", "k"], [3, -2, 5, 2]])
print(df)

         0   1   2   3
b c  3   1   2   3   4
a y -2   5   6   7   8
c k  5   9  10  11  12
     2  13  14  15  16

在對索引進行排序時,可以通過level引數指定索引的層級(排序的層級)。

如果沒有顯式指定,則預設為最外層的層級(層級為0)。

當我們對某個層級進行排序時,該層級的所有內層層級也會進行排序。

print(df.sort_index())

         0   1   2   3
a y -2   5   6   7   8
b c  3   1   2   3   4
c k  2  13  14  15  16
     5   9  10  11  12

print(df.sort_index(level=1))

         0   1   2   3
b c  3   1   2   3   4
c k  2  13  14  15  16
     5   9  10  11  12
a y -2   5   6   7   8

索引堆疊
通過DataFrame物件的stack方法,可以進行索引堆疊,即將指定層級的列轉換成行。
level:指定轉換的層級,預設為-1。
*詳見 Python--pandas--unstack() 與stack()
取消堆疊
通過DataFrame物件的unstack方法,可以取消索引堆疊,即將指定層級的行轉換成列。
level:指定轉換的層級,預設為-1。
fill_value:指定填充值。預設為NaN。
詳見: Python--pandas--unstack() 與stack()
設定索引
在DataFrame中,如果我們需要將現有的某一(幾)列作為索引列,可以呼叫set_index方法來實現。
drop:是否丟棄作為新索引的列,預設為True。
append:是否以追加的方式設定索引,預設為False。
inplace:是否就地修改,預設為False。
詳見Python-pandas--set_index與reset_index
重置索引
呼叫在DataFrame物件的reset_index,可以重置索引。該操作與set_index正好相反。
level:重置索引的層級,預設重置所有層級的索引。如果重置所有索引,將會建立預設整數序列索引。
drop:是否丟棄重置的索引列,預設為False。
inplace:是否就地修改,預設為False。
詳見 Python-pandas--set_index與reset_index
參考部落格:
https://www.jianshu.com/p/2c1c8fbee55a
數分筆記整理15 - 資料處理綜合運用 - 多層次索引MultiIndex
Python3 pandas.MultiIndex類詳解 及swaplevel sortlevel的使用