task_5變形

XYQLTX發表於2020-12-27

一、作業

# 作業1
df = pd.read_csv('data/drugs.csv').sort_values([     'State','COUNTY','SubstanceName'],ignore_index=True)
df.head()
#將第1問中的結果恢復為原表。
df3 = df2.melt(id_vars=['State', 'COUNTY', 'SubstanceName'],
               value_vars=[2010,2011,2012,2013,2014,2015,2016,2017],
               var_name='YYYY',
               value_name='DrugReports'
              )


#按 State 分別統計每年的報告數量總和,其中 State, YYYY 分別為列索引和行索引,
#要求分別使用 pivot_table 函式與 groupby+unstack 兩種不同的策略實現,並體會它們之間的聯絡。
# pivot_table 函式
df4 = df.pivot_table(index = ['State'],
                       columns = ['YYYY'],
                       values = 'DrugReports',
                    aggfunc='sum', margins=True)
df4

#groupby+unstack
df5 = df.groupby(['State','YYYY'])['DrugReports']
df5.sum().unstack()

二、長寬表的變形

長表、寬表是對於某一個特徵而言的。例如:一個表中把性別儲存在某一個列中,那麼它就是關於性別的長表;如果把性別作為列名,列中的元素是某一其他的相關特徵數值,那麼這個表是關於性別的寬表。

pd.DataFrame({'Gender':['F','F','M','M'],'Height':[167,159,173,178]})
pd.DataFrame({'Height:F':[167,159],'Height:M':[173,178]})

2.1 pivot

pivot 是一種典型的長表變寬表的函式,

df = pd.DataFrame({'Class':[1,1,2,2],
                   'Name':['San Zhang','San Zhang','Si Li','Si Li'],
                   'Subject':['Chinese','Math','Chinese','Math'],
                   'Grade':[80,75,90,85]})
df

變形後的行索引、需要轉到列索引的列,以及這些列和行索引對應的數值,它們分別對應了 pivot 方法中的 index, columns, values 引數

df.pivot(index='Name',columns='Subject',values='Grade')
df.pivot(index='Subject',columns='Class',values='Grade')

利用 pivot 進行變形操作需要滿足唯一性的要求,即由於在新表中的行列索引對應了唯一的 value ,因此原表中的 index 和 columns 對應兩個列的行組合必須唯一。

df1=pd.DataFrame({'Class':[1, 1, 2, 2, 1, 1, 2, 2],
                   'Name':['San Zhang', 'San Zhang', 'Si Li', 'Si Li',
                            'San Zhang', 'San Zhang', 'Si Li', 'Si Li'],
                  'Examination': ['Mid', 'Final', 'Mid', 'Final',
                                'Mid', 'Final', 'Mid', 'Final'],
                 'Subject':['Chinese', 'Chinese', 'Chinese', 'Chinese',
                              'Math', 'Math', 'Math', 'Math'],
                  'Grade':[80, 75, 85, 65, 90, 85, 92, 88],
                  'rank':[10, 15, 21, 15, 20, 7, 6, 2]})
pivot_multi = df1.pivot(index=['Class','Name'],columns=['Examination','Subject'],values=['Grade','rank'])

2.2 pivot_table

pivot 的使用依賴於唯一性條件,那如果不滿足唯一性條件,那麼必須通過聚合操作使得相同行列組合對應的多個值變為一個值。pandas 中提供了 pivot_table 來實現,其中的 aggfunc 引數就是使用的聚合函式。

df2 = pd.DataFrame({'Name':['San Zhang', 'San Zhang',
                           'San Zhang', 'San Zhang',
                            'Si Li', 'Si Li', 'Si Li', 'Si Li'],
                    'Subject':['Chinese', 'Chinese', 'Math', 'Math',
                              'Chinese', 'Chinese', 'Math', 'Math'],
                    'Grade':[80, 90, 100, 90, 70, 80, 85, 95]})
df2
df2.pivot_table(index='Name',columns='Subject',values='Grade',aggfunc='mean')
#Subject	Chinese	Math
#Name		
#San Zhang	85	95
#Si Li	75	90

這裡傳入 aggfunc 包含了上一章中介紹的所有合法聚合字串,此外還可以傳入以序列為輸入標量為輸出的聚合函式來實現自定義操作,

df2.pivot_table(index='Name',columns='Subject',values='Grade',aggfunc=lambda x: x.mean())

pivot_table具有邊際彙總的功能,可以通過設定margins=True來實現,其中邊際的聚合方式與aggfunc中給出的聚合方法一致。

df2.pivot_table(index='Name',columns='Subject',values='Grade',aggfunc='mean',margins=True)
  • 注:每行All的值不是一行的均值,而是那一行原表的均值。!!!
  • 練一練,在上面的邊際彙總例子中,行或列的彙總為新表中行元素或者列元素的平均值,而總體的彙總為新表中四個元素的平均值。這種關係一定成立嗎?若不成立,請給出一個例子來說明。
    :當表中存在缺失值時,結果會不一致,例如
df_ex = pd.DataFrame({'Name':['San Zhang', 
                           'San Zhang', 'San Zhang',
                            'Si Li', 'Si Li'],
                  'Subject':['Chinese', 'Chinese','IT',
                               'Chinese', 'Chinese'],
                   'Grade':[80, 90, 100, 90, 90]})
df_ex
df_ex.pivot_table(index='Name',columns='Subject',values='Grade',aggfunc='mean',margins=True)
#Subject	Chinese	IT	All
#Name			
#San Zhang	85.0	100.0	90
#Si Li	90.0	NaN	90
#All	87.5	100.0	90

2.3 melt

長寬表只是資料呈現方式的差異,但其包含的資訊量是等價的,前面提到了利用 pivot 把長錶轉為寬表,那麼就可以通過相應的逆操作把寬錶轉為長表, melt 函式就起到了這樣的作用。
在下面的例子中, Subject 以列索引的形式儲存,現在想要將其壓縮到一個列中。

df3 = pd.DataFrame({'Class':[1,2],'Name':['San Zhang','Si Li'],'Chinese':[80,90],'Math':[85,95]})
df3
df3_melted = df3.melt(id_vars=['Class','Name'],value_vars=['Chinese','Math'],var_name='Subject',value_name='Grade')

  • id_vars是每一個列變數類別會對應一個id_vars的塊,個人理解是指原來表中不變的列
  • value_vars是需要從原來列變為行中的變數,這裡是把列名變為表中元素值了。
  • var_name是原來列變數名的含義,也就是現表中改變的列名
  • value_name是原來列變數值的含義,
  • 新表與原表相比:行數變為:原行數*改變的列數,列數為原表中不變的列加2列(即var_name和value_name兩列)
    具體過程如下圖所示
    在這裡插入圖片描述
    前面提到了 melt 和 pivot 是一組互逆過程,那麼就一定可以通過 pivot 操作把 df_melted 轉回 df 的形式:
df3_unmelted = df3_melted.pivot(index=['Class','Name'],columns='Subject',values='Grade')
df3_unmelted#先 恢復成了多級索引
df3_unmelted=df3_unmelted.reset_index().rename_axis(columns={'Subject':''})
df3_unmelted#把多出的Subject變為空

2.4 wide_to_long

melt 方法中,在列索引中被壓縮的一組值對應的列元素只能代表同一層次的含義,即 values_name 。現在如果列中包含了交叉類別,比如期中期末的類別和語文數學的類別,那麼想要把 values_name 對應的 Grade 擴充為兩列分別對應語文分數和數學分數,只把期中期末的資訊壓縮,這種需求下就要使用 wide_to_long 函式來完成。

df4 = pd.DataFrame({'Class':[1,2],'Name':['San Zhang', 'Si Li'],'Chinese_mid':[80, 75],
                   'Math_mid':[90, 85],'Chinese_final':[85, 80],'Math_final':[90, 80],})
df4
pd.wide_to_long(df4,
               stubnames=['Chinese','Math'],
               i=['Class','Name'],
               j='Examination',
               sep = '_',
                suffix='.+')
  • stubnames為要改動列的變數值的含義,轉化後的表以其為列,等價於melt中的value_name,
  • i為保持不變的id變數,等價於melt的id_vars,
  • j 為壓縮到行的變數名含義,等價於melt中的var_name
  • sep為分隔符
  • suffix為正則字尾???
    具體過程如下圖所示
    在這裡插入圖片描述
res = pivot_multi.copy()
res.columns= res.columns.map(lambda x: '_'.join(x))
res = res.reset_index()
res
res = pd.wide_to_long(res,
                      stubnames=['Grade','rank'],
                      i = ['Class','Name'],
                      j = 'Subject_Examination',
                      sep = '_',
                      suffix = '.+')
res#將原來的列變為行
#下面是變回去
res= res.reset_index()
res[['Subject','Examination']]=res['Subject_Examination'].str.split("_",expand=True)
res = res[['Class', 'Name', 'Examination',
            'Subject', 'Grade', 'rank']].sort_values('Subject')
res= res.reset_index(drop=True)
res

三、索引的變形

3.1 stack與unstack

行列索引之間 的交換,由於這種交換帶來了 DataFrame 維度上的變化,因此屬於變形操作。在第一節中提到的4種變形函式與其不同之處在於,它們都屬於某一列或幾列 元素 和 列索引 之間的轉換,而不是索引之間的轉換。
unstack 函式的作用是把行索引轉為列索引

df = pd.DataFrame(np.ones((4,2)),index=pd.Index([('A','cat','big'),('A','dog','small'),
                                                 ('B','cat','big'),('B','dog','small')]),
                 columns=['col_1','col_2'])
df
df.unstack()#把最裡面的那層行索引轉換為列索引

unstack 的主要引數是移動的層號,預設轉化最內層,移動到列索引的最內層,同時支援同時轉化多個層:

df.unstack(1)#轉中間那層
df.unstack([0,2])#轉最外層行為第二層列,第內層行為最內層列,
df.unstack([2,1])#先轉前面位置的層也就是第三層,然後第2層到列的最內層

類似於pivot中的唯一性要求,在unstack中必須保證被轉為列索引的行索引層和被保留的行索引層構成的組合是唯一的,

my_index = df.index.to_list()
my_index[1]=my_index[0]
df.index = pd.Index(my_index)
df
try:
    df.unstack()
except Exception as e:
        Err_Msg = e
Err_Msg #ValueError('Index contains duplicate entries, cannot reshape')

與 unstack 相反, stack 的作用就是把列索引的層壓入行索引,其用法完全類似。

df1 = pd.DataFrame(np.ones((4,2)),index=pd.Index([('A','cat','big'),('A','dog','small'),
                                                 ('B','cat','big'),('B','dog','small')]),
                 columns=['index_1','index_2']).T
df1
df1.stack()#把列最內側轉為行最內層
df1.stack([2,0])

四、其他變形函式

crosstab

crosstab 並不是一個值得推薦使用的函式,因為它能實現的所有功能 pivot_table 都能完成,並且速度更快。在預設狀態下, crosstab 可以統計元素組合出現的頻數,即 count 操作。

df = pd.read_csv('../joyful-pandas-master/data/learn_pandas.csv')
pd.crosstab(index=df['School'],columns=df['Transfer'])
#等價於如下 crosstab 的如下寫法,這裡的 aggfunc 即聚合引數:
#pd.crosstab(index = df.School,columns=df.Transfer,aggfunc='count')#報錯,少了values
pd.crosstab(index = df.School,columns=df.Transfer,values = [0]*df.shape[0],aggfunc='count')#為什麼values要設定為0

同樣,可以利用 pivot_table 進行等價操作,由於這裡統計的是組合的頻數,因此 values 引數無論傳入哪一個列都不會影響最後的結果:

df.pivot_table(index = 'School',columns= 'Transfer',values = 'Grade',aggfunc='count')

crosstab 的對應位置傳入的是具體的序列,而pivot_table傳入的是被呼叫表對應的名字,若傳入序列對應的值則會報錯。除了預設狀態下的 count 統計,所有的聚合字串和返回標量的自定義函式都是可用的,

pd.crosstab(index = df.School, columns = df.Transfer,
             values = df.Height, aggfunc = 'mean')

explode

explode 引數能夠對某一列的元素進行縱向的展開,被展開的單元格必須儲存 list, tuple, Series, np.ndarray 中的一種型別。

df_ex = pd.DataFrame({'A':[[1,2],'my_str',{1,2},pd.Series([3,4])],'B':1})
df_ex
df_ex.explode('A')#[1,2]變為單個1,2,{1,2}未變,pd.Series([3,4])變為單獨的3,4

get_dummies

get_dummies 是用於特徵構建的重要函式之一,其作用是把類別特徵轉為指示變數。
例如,對性別一列轉為指示變數,屬於性別的對應列標記為1,否則為0:

pd.get_dummies(df.Gender).head()

相關文章