3.6 層級索引
3.6.1 多級索引Series
在實際工作中,我們會經常遇到多維數據,數據索引超過一兩個鍵。我們可以通過層級索引配合多個不同等級的一級索引一起使用,這樣就可以將多維數組轉換成類似Series和DataFrame的形式.
舉例:分析美國各種在兩個不同年份的數據:
In [1] :import pandas as pd
import numpy as np
In [2] :index = [('California', 2000), ('California', 2010),('New York', 2000), ('New York', 2010),('Texas', 2000), ('Texas', 2010)]
populations = [33871648, 37253956,18976457, 19378102,20851820, 25145561]
pop = pd.Series(populations, index=index)
pop
Out[2] :(California, 2000) 33871648
(California, 2010) 37253956
(New York, 2000) 18976457
(New York, 2010) 19378102
(Texas, 2000) 20851820
(Texas, 2010) 25145561
dtype: int64
現在我們想查看所有州2000年的數據,那麼會稍微複雜一些:
In [2] :pop[[i for i in pop.index if i[1]==2010]]
Out[2] :(California, 2010) 37253956
(New York, 2010) 19378102
(Texas, 2010) 25145561
dtype: int64
雖然這種辦法是可行的,但是不夠簡潔,特別是當數據量變大時,效率會很低。所以,pandas提過了更好的解決方案,使用MultiIndex將元組創建一個多級索引。
In [3] :index = pd.MultiIndex.from_tuples(index)
index
Out[3] :MultiIndex(levels=[['California', 'New York', 'Texas'], [2000, 2010]],labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]])
使用新索引替換pop原有索引,得到一個類似DataDrame的Series。其中:前兩列表示Series的多級索引值,第三列是數據。第一列中的空格等同於上面的索引。
In [3] :pop = pop.reindex(index)
pop
Out[3] :
California 2000 33871648
2010 37253956
New York 2000 18976457
2010 19378102
Texas 2000 20851820
2010 25145561
dtype: int64
現在,我們再來獲取某一年美國各州的人口數據:
In [4] :pop[:,2010]
Out[4] :
California 37253956
New York 19378102
Texas 25145561
dtype: int64
可以看到,取值操作更簡潔。其實,上面的Series可以用DataFrame來表示,unstack() 方法可以快速將一個多級索引的Series 轉化爲普通索引的DataFrame:
In [5] :pop_df = pop.unstack()
pop_df
Out[5] :
2000 2010
California 33871648 37253956
New York 18976457 19378102
Texas 20851820 25145561
stack()方法可以實現相反的效果,將DataFrame轉爲一個多級索引的Series:
In [6] :pop_df.stack()
Out[6] :
California 2000 33871648
2010 37253956
New York 2000 18976457
2010 19378102
Texas 2000 20851820
2010 25145561
dtype: int64
意義:我們可以用多級索引的一維Series數據表示二維數據,那麼就可以用DataFrame表示三維甚至更高維度的數據。理論上可以表示任意維度的數據。
3.6.2 多級索引的創建方法
1.顯式地創建多級索引
通過一個有不同等級的若干簡單數組組成的列表來構建MultiIndex:
In [7] :pd.MultiIndex.from_arrays([['a', 'a', 'b', 'b'], [1, 2, 1, 2]])
Out[7] :MultiIndex(levels=[['a', 'b'], [1, 2]],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
也可以通過包含多個索引值的元組構成的列表創建MultiIndex:
In [8] :pd.MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1), ('b', 2)])
Out[8] :MultiIndex(levels=[['a', 'b'], [1, 2]],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
更可以直接提供levels(包含每個等級的索引值列表的列表)和labels(包含每個索引值標籤列表的列表)創建MultiIndex:
In [9] :pd.MultiIndex(levels=[['a', 'b'], [1, 2]],labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
Out[9] :MultiIndex(levels=[['a', 'b'], [1, 2]],labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
在創建Series 或DataFrame 時,可以將這些對象作爲index 參數,或者通過reindex 方法更新Series 或DataFrame 的索引。
2. 多級索引的等級名稱
給MultiIndex 的等級加上名稱會爲一些操作提供便利。你可以在前面任何一個MultiIndex構造器中通過names 參數設置等級名稱,也可以在創建之後通過索引的names 屬性來修改名稱:
In [10]:pop.index.names = ['state', 'year']
pop
Out[10]:
state year
California 2000 33871648
2010 37253956
New York 2000 18976457
2010 19378102
Texas 2000 20851820
2010 25145561
dtype: int64
3. 多級列索引
每個DataFrame 的行與列都是對稱的,也就是說既然有多級行索引,那麼同樣可以有多級列索引。
In [11]: # 多級行索引
index = pd.MultiIndex.from_product([[2013, 2014], [1, 2]],names=['year', 'visit'])
In [12]: # 多級列索引
columns = pd.MultiIndex.from_product([['Bob', 'Guido', 'Sue'], ['HR', 'Temp']],names=['subject', 'type'])
In [13]:# 模擬數據
data = np.round(np.random.randn(4, 6), 1)
data[:, ::2] *= 10
data += 37
In [14]: # 創建DataFrame
health_data = pd.DataFrame(data, index=index, columns=columns)
health_data
Out[14]:
subject Bob Guido Sue
type HR Temp HR Temp HR Temp
year visit
2013 1 19.0 -0.9 -10.0 -0.6 -2.0 -1.1
2 15.0 -0.3 16.0 -0.3 10.0 -1.2
2014 1 -12.0 0.4 -4.0 0.2 11.0 -1.3
2 4.0 0.5 9.0 -0.9 7.0 0.4
3.6.3 多級索引的取值與切片
1. Series多級索引
對於美國各州人口數據的多級索引Series來說:
In [15]:pop
Out[15]:
state year
California 2000 33871648
2010 37253956
New York 2000 18976457
2010 19378102
Texas 2000 20851820
2010 25145561
dtype: int64
可以通過對多個級別索引值獲取單個元素:
In [16]:pop['California', 2010]
Out[16]:37253956
Miltiindex也支持局部取值,即只取索引的某一個層級。假設只取最高級別的索引(California),獲得的結果是一個新的Series,未被選中的低索引值(2000,2010)會被保留。
In [17]:pop['California']
Out[17]:
year
2000 33871648
2010 37253956
dtype: int64
2. DataFrame多級索引
DataFrame多級索引的用法與Series類似。
In [18]:health_data
Out[18]:
subject Bob Guido Sue
type HR Temp HR Temp HR Temp
year visit
2013 1 19.0 -0.9 -10.0 -0.6 -2.0 -1.1
2 15.0 -0.3 16.0 -0.3 10.0 -1.2
2014 1 -12.0 0.4 -4.0 0.2 11.0 -1.3
2 4.0 0.5 9.0 -0.9 7.0 0.4
由於DataFrame的基本索引是列索引,因此Series中多級索引的用法到了DataFrame中就應用在列上了。例如,可以通過簡單的操作獲取Guido的心率數據:
In [19]:health_data['Guido', 'HR']
Out[19]:
year visit
2013 1 -10.0
2 16.0
2014 1 -4.0
2 9.0
Name: (Guido, HR), dtype: float64
與單索引類似,loc、iloc 和ix 索引器都可以使用,例如:
In [20]:health_data.iloc[:2, :2]
Out[20]:
subject Bob
type HR Temp
year visit
2013 1 19.0 -0.9
2 15.0 -0.3