<input id="0qass"><u id="0qass"></u></input>
  • <input id="0qass"><u id="0qass"></u></input>
  • <menu id="0qass"><u id="0qass"></u></menu>

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

    ↑↑↑關注后"星標"Datawhale

    每日干貨 &?每月組隊學習,不錯過

    ?Datawhale干貨?

    作者:耿遠昊,Datawhale成員,華東師范大學

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

    Pandas做分析數據,可以分為索引、分組、變形及合并四種操作。前邊已經介紹過索引操作、分組操作,現在接著對Pandas中的變形操作進行介紹,涉及知識點提綱如下圖:

    ? ? ?本文目錄

    ??????????????1. 透視表

    ????????????????? 1.1.?pivot

    ? ? ? ? ? ? ? ? ? 1.2. pivot_table

    ? ? ? ? ? ? ? ? ? 1.3. crosstab(交叉表)

      • ?2. 其他變形方法

        ???2.1. melt函數

        ? ?2.2. 壓縮與展開

    ? ? ? ? ? ? ? ?3. 啞變量與因子化

      • ? ?3.1. Dummy?Variable(啞變量)

        ? ?3.2. factorize方法

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

    import numpy as np
    import pandas as pd
    df = 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=None
    df_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)
    
    True

    本文電子版 后臺回復 變形 獲取

    “在看,為Pandas點贊

    ??2020 CSDN 皮膚主題: 大白 設計師:CSDN官方博客 返回首頁
    多乐彩