給妹子講python-S02E15創建Pandas多級索引

python,不管你懂沒懂,反正妹子是搞懂了

轉載請註明:知乎專欄《給妹子講python》

【要點搶先看】

1.帶多級索引的Series數據類型2.多級索引Series對象和DataFrame對象的相互轉化3.帶多級索引的DataFrame數據類型4.多級索引的創建方法5.多級行索引和多級列索引舉例

之前的幾集里,我們使用了Series數據類型表示一維數據(即僅有一個索引列),用DataFrame類型數據表示二維數據(即包含行索引和列索引這兩維索引數據)。

但是如果是下面這種情況呢?比如我們用Series來表示美國不同的州、不同年份的人口數據,這對Series來說就相當於有了兩個索引值:即(州,人口)二維索引,第一反應我們嘗試使用元組來表示索引信息:

import pandas as pdimport numpy as npindex = [(California, 2008), (California, 2018), (New York, 2008), (New York, 2018), (Texas, 2008), (Texas, 2018)]population = [33870000, 37250000, 18970000, 19370000, 20850000, 25140000]pop = pd.Series(population, index=index)print(pop)(California, 2008) 33870000(California, 2018) 37250000(New York, 2008) 18970000(New York, 2018) 19370000(Texas, 2008) 20850000(Texas, 2018) 25140000dtype: int64

這是我們第一直覺所採取的方法,但是這種形式在數據的操作上很不方便,比如我們要拿出所有2018年的數據,似乎比較繁瑣。

不用著急,Pandas為我們提供了更好、更直觀的解決方法,他提供了一個MultiIndex類型,專門用來處理這種多級索引的問題。

我們嘗試用這種方法做一個示範:

import pandas as pdimport numpy as npindex = [(California, 2008), (California, 2018), (New York, 2008), (New York, 2018), (Texas, 2008), (Texas, 2018)]mul_index = pd.MultiIndex.from_tuples(index)print(mul_index)MultiIndex(levels=[[California, New York, Texas], [2008, 2018]], labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]])

我們生成了一個MultiIndex類型的對象,其中levels屬性的兩個列表元素表示兩個索引列里的值,labels里則表示每一個索引列每個位置上對應的是levels相應列表裡的第幾個元素。

我們用這種方式,重新來構建一個帶有多維索引的Series類型數據:

import pandas as pdimport numpy as npindex = [(California, 2008), (California, 2018), (New York, 2008), (New York, 2018), (Texas, 2008), (Texas, 2018)]mul_index = pd.MultiIndex.from_tuples(index)population = [33870000, 37250000, 18970000, 19370000, 20850000, 25140000]pop = pd.Series(population, index=mul_index)print(pop)California 2008 33870000 2018 37250000New York 2008 18970000 2018 19370000Texas 2008 20850000 2018 25140000dtype: int64

在結果中,我們可以看到,前面兩列表示Series的多級索引,第三列是我們的數據,那麼回到剛剛的問題,如果此時我們想獲取2018年的所有數據,做法就相當簡單了:

print(pop[:, 2018])California 37250000New York 19370000Texas 25140000dtype: int64

簡簡單單用二維的切片方式就可以處理了。

【妹子說】二維的Series,怎麼感覺和DataFrame其實是一回事兒呢?

沒錯,其實我們把其中的一維索引放到行的位置上,可不就成了一個普通的二維DataFrame對象嘛,而且Pandas里還提供了方法讓這二者互相轉化。我們還是沿用上面的二維Series對象pop來舉個例子:

df_pop = pop.unstack()print(df_pop)print(df_pop.stack()) 2008 2018California 33870000 37250000New York 18970000 19370000Texas 20850000 25140000California 2008 33870000 2018 37250000New York 2008 18970000 2018 19370000Texas 2008 20850000 2018 25140000dtype: int64

unstack( )方法可以使一個帶有多級索引的Series轉化為一個普通的DataFrame對象,而stack( )方法實現的則是相反方向的操作。

【妹子說】既然二維Sereis可以用DataFrame來表示,那幹嘛還要費勁去了解這個多級索引?

很簡單,因為還有更高維的數據,既然我們可以用一維Sereis來表示二維的數據,那麼也可以用Sereis、DataFrame來表示三維甚至是更高維的數據。

比如,我們要表示上述三個州,2008/2018,總人口/18歲以下的人口,我們看到這是三維信息了,怎麼樣,是不是最終還是逃不掉多級索引?

這次我們使用DataFrame來表示三級索引,用我們之前講過的DataFrame構造的方法,用字典列表來進行構造:

import pandas as pdimport numpy as npindex = [(California, 2008), (California, 2018), (New York, 2008), (New York, 2018), (Texas, 2008), (Texas, 2018)]mul_index = pd.MultiIndex.from_tuples(index)population = [33870000, 37250000,18970000, 19370000, 20850000, 25140000]under_18_pop = [9267089, 9284094, 4687374, 4318033, 5906301, 6879014]pop = pd.Series(population, index=mul_index)pop_df = pd.DataFrame({total:pop, under18:under_18_pop})print(pop_df) total under18California 2008 33870000 9267089 2018 37250000 9284094New York 2008 18970000 4687374 2018 19370000 4318033Texas 2008 20850000 5906301 2018 25140000 6879014

這種有多級索引的DataFrame,同樣也是運用類似pop_df[total]這種字典鍵的形式來獲取列信息。

print(pop_df[under18])print(pop_df[under18]/pop_df[total])California 2008 9267089 2018 9284094New York 2008 4687374 2018 4318033Texas 2008 5906301 2018 6879014Name: under18, dtype: int64California 2008 0.273608 2018 0.249237New York 2008 0.247094 2018 0.222924Texas 2008 0.283276 2018 0.273628dtype: float64

我們還可以看出,在含有多級索引的Series和DataFrame數據類型中,除了多級索引之外,其他的包括創建、數據訪問等部分的操作都是一樣的。

因此最核心的就是多級索引的創建,下面我們來總結一下幾種常見的創建的方法:

第一種方式,我們提供一個嵌套列表,其中每一個元素都是一個列表,分別對應一個完整的索引列表。

import pandas as pdimport numpy as npmul_index = pd.MultiIndex.from_arrays([[a, a, b, b],[1,2,1,2]])print(mul_index)MultiIndex(levels=[[a, b], [1, 2]], labels=[[0, 0, 1, 1], [0, 1, 0, 1]])

第二種是我們前面用過的,利用一個元組列表來生成多級索引,其中每一個元組都表示多級索引中一一對應的兩項:

import pandas as pdimport numpy as npmul_index = pd.MultiIndex.from_tuples([(a, 1), (a, 0), (b, 1), (b, 0)])print(mul_index)MultiIndex(levels=[[a, b], [0, 1]], labels=[[0, 0, 1, 1], [1, 0, 1, 0]])

第三種方式是直接傳入levels和labels標籤進行構造:

import pandas as pdimport numpy as npmul_index = pd.MultiIndex(levels=[[a, b], [0, 1]], labels=[[0, 0, 1, 1], [1, 0, 1, 0]])print(mul_index)MultiIndex(levels=[[a, b], [0, 1]], labels=[[0, 0, 1, 1], [1, 0, 1, 0]])

第四種是一種所謂「相乘」的形式,寫法上比較簡單,實現兩兩索引按順序分別組合:

import pandas as pdimport numpy as npmul_index = pd.MultiIndex.from_product([[2008,2018],[1,2]])print(mul_index)MultiIndex(levels=[[2008, 2018], [1, 2]], labels=[[0, 0, 1, 1], [0, 1, 0, 1]])

為了更清晰的表徵每一個索引列,我們可以給每一個索引都加一個名字:

pop = pd.Series(population, index=mul_index)pop.index.names = [state, year]print(pop)state yearCalifornia 2008 33870000 2018 37250000New York 2008 18970000 2018 19370000Texas 2008 20850000 2018 25140000dtype: int64

【妹子說】之前我們舉的幾個例子都是行索引,那列索引應該也可以是多級的吧。

當然,我們可以設置二維列索引,結合之前的二維行索引,我們就可以表示四維數據了。我們看下面的這個例子:

import pandas as pdimport numpy as npindex = pd.MultiIndex.from_product([[2008,2018],[1,2]], names=[year,visit])colomus = pd.MultiIndex.from_product([[Tom,Bill],[22,18]], names=[name,age])data = np.random.randn(4,4)df_data = pd.DataFrame(data,index=index,columns=colomus)print(df_data)name Tom Bill age 22 18 22 18year visit 2008 1 -1.225804 -0.363311 0.098241 -1.318783 2 0.456561 -1.049426 0.407306 -1.0149732018 1 2.178388 -0.491357 1.184801 0.341740 2 -2.536552 -0.351532 -0.233742 -0.164102

【妹子說】這樣一來,在行方向和列方向上,都存在多級索引,那麼如何通過索引實現對DataFrame值的訪問呢?

恩,多級索引的取值問題是一個重點,不過今天的內容已經夠多啦,這個內容我們下一集慢慢再說。


推薦閱讀:

Pandas學習-6concat合併
Pandas學習-8pandas plot圖表
如何與pandas愉快地玩耍(二)
【翻譯】《利用Python進行數據分析·第2版》第5章(中)pandas入門
如何與pandas愉快地玩耍(一)

TAG:pandas包 | 索引 | 數據分析 |