數據分析之Pandas變形操做總結

pandas 是一個強大的分析結構化數據的工具集;它的使用基礎是Numpy(提供高性能的矩陣運算);用於數據挖掘和數據分析,同時也提供數據清洗功能。html

Pandas作分析數據,能夠分爲索引、分組、變形及合併四種操做。前邊已經介紹過索引操做分組操做,如今接着對Pandas中的變形操做進行介紹,涉及知識點提綱以下圖:api

圖片

     本文目錄app

              1. 透視表dom

                  1.1. pivotide

                  1.2. pivot_table函數

                  1.3. crosstab(交叉表工具

 2. 其餘變形方法性能

   2.1. melt函數學習

   2.2. 壓縮與展開編碼

               3. 啞變量與因子化

   3.1. Dummy Variable(啞變量)

   3.2. factorize方法


在詳細講解每一個模塊以前,首先讀入數據:





import numpy as npimport pandas as pddf = pd.read_csv('joyful-pandas-master/data/table.csv')df.head()

透視表

1. pivot

通常狀態下,數據在DataFrame會以壓縮(stacked)狀態存放,例如上面的Gender,兩個類別被疊在一列中,pivot函數可將某一列做爲新的cols:


df.pivot(index='ID',columns='Gender',values='Height').head()  # 設行列名,變成一個新的DataFrame

然而pivot函數具備很強的侷限性,除了功能上較少以外,還不容許values中出現重複的行列索引對(pair),例以下面的語句就會報錯:



# df.pivot(index='School',columns='Gender',values='Height').head()# ValueError: Index contains duplicate entries, cannot reshape

所以,更多的時候會選擇使用強大的pivot_table函數。

2. pivot_table


pd.pivot_table(df,index='ID',columns='Gender',values='Height').head()

可是在速度上,因爲功能更多,天然是比不上原來的pivot函數的。



%timeit df.pivot(index='ID',columns='Gender',values='Height')%timeit pd.pivot_table(df,index='ID',columns='Gender',values='Height')

Pandas中提供了各類選項,下面介紹經常使用參數:

① aggfunc:對組內進行聚合統計,可傳入各種函數,默認爲'mean'


pd.pivot_table(df,index='School',columns='Gender',values='Height',aggfunc=['mean','sum']).head()

② margins:彙總邊際狀態



pd.pivot_table(df,index='School',columns='Gender',values='Height',aggfunc=['mean','sum'],margins=True).head()#margins_name能夠設置名字,默認爲'All'

③ 行、列、值均可覺得多級


pd.pivot_table(df,index=['School','Class'], columns=['Gender','Address'], values=['Height','Weight'])

3. crosstab(交叉表)

交叉表是一種特殊的透視表,典型的用途如分組統計,如如今想要統計關於街道和性別分組的頻數:


pd.crosstab(index=df['Address'],columns=df['Gender'])

交叉表的功能也很強大(但目前還不支持多級分組),下面說明一些重要參數:

① values和aggfunc:分組對某些數據進行聚合操做,這兩個參數必須成對出現


pd.crosstab(index=df['Address'],columns=df['Gender'], values=np.random.randint(1,20,df.shape[0]), aggfunc='min')

默認參數以下:


pd.crosstab(index=df['Address'],columns=df['Gender'],values=1,aggfunc='count')

② 除了邊際參數margins外,還引入了normalize參數(求百分比),可選'all','index','columns'參數值,也就是對全體、行或列求百分比。


pd.crosstab(index=df['Address'],columns=df['Gender'],normalize='all',margins=True)

其餘變形方法

1. melt

melt函數能夠認爲是pivot函數的逆操做,將unstacked狀態的數據,壓縮成stacked,使「寬」的DataFrame變「窄」



df_m = df[['ID','Gender','Math']]df_m.head()

圖片



pivoted = df.pivot(index='ID',columns='Gender',values='Math')pivoted.head()

圖片

melt函數中的id_vars表示須要保留的列,value_vars表示須要stack的一組列,value_name是value_vars對應的值的列名。

詳細能夠看:https://pandas.pydata.org/docs/reference/api/pandas.melt.html



result = pivoted.reset_index().melt(id_vars=['ID'],value_vars=['F','M'],value_name='Math').dropna().set_index('ID').sort_index()result.head()

圖片


result.equals(df_m.set_index('ID'))
True

2. 壓縮與展開

1). stack:這是最基礎的變形函數,總共只有兩個參數:level和dropna



df_s = pd.pivot_table(df,index=['Class','ID'],columns='Gender',values=['Height','Weight'])df_s.groupby('Class').head(2)



df_stacked = df_s.stack()  # 默認將列往行壓縮,從後往前。df_stacked.groupby('Class').head(2)

當將全部列壓入行以後,就變成Series了,好比下一個例子:



ddd = df_stacked.stack()ddd.groupby('Class').head(2)

結論:stack函數能夠看作將橫向的索引放到縱向,所以功能相似與melt,參數level可指定變化的列索引是哪一層(或哪幾層,須要列表)




df_stacked = df_s.stack(level=0)  # 這裏將第一層橫向索引放到縱向。這個參數默認爲level,因此也能夠省略。# df_stacked = df_s.stack(0)df_stacked.groupby('Class').head(2)

這裏說一下dropna參數,默認是True。這個參數是用來刪除缺失值的,這個例子不是很好,展現不出刪除缺失值,可是能夠看下面分享的連接,有一個例子比較明顯的展現了dropna是怎麼刪除缺失值的。

若是dropna=True,那麼就是會將缺失值刪除,若dropna=False,則會保留缺失值。



qwe = df_s.stack(level=0, dropna = True)qwe.groupby('Class').head(2)

參考學習:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.stack.html?highlight=stack#pandas.DataFrame.stack

2). unstack:stack的逆函數,功能上相似於pivot_table。

表達式:DataFrame.unstack(self,level = -1,fill_value = None)


df_stacked.head()

結論:這個unstack就是至關於stack的反向操做,將列索引變爲行索引。默認是從右邊索引開始變。
下面說一下參數:對於level就是轉移行索引,默認是-1,也就上面說的從右往左轉移。第二個參數fill_value也很容易猜到,前面stack的dropna是刪除缺失值,這裏的fill_value就是將出現的缺失值補充成NaN,默認爲None。

參考學習:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.unstack.html#pandas.DataFrame.unstack




result = df_stacked.unstack().swaplevel(1,0,axis=1).sort_index(axis=1)# 這裏swaplevel是交換這兩個列索引的位置,sort_index是將列索引分組。能夠將上面的這兩個函數去掉再運行,就能發現出差異了。result.groupby('Class').head(2


result.equals(df_s)

True

啞變量與因子化

1. Dummy Variable(啞變量)

這裏主要介紹get_dummies函數,其功能主要是進行one-hot編碼:



df_d = df[['Class','Gender','Weight']]df_d.head()

如今但願將上面的表格前兩列轉化爲啞變量,並加入第三列Weight數值:


pd.get_dummies(df_d[['Class','Gender']]).join(df_d['Weight']).head()

可選prefix參數添加前綴,prefix_sep添加分隔符,示例以下:



df_pp = df_d[['Class','Gender']]pd.get_dummies(df_pp, prefix=['C','G'], prefix_sep='*').join(df_d['Weight']).head()

這裏prefix的默認是原前綴,prefixsep默認爲' '。

參考學習:https://pandas.pydata.org/docs/reference/api/pandas.get_dummies.html?highlight=get_dummi

2. factorize方法

該方法主要用於天然數編碼,而且缺失值會被記作-1,其中sort參數表示是否排序後賦值,默認爲False。




codes, uniques = pd.factorize(['b', None, 'a', 'c', 'b'], sort=True)display(codes)display(uniques)

codes是對元素進行編碼,None爲-1。uniques獲得列表的惟一元素s。

參考學習:https://pandas.pydata.org/docs/reference/api/pandas.factorize.html?highlight=factori#pandas.factorize

問題與練習

問   題

問題1:上面提到的變形函數,請總結它們各自的使用特色。

melt/crosstab/pivot/pivot_table/stack/unstack

1)首先咱們講 pivot、pivot_tabel,這兩個變形函數都是對某列的元素變成列索引,功能很強大,能夠同時計算平均值、總和等等數據,可是前者有必定的侷限性。2)其次說一下crosstab,這個函數能夠計算頻數,也能夠計算百分比,功能也較爲強大。3)最後看這個melt、stack和unstack。這些函數主要就是用來變換行列索引,功能比較侷限,其中stack的功能就是將行索引變成列索引,而後melt和unstack的功能相似,和stack的功能偏偏相反。

這裏說的比較寬泛,還有不少參數會影響這些功能的使用,詳細的就看上面的代碼和連接吧。


問題2:變形函數和多級索引是什麼關係?哪些變形函數會使得索引維數變化?具體如何變化?

通常咱們使用變形函數,會是變換行列索引,那麼這裏就會遇到這個多級索引的問題,到底換哪個索引,怎麼選擇索引就值得咱們來探討。從咱們所學的來看,能使用多級索引的變形函數是pivot_tabel,這個函數功能很強大,行列和值均可以多級。那麼面對這個多級索引,咱們要變化維數,就要使用stack和unstack這些函數了。在這些函數中有專門的參數來表明咱們要換的那一行列索引的位置level,從而實現選擇索引。

問題3:請舉出一個除了上文提過的關於啞變量方法的例子。

下面咱們改變df_d中的元素。



df_d = df[['School','Gender','Height']]df_d.head()


pd.get_dummies(df_d[['School','Gender']]).join(df_d['Height']).head()


pd.get_dummies(df_d[['School','Gender']], prefix=['S','G'], prefix_sep='%').join(df_d['Height']).head()

圖片


問題4:使用完stack後當即使用unstack必定能保證變化結果與原始表徹底一致嗎?

不必定。這兩個變形函數都是有參數的,咱們若是不考慮參數,遇到多級索引就頗有可能不會一致。可是咱們要是考慮參數,換的行正好對應換的列,而後經過參數找出,再換回來,再經過swaplevel和sort_index等函數進行修正,就能夠作到一致。


問題5:透視表中涉及了三個函數,請分別使用它們完成相同的目標(任務自定)並比較哪一個速度最快。





%timeit df.pivot(index='ID',columns='Gender',values='Height')%timeit pd.pivot_table(df,index='ID',columns='Gender',values='Height')%timeit pd.crosstab(index=df['ID'],columns=df['Gender'], values=df['Height'], aggfunc='min')

圖片

最快的仍是pivot函數。


問題6:既然melt起到了unstack的功能,爲何再設計unstack函數?

雖說melt和unstack很像,可是使用起來卻十分的複雜,參數太多了,須要咱們本身填寫的東西不少。而這個unstack的參數就兩個,level和fill_value,簡單快捷,使用很方便。因此設計unstack函數應該是爲了更方便的完成任務吧。


練   習

練習1:有一份關於美國10年至17年的非法藥物數據集,列分別記錄了年份、州(5個)、縣、藥物類型、報告數量,請解決下列問題:


pd.read_csv('data/Drugs.csv').head()

圖片

(a) 如今請你將數據錶轉化成以下形態,每行須要顯示每種藥物在每一個地區的10年至17年的變化狀況,且前三列須要排序:



df = pd.read_csv('joyful-pandas-master/data/Drugs.csv',index_col=['State','COUNTY']).sort_index()df.head()







#result = pivoted.reset_index().melt(id_vars=['ID'],value_vars=['F','M'],value_name='Math').dropna().set_index('ID').sort_index()
df_result = pd.pivot_table(df,index=['State','COUNTY','SubstanceName'],columns='YYYY',values='DrugReports',                           fill_value='-').sort_index().reset_index().rename_axis(columns={'YYYY':''})df_result.head()

(b) 如今請將(a)中的結果恢復到原數據表,並經過equal函數檢驗初始表與新的結果是否一致(返回True)










result_melted = result.melt(id_vars=result.columns[:3],value_vars=result.columns[-8:]                ,var_name='YYYY',value_name='DrugReports').query('DrugReports != "-"')result2 = result_melted.sort_values(by=['State','COUNTY','YYYY','SubstanceName']).reset_index().drop(columns='index')#下面其實可有可無,只是交換兩個列再改一下類型(由於‘-’因此type變成object了)cols = list(result2.columns)a, b = cols.index('SubstanceName'), cols.index('YYYY')cols[b], cols[a] = cols[a], cols[b]result2 = result2[cols].astype({'DrugReports':'int','YYYY':'int'})result2

圖片



df_tidy = df.reset_index().sort_values(by=result2.columns[:4].tolist()).reset_index().drop(columns='index')df_tidy

圖片


df_tidy.equals(result2)

False


練習2:現有一份關於某地區地震狀況的數據集,請解決以下問題:


pd.read_csv('joyful-pandas-master/data/Earthquake.csv').head()

圖片

(a) 如今請你將數據錶轉化成以下形態,將方向列展開,並將距離、深度和烈度三個屬性壓縮:




df = pd.read_csv('joyful-pandas-master/data/Earthquake.csv')df = df.sort_values(by=df.columns.tolist()[:3]).sort_index(axis=1).reset_index().drop(columns='index')df.head()

圖片




result = pd.pivot_table(df,index=['日期','時間','維度','經度'],columns='方向',values=['烈度','深度','距離'],                        fill_value='-').stack(level=0).rename_axis(index={None:'地震參數'})result.head(6)

圖片

(b) 如今請將(a)中的結果恢復到原數據表,並經過equal函數檢驗初始表與新的結果是否一致(返回True)





df_result = result.unstack().stack(0)[(~(result.unstack().stack(0)=='-')).any(1)].reset_index()df_result.columns.name=Nonedf_result = df_result.sort_index(axis=1).astype({'深度':'float64','烈度':'float64','距離':'float64'}).sort_index()df_result.head()

圖片


df_result.astype({'深度':'float64','烈度':'float64','距離':'float64'},copy=False).dtypes

圖片


df.equals(df_result)
相關文章
相關標籤/搜索