精通Pandas变形操作:pivot_table()、get_dummies()、stack()函数

在这里插入图片描述

>>> import numpy as np
>>> import pandas as pd
>>> df = pd.read_csv('data/table.csv')
>>> df.head()

在这里插入图片描述

一、透视表

1. pivot

一般状态下,数据在DataFrame会以压缩(stacked)状态存放,例如上面的Gender,两个类别被叠在一列中,pivot函数可将某一列作为新的cols:

>>> df.pivot(index='ID',columns='Gender',values='Height').head()

Gender      F      M
ID                  
1101      NaN  173.0
1102    192.0    NaN
1103      NaN  186.0
1104    167.0    NaN
1105    159.0    NaN

然而pivot函数具有很强的局限性,除了功能上较少之外,还不允许values中出现重复的行列索引对(pair),例如下面的语句就会报错:

#df.pivot(index='School',columns='Gender',values='Height').head()

因此,更多的时候会选择使用强大的pivot_table函数。

2. pivot_table

首先,再现上面的操作:

>>> pd.pivot_table(df,index='ID',columns='Gender',values='Height').head()
# 由于功能更多,速度上自然是比不上原来的pivot函数
>>> pd.pivot_table(df,index="School",columns="Gender",values="Height").head()
# 默认为mean组内聚合
Gender           F           M
School                        
S_1     173.125000  178.714286
S_2     173.727273  172.000000
>>> %timeit df.pivot(index='ID',columns='Gender',values='Height')
>>> %timeit pd.pivot_table(df,index='ID',columns='Gender',values='Height')
2.31 ms ± 564 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
13.8 ms ± 2.59 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

**#### Pandas中提供了各种选项,下面介绍常用参数:

① aggfunc:对组内进行聚合统计,可传入各类函数,默认为’mean’**

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

              mean               sum      
Gender           F           M     F     M
School                                    
S_1     173.125000  178.714286  1385  1251
S_2     173.727273  172.000000  1911  1548

② margins:汇总边际状态

>>> pd.pivot_table(df,index='School',columns='Gender',values='Height',aggfunc=['mean','sum'],margins=True).head()
#margins_name可以设置名字,默认为'All'
              mean                           sum            
Gender           F           M         All     F     M   All
School                                                      
S_1     173.125000  178.714286  175.733333  1385  1251  2636
S_2     173.727273  172.000000  172.950000  1911  1548  3459
All     173.473684  174.937500  174.142857  3296  2799  6095

③ 行、列、值都可以为多级

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

在这里插入图片描述

3. crosstab(交叉表)

交叉表是一种特殊的透视表,典型的用途如分组统计,如现在想要统计关于街道和性别分组的频数:

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

Gender    F  M
Address       
street_1  1  2
street_2  4  2
street_4  3  5
street_5  3  3
street_6  5  1
street_7  3  3

交叉表的功能也很强大(但目前还不支持多级分组),下面说明一些重要参数:

① 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')

Gender     F   M
Address         
street_1  16   4
street_2   9  13
street_4   2   1
street_5  15   7
street_6  10   2
street_7   2   6

② 除了边际参数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()
     ID Gender  Math
0  1101      M  34.0
1  1102      F  32.5
2  1103      M  87.2
3  1104      F  80.4
4  1105      F  84.8
# melt函数中的id_vars表示需要保留的列,value_vars表示需要stack的一组列
>>> pivoted = df.pivot(index='ID',columns='Gender',values='Math')
>>> result = pivoted.reset_index().melt(id_vars=['ID'],value_vars=['F','M'],value_name='Math')\
                     .dropna().set_index('ID').sort_index()
#检验是否与展开前的df相同,可以分别将这些链式方法的中间步骤展开,看看是什么结果
>>> print(result.equals(df_m.set_index('ID')))
True
>>> result.head()
   Gender  Math
ID               
1101      M  34.0
1102      F  32.5
1103      M  87.2
1104      F  80.4
1105      F  84.8

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)

           Height        Weight      
Gender          F      M      F     M
Class ID                             
C_1   1101    NaN  173.0    NaN  63.0
      1102  192.0    NaN   73.0   NaN
C_2   1201    NaN  188.0    NaN  68.0
      1202  176.0    NaN   94.0   NaN
C_3   1301    NaN  161.0    NaN  68.0
      1302  175.0    NaN   57.0   NaN
C_4   2401  192.0    NaN   62.0   NaN
      2402    NaN  166.0    NaN  82.0
>>> df_stacked = df_s.stack() # df_s.stack(1)
>>> df_stacked.groupby('Class').head(2)

                   Height  Weight
Class ID   Gender                
C_1   1101 M        173.0    63.0
      1102 F        192.0    73.0
C_2   1201 M        188.0    68.0
      1202 F        176.0    94.0
C_3   1301 M        161.0    68.0
      1302 F        175.0    57.0
C_4   2401 F        192.0    62.0
      2402 M        166.0    82.0

stack函数可以看做将横向的索引放到纵向,因此功能类似与melt,参数level可指定变化的列索引是哪一层(或哪几层,需要列表。

>>> df_stacked = df_s.stack(0)
>>> df_stacked.groupby('Class').head(2)

Gender                 F      M
Class ID                       
C_1   1101 Height    NaN  173.0
           Weight    NaN   63.0
C_2   1201 Height    NaN  188.0
           Weight    NaN   68.0
C_3   1301 Height    NaN  161.0
           Weight    NaN   68.0
C_4   2401 Height  192.0    NaN
           Weight   62.0    NaN

(2) unstack:stack的逆函数,功能上类似于pivot_table

>>>  result = df_stacked.unstack().swaplevel(1,0,axis=1).sort_index(axis=1)
>>>  result.equals(df_s)
#同样在unstack中可以指定level参数
True

三、哑变量与因子化

1. Dummy Variable(哑变量)

这里主要介绍get_dummies函数,其功能主要是进行one-hot编码:

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

 Class Gender  Weight
0   C_1      M      63
1   C_1      F      73
2   C_1      M      82
3   C_1      F      81
4   C_1      F      64

现在希望将上面的表格前两列转化为哑变量,并加入第三列Weight数值:

>>> pd.get_dummies(df_d[['Class','Gender']]).join(df_d['Weight']).head()
#可选prefix参数添加前缀,prefix_sep添加分隔符

   Class_C_1  Class_C_2  Class_C_3  Class_C_4  Gender_F  Gender_M  Weight
0          1          0          0          0         0         1      63
1          1          0          0          0         1         0      73
2          1          0          0          0         0         1      82
3          1          0          0          0         1         0      81
4          1          0          0          0         1         0      64

2. factorize方法

该方法主要用于自然数编码,并且缺失值会被记做-1,其中sort参数表示是否排序后赋值。

>>> codes, uniques = pd.factorize(['b', None, 'a', 'c', 'b'], sort=True)
>>> display(codes)
array([ 1, -1,  0,  2,  1], dtype=int64)
>>> display(uniques)
array(['a', 'b', 'c'], dtype=object)

四、问题与练习

1. 问题

【问题一】 上面提到了许多变形函数,如melt/crosstab/pivot/pivot_table/stack/unstack函数,请总结它们各自的使用特点。
【问题二】 变形函数和多级索引是什么关系?哪些变形函数会使得索引维数变化?具体如何变化?
【问题三】 请举出一个除了上文提过的关于哑变量方法的例子。
【问题四】 使用完stack后立即使用unstack一定能保证变化结果与原始表完全一致吗?
【问题五】 透视表中涉及了三个函数,请分别使用它们完成相同的目标(任务自定)并比较哪个速度最快。
【问题六】 既然melt起到了stack的功能,为什么再设计stack函数?

2. 练习

【练习一】 继续使用上一章的药物数据集:

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

在这里插入图片描述
(a) 现在请你将数据表转化成如下形态,每行需要显示每种药物在每个地区的10年至17年的变化情况,且前三列需要排序:
在这里插入图片描述
(b) 现在请将(a)中的结果恢复到原数据表,并通过equal函数检验初始表与新的结果是否一致(返回True)

【练习二】 现有一份关于某地区地震情况的数据集,请解决如下问题:

>>> pd.read_csv('data/Earthquake.csv').head()

在这里插入图片描述
(a) 现在请你将数据表转化成如下形态,将方向列展开,并将距离、深度和烈度三个属性压缩:
在这里插入图片描述

(b) 现在请将(a)中的结果恢复到原数据表,并通过equal函数检验初始表与新的结果是否一致(返回True)

参考:https://github.com/datawhalechina/joyful-pandas

关于Datawhale

Datawhale是一个专注于数据科学与AI领域的开源组织,汇集了众多领域院校和知名企业的优秀学习者,聚合了一群有开源精神和探索精神的团队成员。Datawhale以“for the learner,和学习者一起成长”为愿景,鼓励真实地展现自我、开放包容、互信互助、敢于试错和勇于担当。同时Datawhale 用开源的理念去探索开源内容、开源学习和开源方案,赋能人才培养,助力人才成长,建立起人与人,人与知识,人与企业和人与未来的联结。

原创文章 24 获赞 31 访问量 2969

猜你喜欢

转载自blog.csdn.net/OuDiShenmiss/article/details/105815834