[Python資料科學]用Pandas分析資料— 資料的聚合
在處理資料時,常常會希望把一些分散的資料集合在一起,然後再針對這個集合在一起的大資料表,進行分組,在對每一個組別的內容進行進一步的運算,然後再將運算結果集合為一個新的報表。這些過程的目的是為了產出最終的報表。這一篇主要的焦點會集中在資料的分組部分。我們會使用美國《財富》雜誌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)
結果:
延伸閱讀: