[Python資料科學]用Pandas分析資料— 資料的聚合

Sean Yeh
Python Everywhere -from Beginner to Advanced
12 min readJun 20, 2020

在處理資料時,常常會希望把一些分散的資料集合在一起,然後再針對這個集合在一起的大資料表,進行分組,在對每一個組別的內容進行進一步的運算,然後再將運算結果集合為一個新的報表。這些過程的目的是為了產出最終的報表。這一篇主要的焦點會集中在資料的分組部分。我們會使用美國財富》雜誌18年評選的全美最大的1000家公司的營業額排行榜資料,作為來源範例檔案。

讀取原始資料

在讀取原始資料前,先匯入Pandas套件。

import pandas as pd

讀取Fortune 1000的Excel檔案(我們使用網路上可以下載到的美國Fortune 1000檔案作為例子)。並且設定 index_col=”Rank”,藉此設定,可以直接使用Excel檔案中的Rank欄位作為資料的index值。最後將讀取結果指定給Fortune變數。

Fortune = pd.read_excel("sample_data/fortune1000-final.xlsx" ,index_col="Rank")

可以用type來看看Fortune的物件型態。

type(Fortune)

結果發現,Fortune的物件型態為Dataframe。

接著,列出Fortune Dataframe的前面3行:

Fortune.head(3)

結果如下:

groupby方法

大略分析一下讀取的Frotune Dataframe ,發現可以利用Sector欄位作為群組資料的基礎。我們使用groupby方法來群組資料。

Fortune.groupby("Sector")

將資料groupby之後,我們會得到一個groupby物件,這個物件無法直接看到內容。

同樣的,我們指定一個變數Sectors給這個group群組。

sectors = Fortune.groupby("Sector")

#注意,Fortune的資料型態為DataFrame物件,而sectors的資料型態為DataFrameGroupBy物件。兩者的資料型態不一樣。

size()與value_counts()

觀察一下,當我們對sectors群組(DataFrameGroupBy物件)使用size()與value_counts()兩種方式後,會得到什麼結果?

下面先使用size()

sectors.size()

size() 可以計算各分組的大小,並且回傳一個Series。使用size()可以得到的結果如下:

接著,再使用value_counts()

Fortune['Sector'].value_counts()

使用value_counts()得到的結果如下:

我們從上面的實驗可以發現,不論使用size()value_counts()兩者中的任一方式,得到的結果是一樣的。

first()與last()

使用first( ),可以顯示出每個Group群組裡面的第一筆資料。

sectors.first()

例如,我們可以知道在Aerospace & Defense產業(航太與國防產業)類別中排名第一為Boeing(波音),Apparel業(服飾業)類別中的排名第一為Nike。

反之,使用last()可以顯示每個Group群組裡面的最後一筆資料,這裡就不顯示結果了,大家若有興趣想知道結果,可以自行操作試試。

sectors.last()

groups

使用groups,會回傳一個字典物件,裡面列出每個key的value值。

sectors.groups

以同樣的例子來說sectors.groups,將列出以每個Sector為key所對應的value值。我們觀察一下結果,可以發現在每個value值裡面,都有很多的數字(下面列出Aerospace & Defense的key-value),這些數字代表的是每一個row的index:

'Aerospace &  Defense': Int64Index([ 27,  51,  59,  99, 118, 119, 208, 225, 276, 381, 405, 407, 415,541, 661, 662, 807, 830, 885, 931, 955, 956, 960, 976, 989],dtype='int64', name='Rank'),

如果要取得在內容,可以使用loc來實現。例如:

Fortune.loc[27]

得到的結果是Aerospace & Defense類別中,row編號為27的一筆資料(index為27)。又由於我們在一開始的時候,將Rank欄位設定為index(index_col="Rank"),因此編號為27代表的是Rank排名為27的公司,顯示出來的結過是Boeing波音公司:

用get_group方法,取群組資料

由於sectors群組的資料型態為DataFrameGroupBy物件,無法直接使用print()顯示內容。須透過get_group方法的使用,才可以取得特定群組中某一組的資料。例如下面的程式碼會顯示所有Aerospace & Defense群組的row資料:

sectors.get_group('Aerospace &  Defense')

顯示結果如下:

除了使用上面的方式外,我們也可以用下面的方式取得一樣的結果。

Fortune[Fortune['Sector'] =='Aerospace &  Defense']

使用其他方法

max方法

.max()可以取得每個群組中的最大值。例如在Retailing群組中,最大值的是:

sectors.get_group('Retailing').max()

min方法

.min()可以取得每個群組中的最小值。例如在Retailing群組中,最小值的是:

sectors.get_group('Retailing').min()

sum方法

.sum()可以取得每個群組總和。例如,我們將.sum()用在sectors群組上,會得到sectors群組中,各個欄位的個別加總。

sectors.sum()

如果你對這個數字有所疑惑,可以透過下面的方式來驗證看看是否正確。

例如,下面這一行取得Aerospace & Defense分組中Employees欄位的總和,先(get_group)取得群組,再選擇欄位(['Employees'])),接著計算總和(sum())。運算結果為1,010,124。也就是說美國的航太與國防業的員工總數大約一百多萬。

sectors.get_group('Aerospace &  Defense')['Employees'].sum()

將這個結果比對一下前面的運算結果,發現與sectors.sum()的結果一樣。

mean方法

如果要計算群組中每一組的平均,可以使用GroupBy的 .mean()取得各組的平均數。

sectors.mean()

對多欄位取值

也可以一次取得兩個以上欄位的值。例如下面可以取出Revenues與Employees的最大值

sectors[["Revenues","Employees"]].sum()

對多欄位進行群組

前面的例子都是針對單一欄位進行群組,有時候,我沒會希望針對一個以上的欄位來進行分組。Pandas提供我們選擇兩個以上欄位,作為群組資料的條件。如下,同時選擇了Sector與Industry兩個欄位作為群組資料的條件。

sectors = Fortune.groupby(["Sector","Industry"])

取得group群組的大小

使用size() 可以取得群組的大小,由於我們選擇了Sector與Industry兩個欄位作為群組資料的條件,顯示size()的結果是兩個欄位的集合。

sectors.size()

如果我們只想計算Revenues的總和,可以用下面這個方式取得:

sectors["Revenues"].sum()

同樣的道理,如果只想計算Employees的平均數,可以這麼寫:

sectors["Employees"].mean()

agg方法 — — 資料的聚合

接下來要討論Pandas 的資料聚合,我們先還原回到原來的設定。

Fortune = pd.read_excel("sample_data/fortune1000-final.xlsx" ,index_col="Rank")
sectors = Fortune.groupby("Sector")

若要對一個Series或DataFrame的所有欄位進行聚合的話,可以將要執行的函數傳入agg方法中,或者是執行既有的函數,例如mean或std等。

sectors.agg('mean')

上面的方式可以計算出sectors群組各欄的平均值。

.agg() 可以傳入dict字典

我們也可以在agg方法中傳入一組字典,以欄位名稱為字典的key、要執行的項目則為字典的value。

如下,可以取得sectors群組中Revenues的總和(sum),與Employees的平均數(mean)。

sectors.agg({"Revenues":"sum","Employees":"mean"})

結果如下:

.agg() 可以放入list串列

除了在agg中傳入字典外,如果要分別獲得群組各欄的狀況,也可以在agg()裡面傳入一個list串列來取得。其中,串列的內容為要執行的項目,這裡是指計算各欄的大小,總和與平均數。

sectors.agg(["size","sum","mean"])

.agg() 可以交互傳入dict字典與list串列

如果想一次取得sectors群組中Revenues的總和與平均數,以及Employees的中位數。其中,Revenues使用了一個list放置總(sum)和與平均數(mean)。

sectors.agg({"Revenues":["sum","mean"],"Employees":"mean"})

結果:

Iterating through Groups遍歷群組

如果只是使用下面的程式碼,可以知道每個sectors群組的獲利最高的公司(Profits最大值)是多少額度,但從這裡卻看不出實際上獲利最高的是群組(sectors群組)裡的哪一個公司。如果想要知道裡面的內容,需要再對資料進行其他的加工。

sectors["Profits"].max()

由於nlargest()函數可以取得最大的N 個元素列表,我們可以使用這個函數來取得sectors群組裡面每一組中Revenues為第一大的列表。

因此,我們用for迴圈遍歷整個sector群組,將sectors群組裡面每一組中Revenues為第一大的列表取出,並指定給變數highest_revenue_company,然後再逐一的將結果存入一個新的df中。

for sector, data in sectors:
highest_revenue_company = data.nlargest(1,"Revenues")
df = df.append(highest_revenue_company)

執行程式後,可以看到結果。每個群組中Revenues第一名的公司資料。

我們可以如法泡製上面的方法,以城市(City)為群組:

cities = Fortune.groupby("City")

用for迴圈遍歷整個sector群組,找出Revenues 最高的城市。

for city, data in cities:
highest_revenue_company = data.nlargest(1,"Revenues")
df = df.append(highest_revenue_company)

結果:

延伸閱讀:

--

--

Sean Yeh
Python Everywhere -from Beginner to Advanced

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。